<commit_before>define(['lodash', 'angular', 'filters/lstring', 'directives/print-smart/print-smart-checkout', './meeting-document', 'authentication',
        'css!./meeting-documents.css', 'angular-cache', 'services/conference-service'
], function(_, ng) {

    var CRP = /.*\/CRP(\d+)(?:$|\/[A-Z]+\d+$)/i
    var REV = /.*\/REV(\d+)$/i
    var ADD = /.*\/ADD(\d+)$/i
    var STATISTICS = {};

    return  ["$scope", "$route", "$http", '$q', '$location', 'authentication', 'conferenceService', '$filter', 'CacheFactory', function(
              $scope,   $route,   $http,   $q,   $location,   authentication,   conferenceService,   $filter,   CacheFactory) {

        var $ctrl = this;
        var lstring  = $filter('lstring');
        var conferenceCode = $route.current.params.code;
        var httpCache = initCache();

        $ctrl.documents = null;
        $ctrl.meetings  = null;
        $ctrl.toHtmlID  = toHtmlID;
        $ctrl.edit = edit;
        $ctrl.add  = add;

        load();
        initAffix();

        //====================================
        // Init editor security
        //===================================
        $q.when(authentication.getUser()).then(function(user){
            $ctrl.isEditor = authentication.isInRole(user, ["EditorialService"]);
            $ctrl.isStaff  = authentication.isInRole(user, ["ScbdStaff"]) || $ctrl.isEditor;
        });

        //====================================
        //
        //====================================
        function load() {

            var meetings;

            return $q.when(httpCache.reValidate()).then(function(){

                return conferenceService.getActiveConference(conferenceCode);

            }).then(function(conference){

                var query, fields;

                var ids = _.map(conference.MajorEventIDs, function(id){
                    return { $oid: id };
                });

                // load meetings (no wait)

                query  = { _id: { $in : ids } };
                fields = { EVT_CD:1, agenda:1, printSmart:1 };

                meetings = $http.get("/api/v2016/meetings", { params: { q: query, f: fields }, cache: httpCache }).then(resData);

                // load documents

                var query = {
                    meeting: { $in : ids },
                    status:  'public',
                    type:    'in-session',
                    nature: { $in : ['non-paper', 'crp', 'limited'] }
                };

                return $http.get('/api/v2016/documents', { params: { q: query }, cache: httpCache }).then(resData);

            }).then(function(documents){

                $ctrl.tabs = _(documents).sortBy(buildSortKey).reduce(function(tabs, d){

                    d.metadata = _.defaults(d.metadata||{}, { //Normalize
                        printable: true,
                        visible : true
                    });

                    var tab = tabs[d.group||''] = tabs[d.group||''] || {
                        code: d.group||'plenary',
                        documents : []
                    };

                    tab.documents.push(d);

                    return tabs;

                }, {});

                $ctrl.currentTab = _($ctrl.tabs).values().first();

                if($ctrl.currentTab)
                    $ctrl.currentTab.loaded = true;

                $ctrl.documents = documents;

                //resolve meetings
                return meetings;

            }).then(function(meetings){

                var ids = _.map(meetings, '_id');

                $ctrl.meetings   = _.zipObject(ids, meetings);
                $ctrl.printSmart = _.some(meetings, {printSmart:true});

            }).catch(console.error)
        }

        //====================================
        //
        //====================================
        function edit(document) {

            var meeting = $ctrl.meetings[document.meeting];

            var parts = [
                $route.current.params.code,
                meeting.EVT_CD,
                'documents',
                document._id
            ];

            $location.path('/'+_.map(parts, encodeURIComponent).join('/'));
        }

        //====================================
        //
        //====================================
        function add(meeting) {

            var parts = [
                $route.current.params.code,
                meeting.EVT_CD,
                'documents',
                'new'
            ];

            $location.path('/'+_.map(parts, encodeURIComponent).join('/'));
        }

        //====================================
        //
        //====================================
        function buildSortKey(d) {

            var sortKey = [];

            if((d.metadata||{}).superseded) sortKey.push("B");
            else                            sortKey.push("A");

                 if(d.nature=="limited")    sortKey.push("A");
            else if(d.nature=="crp")        sortKey.push("B");
            else if(d.nature=="non-paper")  sortKey.push("C");
            else                            sortKey.push("Z");

            var normalizedSymbol = (d.symbol || 'ZZZ').replace(/\d+/g, pad);

            if(d.nature=="limited") {
                sortKey.push(pad("0")) //TODO: Meeting index
                sortKey.push(normalizedSymbol)
            }
            else {

                var crp = parseInt(getNumber(normalizedSymbol, CRP) || 9999);
                var rev = parseInt(getNumber(normalizedSymbol, REV) ||    0);
                var add = parseInt(getNumber(normalizedSymbol, ADD) ||    0);

                sortKey.push(pad(crp));
                sortKey.push(pad(9999-rev));
                sortKey.push(pad(add));
            }

            sortKey.push((normalizedSymbol || lstring(d.title)).toUpperCase());

            return sortKey.join('-')
        }

        function getNumber(symbol, re) {

            if(re.test(symbol))
                return symbol.replace(re, "$1");

            return "";
        }

        //====================================
        //
        //====================================
        function pad(t) {

            t = `${t||''}`;

            while(t.length<4)
                t = '0'+t;

            return t;
        }

        //====================================
        //
        //====================================
        function toHtmlID(text) {
            return (text||'').replace(/[^A-Z0-9]/gi, '_');
        }

        //====================================
        //
        //====================================
        function resData(res) { return res.data; }

        //==============================
        //
        //==============================
        function initAffix() {

            var affixReady = $scope.$watch(function() {

                var psc = ng.element('#print-smart-checkout');

                if(psc.size()) {
                    psc.affix({ offset: { top:psc.offset().top - 10 } });
                    affixReady();
                }
            });
        }

        //==============================
        //
        //==============================
        function initCache() {

            var cacheId = 'meeting_documents_insession_'+conferenceCode;
            var cache   = CacheFactory.get(cacheId)

            if(!cache) {

                cache = CacheFactory.createCache(cacheId, {
                    deleteOnExpire: 'passive',
                    maxAge        : 5 * 60 * 1000,
                    storageMode   : 'localStorage',
                    storagePrefix : 'httpCache_'
                });

                cache.reValidate = function() {

                    var thisCache = this;

                    return conferenceService.getActiveConference(conferenceCode).then(function(conference) {

                        var reqs = _.map(conference.MajorEventIDs, function(id){
                            return $http.get('/api/v2016/meetings/'+encodeURIComponent(id)+'/documents/statistics').then(resData);
                        });

                        return $q.all(reqs);

                    }).then(function(stats){

                        var oldStats = STATISTICS[conferenceCode] || thisCache.get('statistics');
                        var newStats = stats;

                        if(!_.isEqual(oldStats, newStats)) {
                            thisCache.removeAll();
                            thisCache.put('statistics', newStats);
                            STATISTICS[conferenceCode]= newStats;
                        }

                        return thisCache;

                    }).catch(function(e){

                        thisCache.removeAll();
                        return thisCache;
                    });
                };
            }

            return cache;
        }
    }]
});
<commit_msg>Fix for IE that do not support string template `${}`<commit_after>define(['lodash', 'angular', 'filters/lstring', 'directives/print-smart/print-smart-checkout', './meeting-document', 'authentication',
        'css!./meeting-documents.css', 'angular-cache', 'services/conference-service'
], function(_, ng) {

    var CRP = /.*\/CRP(\d+)(?:$|\/[A-Z]+\d+$)/i
    var REV = /.*\/REV(\d+)$/i
    var ADD = /.*\/ADD(\d+)$/i
    var STATISTICS = {};

    return  ["$scope", "$route", "$http", '$q', '$location', 'authentication', 'conferenceService', '$filter', 'CacheFactory', function(
              $scope,   $route,   $http,   $q,   $location,   authentication,   conferenceService,   $filter,   CacheFactory) {

        var $ctrl = this;
        var lstring  = $filter('lstring');
        var conferenceCode = $route.current.params.code;
        var httpCache = initCache();

        $ctrl.documents = null;
        $ctrl.meetings  = null;
        $ctrl.toHtmlID  = toHtmlID;
        $ctrl.edit = edit;
        $ctrl.add  = add;

        load();
        initAffix();

        //====================================
        // Init editor security
        //===================================
        $q.when(authentication.getUser()).then(function(user){
            $ctrl.isEditor = authentication.isInRole(user, ["EditorialService"]);
            $ctrl.isStaff  = authentication.isInRole(user, ["ScbdStaff"]) || $ctrl.isEditor;
        });

        //====================================
        //
        //====================================
        function load() {

            var meetings;

            return $q.when(httpCache.reValidate()).then(function(){

                return conferenceService.getActiveConference(conferenceCode);

            }).then(function(conference){

                var query, fields;

                var ids = _.map(conference.MajorEventIDs, function(id){
                    return { $oid: id };
                });

                // load meetings (no wait)

                query  = { _id: { $in : ids } };
                fields = { EVT_CD:1, agenda:1, printSmart:1 };

                meetings = $http.get("/api/v2016/meetings", { params: { q: query, f: fields }, cache: httpCache }).then(resData);

                // load documents

                var query = {
                    meeting: { $in : ids },
                    status:  'public',
                    type:    'in-session',
                    nature: { $in : ['non-paper', 'crp', 'limited'] }
                };

                return $http.get('/api/v2016/documents', { params: { q: query }, cache: httpCache }).then(resData);

            }).then(function(documents){

                $ctrl.tabs = _(documents).sortBy(buildSortKey).reduce(function(tabs, d){

                    d.metadata = _.defaults(d.metadata||{}, { //Normalize
                        printable: true,
                        visible : true
                    });

                    var tab = tabs[d.group||''] = tabs[d.group||''] || {
                        code: d.group||'plenary',
                        documents : []
                    };

                    tab.documents.push(d);

                    return tabs;

                }, {});

                $ctrl.currentTab = _($ctrl.tabs).values().first();

                if($ctrl.currentTab)
                    $ctrl.currentTab.loaded = true;

                $ctrl.documents = documents;

                //resolve meetings
                return meetings;

            }).then(function(meetings){

                var ids = _.map(meetings, '_id');

                $ctrl.meetings   = _.zipObject(ids, meetings);
                $ctrl.printSmart = _.some(meetings, {printSmart:true});

            }).catch(console.error)
        }

        //====================================
        //
        //====================================
        function edit(document) {

            var meeting = $ctrl.meetings[document.meeting];

            var parts = [
                $route.current.params.code,
                meeting.EVT_CD,
                'documents',
                document._id
            ];

            $location.path('/'+_.map(parts, encodeURIComponent).join('/'));
        }

        //====================================
        //
        //====================================
        function add(meeting) {

            var parts = [
                $route.current.params.code,
                meeting.EVT_CD,
                'documents',
                'new'
            ];

            $location.path('/'+_.map(parts, encodeURIComponent).join('/'));
        }

        //====================================
        //
        //====================================
        function buildSortKey(d) {

            var sortKey = [];

            if((d.metadata||{}).superseded) sortKey.push("B");
            else                            sortKey.push("A");

                 if(d.nature=="limited")    sortKey.push("A");
            else if(d.nature=="crp")        sortKey.push("B");
            else if(d.nature=="non-paper")  sortKey.push("C");
            else                            sortKey.push("Z");

            var normalizedSymbol = (d.symbol || 'ZZZ').replace(/\d+/g, pad);

            if(d.nature=="limited") {
                sortKey.push(pad("0")) //TODO: Meeting index
                sortKey.push(normalizedSymbol)
            }
            else {

                var crp = parseInt(getNumber(normalizedSymbol, CRP) || 9999);
                var rev = parseInt(getNumber(normalizedSymbol, REV) ||    0);
                var add = parseInt(getNumber(normalizedSymbol, ADD) ||    0);

                sortKey.push(pad(crp));
                sortKey.push(pad(9999-rev));
                sortKey.push(pad(add));
            }

            sortKey.push((normalizedSymbol || lstring(d.title)).toUpperCase());

            return sortKey.join('-')
        }

        function getNumber(symbol, re) {

            if(re.test(symbol))
                return symbol.replace(re, "$1");

            return "";
        }

        //====================================
        //
        //====================================
        function pad(t) {

            t = (t||'').toString();

            while(t.length<4)
                t = '0'+t;

            return t;
        }

        //====================================
        //
        //====================================
        function toHtmlID(text) {
            return (text||'').replace(/[^A-Z0-9]/gi, '_');
        }

        //====================================
        //
        //====================================
        function resData(res) { return res.data; }

        //==============================
        //
        //==============================
        function initAffix() {

            var affixReady = $scope.$watch(function() {

                var psc = ng.element('#print-smart-checkout');

                if(psc.size()) {
                    psc.affix({ offset: { top:psc.offset().top - 10 } });
                    affixReady();
                }
            });
        }

        //==============================
        //
        //==============================
        function initCache() {

            var cacheId = 'meeting_documents_insession_'+conferenceCode;
            var cache   = CacheFactory.get(cacheId)

            if(!cache) {

                cache = CacheFactory.createCache(cacheId, {
                    deleteOnExpire: 'passive',
                    maxAge        : 5 * 60 * 1000,
                    storageMode   : 'localStorage',
                    storagePrefix : 'httpCache_'
                });

                cache.reValidate = function() {

                    var thisCache = this;

                    return conferenceService.getActiveConference(conferenceCode).then(function(conference) {

                        var reqs = _.map(conference.MajorEventIDs, function(id){
                            return $http.get('/api/v2016/meetings/'+encodeURIComponent(id)+'/documents/statistics').then(resData);
                        });

                        return $q.all(reqs);

                    }).then(function(stats){

                        var oldStats = STATISTICS[conferenceCode] || thisCache.get('statistics');
                        var newStats = stats;

                        if(!_.isEqual(oldStats, newStats)) {
                            thisCache.removeAll();
                            thisCache.put('statistics', newStats);
                            STATISTICS[conferenceCode]= newStats;
                        }

                        return thisCache;

                    }).catch(function(e){

                        thisCache.removeAll();
                        return thisCache;
                    });
                };
            }

            return cache;
        }
    }]
});
<|endoftext|>


<commit_before>// @flow weak

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { timer } from 'd3-timer';
import { connect } from 'react-redux';
import Slider from 'material-ui/Slider';
import Paper from 'material-ui/Paper';
import Surface from 'resonance/Surface';
import NodeGroup from 'resonance/NodeGroup';
import MarkdownElement from 'docs/src/components/MarkdownElement';
import Arc from './Arc';
import { getNodes, getScales, updateScales } from '../module';
import { VIEW, TRBL, DIMS } from '../module/constants';
import { x, y, getScaleInterpolators } from '../module/scales';
import description from '../description.md';

const arcKeyAccessor = (d) => d.filePath;

export class Example extends Component {
  constructor(props) {
    super(props);

    (this:any).setDuration = this.setDuration.bind(this);
    (this:any).setActiveNode = this.setActiveNode.bind(this);
    (this:any).setActivePath = this.setActivePath.bind(this);
  }

  state = {
    duration: 750,
    activePath: 'Click on an arc...',
  }

  componentWillMount() {
    const { xScale, yScale } = this.props;

    x.range(xScale.range()).domain(xScale.domain());
    y.range(yScale.range()).domain(yScale.domain());
  }

  componentWillReceiveProps(next) {
    const { props } = this;
    const { duration } = this.state;

    if (
      next.xScale !== props.xScale ||
      next.yScale !== props.yScale
    ) {
      if (this.transition) {
        this.transition.stop();
      }

      const { xd, yd, yr } = getScaleInterpolators(next);

      this.transition = timer((elapsed) => {
        const t = elapsed < duration ? (elapsed / duration) : 1;

        x.domain(xd(t));
        y.domain(yd(t)).range(yr(t));

        if (t === 1) {
          this.transition.stop();
        }
      });
    }
  }

  componentWillUnmount() {
    if (this.transition) {
      this.transition.stop();
    }
  }

  setActiveNode(node) {
    const { dispatch } = this.props;
    dispatch(updateScales(node));
  }

  setActivePath(path) {
    this.setState({
      activePath: path,
    });
  }

  setDuration(e, value) {
    this.setState({
      duration: Math.floor(value * 10000),
    });
  }

  transition = null;

  render() {
    const { nodes, path } = this.props;
    const { duration, activePath } = this.state;

    return (
      <Paper style={{ padding: 20 }}>

                <MarkdownElement text={description} />

Transition Duration: {(duration / 1000).toFixed(1)} Seconds

                <Slider
                  defaultValue={0.1}
                  onChange={this.setDuration}
                />

                <Surface view={VIEW} trbl={TRBL}>
                  <text y={15} fontSize="10px">{activePath}</text>
                  <g transform={`translate(${DIMS[0] / 2},${DIMS[1] / 2})`}>
                    <NodeGroup
                      data={nodes}
                      path={path}
                      duration={duration}
                      nodeComponent={Arc}
                      keyAccessor={arcKeyAccessor}
                      setActiveNode={this.setActiveNode}
                      activePath={activePath}
                      setActivePath={this.setActivePath}
                    />
                  </g>
                </Surface>

      </Paper>
    );
  }
}

Example.propTypes = {
  path: PropTypes.func.isRequired,
  nodes: PropTypes.array.isRequired,
  xScale: PropTypes.func.isRequired,
  yScale: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
};

const makeMapStateToProps = () => {
  const mapStateToProps = (state) => {
    const nodes = getNodes(state);
    const { path, xScale, yScale } = getScales(state);

    return {
      path,
      nodes,
      xScale,
      yScale,
    };
  };
  return mapStateToProps;
};

export default connect(makeMapStateToProps())(Example);
<commit_msg>add easing function<commit_after>// @flow weak

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { timer } from 'd3-timer';
import { easeCubic } from 'd3-ease';
import { connect } from 'react-redux';
import Slider from 'material-ui/Slider';
import Paper from 'material-ui/Paper';
import Surface from 'resonance/Surface';
import NodeGroup from 'resonance/NodeGroup';
import MarkdownElement from 'docs/src/components/MarkdownElement';
import Arc from './Arc';
import { getNodes, getScales, updateScales } from '../module';
import { VIEW, TRBL, DIMS } from '../module/constants';
import { x, y, getScaleInterpolators } from '../module/scales';
import description from '../description.md';

const arcKeyAccessor = (d) => d.filePath;

export class Example extends Component {
  constructor(props) {
    super(props);

    (this:any).setDuration = this.setDuration.bind(this);
    (this:any).setActiveNode = this.setActiveNode.bind(this);
    (this:any).setActivePath = this.setActivePath.bind(this);
  }

  state = {
    duration: 750,
    activePath: 'Click on an arc...',
  }

  componentWillMount() {
    const { xScale, yScale } = this.props;

    x.range(xScale.range()).domain(xScale.domain());
    y.range(yScale.range()).domain(yScale.domain());
  }

  componentWillReceiveProps(next) {
    const { props } = this;
    const { duration } = this.state;

    if (
      next.xScale !== props.xScale ||
      next.yScale !== props.yScale
    ) {
      if (this.transition) {
        this.transition.stop();
      }

      const { xd, yd, yr } = getScaleInterpolators(next);

      this.transition = timer((elapsed) => {
        const t = easeCubic(elapsed < duration ? (elapsed / duration) : 1);

        x.domain(xd(t));
        y.domain(yd(t)).range(yr(t));

        if (t >= 1) {
          this.transition.stop();
        }
      });
    }
  }

  componentWillUnmount() {
    if (this.transition) {
      this.transition.stop();
    }
  }

  setActiveNode(node) {
    const { dispatch } = this.props;
    dispatch(updateScales(node));
  }

  setActivePath(path) {
    this.setState({
      activePath: path,
    });
  }

  setDuration(e, value) {
    this.setState({
      duration: Math.floor(value * 10000),
    });
  }

  transition = null;

  render() {
    const { nodes, path } = this.props;
    const { duration, activePath } = this.state;

    return (
      <Paper style={{ padding: 20 }}>

                <MarkdownElement text={description} />

Transition Duration: {(duration / 1000).toFixed(1)} Seconds

                <Slider
                  defaultValue={0.1}
                  onChange={this.setDuration}
                />

                <Surface view={VIEW} trbl={TRBL}>
                  <text y={15} fontSize="10px">{activePath}</text>
                  <g transform={`translate(${DIMS[0] / 2},${DIMS[1] / 2})`}>
                    <NodeGroup
                      data={nodes}
                      path={path}
                      duration={duration}
                      nodeComponent={Arc}
                      keyAccessor={arcKeyAccessor}
                      setActiveNode={this.setActiveNode}
                      activePath={activePath}
                      setActivePath={this.setActivePath}
                    />
                  </g>
                </Surface>

      </Paper>
    );
  }
}

Example.propTypes = {
  path: PropTypes.func.isRequired,
  nodes: PropTypes.array.isRequired,
  xScale: PropTypes.func.isRequired,
  yScale: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
};

const makeMapStateToProps = () => {
  const mapStateToProps = (state) => {
    const nodes = getNodes(state);
    const { path, xScale, yScale } = getScales(state);

    return {
      path,
      nodes,
      xScale,
      yScale,
    };
  };
  return mapStateToProps;
};

export default connect(makeMapStateToProps())(Example);
<|endoftext|>


<commit_before>/**
 * Name: MessageBar
 * Description: A Message Bar Component displayed at the top of screen
 * https://github.com/talor-a/react-native-message-bar
 */
'use strict'

import React, { Component } from 'react'
import {
  Text,
  View,
  TouchableOpacity,
  Animated,
  Dimensions,
  Image
} from 'react-native'

let windowWidth = Dimensions.get('window').width
let windowHeight = Dimensions.get('window').height

class MessageBar extends Component {
  constructor (props) {
    super(props)

    this.animatedValue = new Animated.Value(0)
    this.notifyAlertHiddenCallback = null
    this.alertShown = false
    this.timeoutHide = null

    this.state = this.getStateByProps(props)
  }

  componentDidMount () {
    // Configure the offsets prior to recieving updated props or recieving the first alert
    // This ensures the offsets are set properly at the outset based on the initial position.
    // This prevents the bar from appearing  and covering half of the screen when the
    // device is started in landscape and then rotated to portrait.
    // This does not happen after the first alert appears, as setNewState() is called on each
    // alert and calls _changeOffsetByPosition()
    this._changeOffsetByPosition(this.state.position)
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps && Object.keys(nextProps).length > 0) {
      this.setNewState(nextProps)
    }
  }

  setNewState (state) {
    // Set the new state, this is triggered when the props of this MessageBar changed
    this.setState(this.getStateByProps(state))

    // Apply the colors of the alert depending on its alertType
    this._applyAlertStylesheet(state.alertType)

    // Override the opposition style position regarding the state position in order to have the alert sticks that position
    this._changeOffsetByPosition(state.position)
  }

  getStateByProps (props) {
    return {
      // Default values, will be overridden
      backgroundColor: '#007bff', // default value : blue
      strokeColor: '#006acd', // default value : blue
      animationTypeTransform: 'SlideFromTop', // default value

      /* Cusomisation of the alert: Title, Message, Icon URL, Alert alertType (error, success, warning, info), Duration for Alert keep shown */
      title: props.title,
      message: props.message,
      avatar: props.avatar,
      alertType: props.alertType || 'info',
      duration: props.duration || 3000,

      /* Hide setters */
      shouldHideAfterDelay: props.shouldHideAfterDelay == undefined
        ? true
        : props.shouldHideAfterDelay,
      shouldHideOnTap: props.shouldHideOnTap == undefined
        ? true
        : props.shouldHideOnTap,

      /* Callbacks method on Alert Tapped, on Alert Show, on Alert Hide */
      onTapped: props.onTapped,
      onShow: props.onShow,
      onHide: props.onHide,

      /* Stylesheets */
      stylesheetInfo: props.stylesheetInfo || {
        backgroundColor: '#007bff',
        strokeColor: '#006acd'
      }, // Default are blue colors
      stylesheetSuccess: props.stylesheetSuccess || {
        backgroundColor: 'darkgreen',
        strokeColor: 'darkgreen'
      }, // Default are Green colors
      stylesheetWarning: props.stylesheetWarning || {
        backgroundColor: '#ff9c00',
        strokeColor: '#f29400'
      }, // Default are orange colors
      stylesheetError: props.stylesheetError || {
        backgroundColor: '#ff3232',
        strokeColor: '#FF0000'
      }, // Default are red colors
      stylesheetExtra: props.stylesheetExtra || {
        backgroundColor: '#007bff',
        strokeColor: '#006acd'
      }, // Default are blue colors, same as info

      /* Duration of the animation */
      durationToShow: props.durationToShow || 350,
      durationToHide: props.durationToHide || 350,

      /* Offset of the View, useful if you have a navigation bar or if you want the alert be shown below another component instead of the top of the screen */
      viewTopOffset: props.viewTopOffset || 0,
      viewBottomOffset: props.viewBottomOffset || 0,
      viewLeftOffset: props.viewLeftOffset || 0,
      viewRightOffset: props.viewRightOffset || 0,

      /* Inset of the view, useful if you want to apply a padding at your alert content */
      viewTopInset: props.viewTopInset || 0,
      viewBottomInset: props.viewBottomInset || 0,
      viewLeftInset: props.viewLeftInset || 0,
      viewRightInset: props.viewRightInset || 0,

      /* Padding around the content, useful if you want a tiny message bar */
      messageBarPadding: props.messageBarPadding || 10,

      /* Number of Lines for Title and Message */
      titleNumberOfLines: props.titleNumberOfLines == undefined
        ? 1
        : props.titleNumberOfLines,
      messageNumberOfLines: props.messageNumberOfLines == undefined
        ? 2
        : props.messageNumberOfLines,

      /* Style for the text elements and the avatar */
      titleStyle: props.titleStyle || {
        color: 'white',
        fontSize: 18,
        fontWeight: 'bold'
      },
      messageStyle: props.messageStyle || { color: 'white', fontSize: 16 },
      avatarStyle: props.avatarStyle || {
        height: 40,
        width: 40,
        borderRadius: 20
      },

      /* Position of the alert and Animation Type the alert is shown */
      position: props.position || 'top',
      animationType: props.animationType
    }
  }

  /*
  * Show the alert
  */
  showMessageBarAlert () {
    // If an alert is already shonw or doesn't have a title or a message, do nothing
    if (
      this.alertShown ||
      (this.state.title == null && this.state.message == null)
    ) {
      return
    }

    // Set the data of the alert in the state
    this.alertShown = true

    // Display the alert by animating it from the top of the screen
    // Auto-Hide it after a delay set in the state
    Animated.timing(this.animatedValue, {
      toValue: 1,
      duration: this.state.durationToShow
    }).start(this._showMessageBarAlertComplete())
  }

  /*
  * Hide the alert after a delay, typically used for auto-hidding
  */
  _showMessageBarAlertComplete () {
    // Execute onShow callback if any
    this._onShow()

    // If the duration is null, do not hide the
    if (this.state.shouldHideAfterDelay) {
      this.timeoutHide = setTimeout(() => {
        this.hideMessageBarAlert()
      }, this.state.duration)
    }
  }

  /*
  * Return true if the MessageBar is currently displayed, otherwise false
  */
  isMessageBarShown () {
    return this.alertShown
  }

  /*
  * Hide the alert, typically used when user tap the alert
  */
  hideMessageBarAlert () {
    // Hide the alert after a delay set in the state only if the alert is still visible
    if (!this.alertShown) {
      return
    }

    clearTimeout(this.timeoutHide)

    // Animate the alert to hide it to the top of the screen
    Animated.timing(this.animatedValue, {
      toValue: 0,
      duration: this.state.durationToHide
    }).start(this._hideMessageBarAlertComplete())
  }

  _hideMessageBarAlertComplete () {
    // The alert is not shown anymore
    this.alertShown = false

    this._notifyAlertHidden()

    // Execute onHide callback if any
    this._onHide()
  }

  /*
  * Callback executed to tell the observer the alert is hidden
  */
  _notifyAlertHidden () {
    if (this.notifyAlertHiddenCallback) {
      this.notifyAlertHiddenCallback()
    }
  }

  /*
  * Callback executed when the user tap the alert
  */
  _alertTapped () {
    // Hide the alert
    if (this.state.shouldHideOnTap) {
      this.hideMessageBarAlert()
    }

    // Execute the callback passed in parameter
    if (this.state.onTapped) {
      this.state.onTapped()
    }
  }

  /*
  * Callback executed when alert is shown
  */
  _onShow () {
    if (this.state.onShow) {
      this.state.onShow()
    }
  }

  /*
  * Callback executed when alert is hidden
  */
  _onHide () {
    if (this.state.onHide) {
      this.state.onHide()
    }
  }

  /*
  * Change the background color and the line stroke color depending on the alertType
  * If the alertType is not recognized, the 'info' one (blue colors) is selected for you
  */
  _applyAlertStylesheet (alertType) {
    // Set the Background color and the line stroke color of the alert depending on its alertType
    // Set to blue-info if no alertType or if the alertType is not recognized

    let backgroundColor
    let strokeColor

    switch (alertType) {
      case 'success':
        backgroundColor = this.state.stylesheetSuccess.backgroundColor
        strokeColor = this.state.stylesheetSuccess.strokeColor
        break
      case 'error':
        backgroundColor = this.state.stylesheetError.backgroundColor
        strokeColor = this.state.stylesheetError.strokeColor
        break
      case 'warning':
        backgroundColor = this.state.stylesheetWarning.backgroundColor
        strokeColor = this.state.stylesheetWarning.strokeColor
        break
      case 'info':
        backgroundColor = this.state.stylesheetInfo.backgroundColor
        strokeColor = this.state.stylesheetInfo.strokeColor
        break
      default:
        backgroundColor = this.state.stylesheetExtra.backgroundColor
        strokeColor = this.state.stylesheetExtra.strokeColor
        break
    }

    this.setState({
      backgroundColor: backgroundColor,
      strokeColor: strokeColor
    })
  }

  /*
  * Change view<Position>Offset property depending on the state position
  */
  _changeOffsetByPosition (position) {
    switch (position) {
      case 'top':
        this.setState({
          viewBottomOffset: null
        })
        break
      case 'bottom':
        this.setState({
          viewTopOffset: null
        })
        break
      default:
        this.setState({
          viewBottomOffset: null
        })
        break
    }
  }

  /*
  * Set the animation transformation depending on the chosen animationType, or depending on the state's position if animationType is not overridden
  */
  _applyAnimationTypeTransformation () {
    let position = this.state.position
    let animationType = this.state.animationType

    if (animationType === undefined) {
      if (position === 'bottom') {
        animationType = 'SlideFromBottom'
      } else {
        // Top by default
        animationType = 'SlideFromTop'
      }
    }

    switch (animationType) {
      case 'SlideFromTop':
        var animationY = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [-windowHeight, 0]
        })
        this.animationTypeTransform = [{ translateY: animationY }]
        break
      case 'SlideFromBottom':
        var animationY = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [windowHeight, 0]
        })
        this.animationTypeTransform = [{ translateY: animationY }]
        break
      case 'SlideFromLeft':
        var animationX = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [-windowWidth, 0]
        })
        this.animationTypeTransform = [{ translateX: animationX }]
        break
      case 'SlideFromRight':
        var animationX = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [windowWidth, 0]
        })
        this.animationTypeTransform = [{ translateX: animationX }]
        break
      default:
        var animationY = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [-windowHeight, 0]
        })
        this.animationTypeTransform = [{ translateY: animationY }]
        break
    }
  }

  /*
  * Alert Rendering Methods
  */

  render () {
    // Set the animation transformation depending on the chosen animationType, or depending on the state's position if animationType is not overridden
    this._applyAnimationTypeTransformation()

    return (
      <Animated.View
        style={{
          transform: this.animationTypeTransform,
          backgroundColor: this.state.backgroundColor,
          borderColor: this.state.strokeColor,
          borderBottomWidth: 1,
          position: 'absolute',
          top: this.state.viewTopOffset,
          bottom: this.state.viewBottomOffset,
          left: this.state.viewLeftOffset,
          right: this.state.viewRightOffset,
          paddingTop: this.state.viewTopInset,
          paddingBottom: this.state.viewBottomInset,
          paddingLeft: this.state.viewLeftInset,
          paddingRight: this.state.viewRightInset
        }}>
        <TouchableOpacity
          onPress={() => {
            this._alertTapped()
          }}
          style={{ flex: 1 }}>
          <View
            style={{
              flex: 1,
              flexDirection: 'row',
              alignItems: 'flex-end',
              padding: this.state.messageBarPadding
            }}>
            {this.renderImage()}
            <View
              style={{
                flex: 1,
                flexDirection: 'column',
                alignSelf: 'stretch',
                justifyContent: 'center',
                marginLeft: 10
              }}>
              {this.renderTitle()}
              {this.renderMessage()}
            </View>
          </View>
        </TouchableOpacity>
      </Animated.View>
    )
  }

  renderImage () {
    if (this.state.avatar != null) {
      var imageSource
      var avatar = this.state.avatar

      if (typeof avatar === 'string') {
        if (avatar.match(/^https?:/)) {
          // this is a network file
          imageSource = { uri: avatar }
        } else {
          // this is a local file : require('<path/to/my/local/image.extension>')
          imageSource = avatar
        }

        return <Image source={imageSource} style={this.state.avatarStyle} />
      } else if (React.isValidElement(avatar)) {
        // this is a react component
        return avatar
      }
    }
  }

  renderTitle () {
    if (this.state.title != null) {
      return (
        <Text
          numberOfLines={this.state.titleNumberOfLines}
          style={this.state.titleStyle}>
          {this.state.title}
        </Text>
      )
    }
  }

  renderMessage () {
    if (this.state.message != null) {
      return (
        <Text
          numberOfLines={this.state.messageNumberOfLines}
          style={this.state.messageStyle}>
          {this.state.message}
        </Text>
      )
    }
  }
}

module.exports = MessageBar
<commit_msg>Enable setting default per-component values.<commit_after>/**
 * Name: MessageBar
 * Description: A Message Bar Component displayed at the top of screen
 * https://github.com/talor-a/react-native-message-bar
 */
'use strict'

import React, { Component } from 'react'
import {
  Text,
  View,
  TouchableOpacity,
  Animated,
  Dimensions,
  Image
} from 'react-native'

let windowWidth = Dimensions.get('window').width
let windowHeight = Dimensions.get('window').height

class MessageBar extends Component {
  constructor (props) {
    super(props)

    this.animatedValue = new Animated.Value(0)
    this.notifyAlertHiddenCallback = null
    this.alertShown = false
    this.timeoutHide = null

    this.state = this.getStateByProps(props)
    this.defaultState = this.getStateByProps(props)
  }

  componentDidMount () {
    // Configure the offsets prior to recieving updated props or recieving the first alert
    // This ensures the offsets are set properly at the outset based on the initial position.
    // This prevents the bar from appearing  and covering half of the screen when the
    // device is started in landscape and then rotated to portrait.
    // This does not happen after the first alert appears, as setNewState() is called on each
    // alert and calls _changeOffsetByPosition()
    this._changeOffsetByPosition(this.state.position)
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps && Object.keys(nextProps).length > 0) {
      this.setNewState(nextProps)
    }
  }

  setNewState (state) {
    // Set the new state, this is triggered when the props of this MessageBar changed
    this.setState(this.getStateByProps(state))

    // Apply the colors of the alert depending on its alertType
    this._applyAlertStylesheet(state.alertType)

    // Override the opposition style position regarding the state position in order to have the alert sticks that position
    this._changeOffsetByPosition(state.position)
  }

  getStateByProps (props) {
    const def = this.defaultState || {}
    return {
      // Default values, will be overridden
      backgroundColor: '#007bff', // default value : blue
      strokeColor: '#006acd', // default value : blue
      titleColor: '#ffffff', // default value : white
      messageColor: '#ffffff', // default value : white
      animationTypeTransform: 'SlideFromTop', // default value

      /* Cusomisation of the alert: Title, Message, Icon URL, Alert alertType (error, success, warning, info), Duration for Alert keep shown */
      title: props.title,
      message: props.message,
      avatar: props.avatar,
      alertType: props.alertType || 'info',
      duration: props.duration || def.duration || 3000,

      /* Hide setters */
      shouldHideAfterDelay:
        props.shouldHideAfterDelay == undefined &&
        def.shouldHideAfterDelay == undefined
          ? true
          : props.shouldHideAfterDelay || def.shouldHideAfterDelay,
      shouldHideOnTap:
        props.shouldHideOnTap == undefined &&
        def.shouldHideOnTap == undefined
          ? true
          : props.shouldHideOnTap || def.shouldHideOnTap,

      /* Callbacks method on Alert Tapped, on Alert Show, on Alert Hide */
      onTapped: props.onTapped || def.onTapped,
      onShow: props.onShow || def.onShow,
      onHide: props.onHide || def.onHide,

      /* Stylesheets */
      stylesheetInfo: props.stylesheetInfo ||
      def.stylesheetInfo || {
        backgroundColor: '#007bff',
        strokeColor: '#006acd',
        titleColor: '#ffffff',
        messageColor: '#ffffff'
      }, // Default are blue colors
      stylesheetSuccess: props.stylesheetSuccess ||
      def.stylesheetSuccess || {
        backgroundColor: 'darkgreen',
        strokeColor: 'darkgreen',
        titleColor: '#ffffff',
        messageColor: '#ffffff'
      }, // Default are Green colors
      stylesheetWarning: props.stylesheetWarning ||
      def.stylesheetWarning || {
        backgroundColor: '#ff9c00',
        strokeColor: '#f29400',
        titleColor: '#ffffff',
        messageColor: '#ffffff'
      }, // Default are orange colors
      stylesheetError: props.stylesheetError ||
      def.stylesheetError || {
        backgroundColor: '#ff3232',
        strokeColor: '#FF0000',
        titleColor: '#ffffff',
        messageColor: '#ffffff'
      }, // Default are red colors
      stylesheetExtra: props.stylesheetExtra ||
      def.stylesheetExtra || {
        backgroundColor: '#007bff',
        strokeColor: '#006acd',
        titleColor: '#ffffff',
        messageColor: '#ffffff'
      }, // Default are blue colors, same as info

      /* Duration of the animation */
      durationToShow: props.durationToShow || def.durationToShow || 350,
      durationToHide: props.durationToHide || def.durationToHide || 350,

      /* Offset of the View, useful if you have a navigation bar or if you want the alert be shown below another component instead of the top of the screen */
      viewTopOffset: props.viewTopOffset || def.viewTopOffset || 0,
      viewBottomOffset: props.viewBottomOffset || def.viewBottomOffset || 0,
      viewLeftOffset: props.viewLeftOffset || def.viewLeftOffset || 0,
      viewRightOffset: props.viewRightOffset || def.viewRightOffset || 0,

      /* Inset of the view, useful if you want to apply a padding at your alert content */
      viewTopInset: props.viewTopInset || def.viewTopInset || 0,
      viewBottomInset: props.viewBottomInset || def.viewBottomInset || 0,
      viewLeftInset: props.viewLeftInset || def.viewLeftInset || 0,
      viewRightInset: props.viewRightInset || def.viewRightInset || 0,

      /* Padding around the content, useful if you want a tiny message bar */
      messageBarPadding: props.messageBarPadding || def.messageBarPadding || 10,

      /* Number of Lines for Title and Message */
      titleNumberOfLines:
        props.titleNumberOfLines == undefined &&
        def.titleNumberOfLines == undefined
          ? 1
          : props.titleNumberOfLines || def.titleNumberOfLines,
      messageNumberOfLines:
        props.messageNumberOfLines == undefined &&
        def.titleNumberOfLines == undefined
          ? 2
          : props.messageNumberOfLines || def.messageNumberOfLines,

      /* Style for the text elements and the avatar */
      titleStyle: props.titleStyle || def.titleStyle || {
        fontSize: 18,
        fontWeight: 'bold'
      },
      messageStyle: props.messageStyle || def.messageStyle || {
        fontSize: 16
      },
      avatarStyle: props.avatarStyle || def.avatarStyle || {
        height: 40,
        width: 40,
        borderRadius: 20
      },

      /* Position of the alert and Animation Type the alert is shown */
      position: props.position || def.position || 'top',
      animationType: props.animationType || def.animationType
    }
  }

  /*
  * Show the alert
  */
  showMessageBarAlert () {
    // If an alert is already shonw or doesn't have a title or a message, do nothing
    if (
      this.alertShown ||
      (this.state.title == null && this.state.message == null)
    ) {
      return
    }

    // Set the data of the alert in the state
    this.alertShown = true

    // Display the alert by animating it from the top of the screen
    // Auto-Hide it after a delay set in the state
    Animated.timing(this.animatedValue, {
      toValue: 1,
      duration: this.state.durationToShow
    }).start(this._showMessageBarAlertComplete())
  }

  /*
  * Hide the alert after a delay, typically used for auto-hidding
  */
  _showMessageBarAlertComplete () {
    // Execute onShow callback if any
    this._onShow()

    // If the duration is null, do not hide the
    if (this.state.shouldHideAfterDelay) {
      this.timeoutHide = setTimeout(() => {
        this.hideMessageBarAlert()
      }, this.state.duration)
    }
  }

  /*
  * Return true if the MessageBar is currently displayed, otherwise false
  */
  isMessageBarShown () {
    return this.alertShown
  }

  /*
  * Hide the alert, typically used when user tap the alert
  */
  hideMessageBarAlert () {
    // Hide the alert after a delay set in the state only if the alert is still visible
    if (!this.alertShown) {
      return
    }

    clearTimeout(this.timeoutHide)

    // Animate the alert to hide it to the top of the screen
    Animated.timing(this.animatedValue, {
      toValue: 0,
      duration: this.state.durationToHide
    }).start(this._hideMessageBarAlertComplete())
  }

  _hideMessageBarAlertComplete () {
    // The alert is not shown anymore
    this.alertShown = false

    this._notifyAlertHidden()

    // Execute onHide callback if any
    this._onHide()
  }

  /*
  * Callback executed to tell the observer the alert is hidden
  */
  _notifyAlertHidden () {
    if (this.notifyAlertHiddenCallback) {
      this.notifyAlertHiddenCallback()
    }
  }

  /*
  * Callback executed when the user tap the alert
  */
  _alertTapped () {
    // Hide the alert
    if (this.state.shouldHideOnTap) {
      this.hideMessageBarAlert()
    }

    // Execute the callback passed in parameter
    if (this.state.onTapped) {
      this.state.onTapped()
    }
  }

  /*
  * Callback executed when alert is shown
  */
  _onShow () {
    if (this.state.onShow) {
      this.state.onShow()
    }
  }

  /*
  * Callback executed when alert is hidden
  */
  _onHide () {
    if (this.state.onHide) {
      this.state.onHide()
    }
  }

  /*
  * Change the background color and the line stroke color depending on the alertType
  * If the alertType is not recognized, the 'info' one (blue colors) is selected for you
  */
  _applyAlertStylesheet (alertType) {
    // Set the Background color and the line stroke color of the alert depending on its alertType
    // Set to blue-info if no alertType or if the alertType is not recognized

    let backgroundColor
    let strokeColor
    let titleColor
    let messageColor

    switch (alertType) {
      case 'success':
        backgroundColor = this.state.stylesheetSuccess.backgroundColor
        strokeColor = this.state.stylesheetSuccess.strokeColor
        titleColor = this.state.stylesheetSuccess.titleColor
        messageColor = this.state.stylesheetSuccess.messageColor
        break
      case 'error':
        backgroundColor = this.state.stylesheetError.backgroundColor
        strokeColor = this.state.stylesheetError.strokeColor
        titleColor = this.state.stylesheetError.titleColor
        messageColor = this.state.stylesheetError.messageColor
        break
      case 'warning':
        backgroundColor = this.state.stylesheetWarning.backgroundColor
        strokeColor = this.state.stylesheetWarning.strokeColor
        titleColor = this.state.stylesheetWarning.titleColor
        messageColor = this.state.stylesheetWarning.messageColor
        break
      case 'info':
        backgroundColor = this.state.stylesheetInfo.backgroundColor
        strokeColor = this.state.stylesheetInfo.strokeColor
        titleColor = this.state.stylesheetInfo.titleColor
        messageColor = this.state.stylesheetInfo.messageColor
        break
      default:
        backgroundColor = this.state.stylesheetExtra.backgroundColor
        strokeColor = this.state.stylesheetExtra.strokeColor
        titleColor = this.state.stylesheetExtra.titleColor
        messageColor = this.state.stylesheetExtra.messageColor
        break
    }

    this.setState({
      backgroundColor: backgroundColor,
      strokeColor: strokeColor,
      titleColor: titleColor,
      messageColor: messageColor
    })
  }

  /*
  * Change view<Position>Offset property depending on the state position
  */
  _changeOffsetByPosition (position) {
    switch (position) {
      case 'top':
        this.setState({
          viewBottomOffset: null
        })
        break
      case 'bottom':
        this.setState({
          viewTopOffset: null
        })
        break
      default:
        this.setState({
          viewBottomOffset: null
        })
        break
    }
  }

  /*
  * Set the animation transformation depending on the chosen animationType, or depending on the state's position if animationType is not overridden
  */
  _applyAnimationTypeTransformation () {
    let position = this.state.position
    let animationType = this.state.animationType

    if (animationType === undefined) {
      if (position === 'bottom') {
        animationType = 'SlideFromBottom'
      } else {
        // Top by default
        animationType = 'SlideFromTop'
      }
    }

    switch (animationType) {
      case 'SlideFromTop':
        var animationY = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [-windowHeight, 0]
        })
        this.animationTypeTransform = [{ translateY: animationY }]
        break
      case 'SlideFromBottom':
        var animationY = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [windowHeight, 0]
        })
        this.animationTypeTransform = [{ translateY: animationY }]
        break
      case 'SlideFromLeft':
        var animationX = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [-windowWidth, 0]
        })
        this.animationTypeTransform = [{ translateX: animationX }]
        break
      case 'SlideFromRight':
        var animationX = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [windowWidth, 0]
        })
        this.animationTypeTransform = [{ translateX: animationX }]
        break
      default:
        var animationY = this.animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [-windowHeight, 0]
        })
        this.animationTypeTransform = [{ translateY: animationY }]
        break
    }
  }

  /*
  * Alert Rendering Methods
  */

  render () {
    // Set the animation transformation depending on the chosen animationType, or depending on the state's position if animationType is not overridden
    this._applyAnimationTypeTransformation()

    return (
      <Animated.View
        style={{
          transform: this.animationTypeTransform,
          backgroundColor: this.state.backgroundColor,
          borderColor: this.state.strokeColor,
          borderBottomWidth: 1,
          position: 'absolute',
          top: this.state.viewTopOffset,
          bottom: this.state.viewBottomOffset,
          left: this.state.viewLeftOffset,
          right: this.state.viewRightOffset,
          paddingTop: this.state.viewTopInset,
          paddingBottom: this.state.viewBottomInset,
          paddingLeft: this.state.viewLeftInset,
          paddingRight: this.state.viewRightInset
        }}>
        <TouchableOpacity
          onPress={() => {
            this._alertTapped()
          }}
          style={{ flex: 1 }}>
          <View
            style={{
              flex: 1,
              flexDirection: 'row',
              alignItems: 'flex-end',
              padding: this.state.messageBarPadding
            }}>
            {this.renderImage()}
            <View
              style={{
                flex: 1,
                flexDirection: 'column',
                alignSelf: 'stretch',
                justifyContent: 'center',
                marginLeft: 10
              }}>
              {this.renderTitle()}
              {this.renderMessage()}
            </View>
          </View>
        </TouchableOpacity>
      </Animated.View>
    )
  }

  renderImage () {
    if (this.state.avatar != null) {
      var imageSource
      var avatar = this.state.avatar

      if (typeof avatar === 'string') {
        if (avatar.match(/^https?:/)) {
          // this is a network file
          imageSource = { uri: avatar }
        } else {
          // this is a local file : require('<path/to/my/local/image.extension>')
          imageSource = avatar
        }

        return <Image source={imageSource} style={this.state.avatarStyle} />
      } else if (React.isValidElement(avatar)) {
        // this is a react component
        return avatar
      }
    }
  }

  renderTitle () {
    if (this.state.title != null) {
      return (
        <Text
          numberOfLines={this.state.titleNumberOfLines}
          style={[this.state.titleStyle, {color: this.state.titleColor}]}>
          {this.state.title}
        </Text>
      )
    }
  }

  renderMessage () {
    if (this.state.message != null) {
      return (
        <Text
          numberOfLines={this.state.messageNumberOfLines}
          style={[this.state.messageStyle, {color: this.state.messageColor}]}>
          {this.state.message}
        </Text>
      )
    }
  }
}

module.exports = MessageBar
<|endoftext|>


<commit_before>function karyo_to_coords(data) {
	var total = 0;
	var spacer = set_spacer(data);
	$.each(data.chromosomes, function(key, value) {
		total += value.length + spacer;
	});
	var current = 0;
	var index = 0;
	for (var i = 0; i < data.order.length; i++) {
		var key = data.order[i];
		var value = data.chromosomes[key];
		data.chromosomes[key] = {
			"value" : value.length,
			"startAngle" : ((current + spacer) / total) * (2 * Math.PI),
			"index" : index++,
			"genome_id" : value.genome_id,
			"name" : key,
			"rc" : value.rc
		};
		current += value.length + spacer;
		data.chromosomes[key].endAngle = (current / total) * (2 * Math.PI);

		if (value.rc == true) {
			var startAngle = data.chromosomes[key].startAngle;
			var endAngle = data.chromosomes[key].endAngle;
			data.chromosomes[key].startAngle = endAngle;
			data.chromosomes[key].endAngle = startAngle;
		}

	}
	return data.chromosomes;
}

function set_spacer(data) {
	var spacer = 0;
	$.each(data.chromosomes, function(key, value) {
		spacer += value.length;
	});
	spacer = spacer * 0.0038; // ca. 4% der Gesamtsumme aller Sequenzen //
	// entsprechen dem geeigneten Spacer
	return spacer;
}

function create_new_karyo(karyo, g) {
	var key = g.name;
	var endAngle = karyo[key].endAngle;
	var startAngle = karyo[key].startAngle;
	karyo[key] = {
		"value" : karyo[key].value,
		"startAngle" : endAngle,
		"index" : karyo[key].index,
		"genome_id" : karyo[key].genome_id,
		"name" : karyo[key].name,
		"endAngle" : startAngle,
		"rc" : false
	};
	return karyo;
}
<commit_msg>Korrekter Vergleich von value und object<commit_after>function karyo_to_coords(data) {
	var total = 0;
	var spacer = set_spacer(data);
	$.each(data.chromosomes, function(key, value) {
		total += value.length + spacer;
	});
	var current = 0;
	var index = 0;
	for (var i = 0; i < data.order.length; i++) {
		var key = data.order[i];
		var value = data.chromosomes[key];
		data.chromosomes[key] = {
			"value" : value.length,
			"startAngle" : ((current + spacer) / total) * (2 * Math.PI),
			"index" : index++,
			"genome_id" : value.genome_id,
			"name" : key,
			"rc" : value.rc
		};
		current += value.length + spacer;
		data.chromosomes[key].endAngle = (current / total) * (2 * Math.PI);

		if (value.rc === true) {
			var startAngle = data.chromosomes[key].startAngle;
			var endAngle = data.chromosomes[key].endAngle;
			data.chromosomes[key].startAngle = endAngle;
			data.chromosomes[key].endAngle = startAngle;
		}

	}
	return data.chromosomes;
}

function set_spacer(data) {
	var spacer = 0;
	$.each(data.chromosomes, function(key, value) {
		spacer += value.length;
	});
	spacer = spacer * 0.0038; // ca. 4% der Gesamtsumme aller Sequenzen //
	// entsprechen dem geeigneten Spacer
	return spacer;
}

function create_new_karyo(karyo, g) {
	var key = g.name;
	var endAngle = karyo[key].endAngle;
	var startAngle = karyo[key].startAngle;
	karyo[key] = {
		"value" : karyo[key].value,
		"startAngle" : endAngle,
		"index" : karyo[key].index,
		"genome_id" : karyo[key].genome_id,
		"name" : karyo[key].name,
		"endAngle" : startAngle,
		"rc" : false
	};
	return karyo;
}
<|endoftext|>


<commit_before>define([
	"dojo/_base/lang",
	"dojo/_base/declare",
	"dojo/aspect",
	"dojo/when",
	"dojo/promise/all",
	"dojo/_base/array",
	"dojo/on",
	/*=====, "./api/Store" =====*/
], function(lang, declare, aspect, when, whenAll, arrayUtil, on /*=====, Store =====*/){

// module:
//		dstore/Observable
var undef, revision = 0;

	function createRange(newStart, newEnd){
		return {
			start: newStart,
			count: newEnd - newStart
		};
	}

	function registerRange(ranges, newStart, newEnd){
		for(var i = ranges.length - 1; i >= 0; --i){
			var existingRange = ranges[i],
				existingStart = existingRange.start,
				existingEnd = existingStart + existingRange.count;

			if (newStart > existingEnd){
				// existing range completely precedes new range. we are done.
				ranges.splice(i + 1, 0, createRange(newStart, newEnd));
				return;
			}else if(newEnd >= existingStart){
				// the ranges overlap and must be merged into a single range
				newStart = Math.min(newStart, existingStart);
				newEnd = Math.max(newEnd, existingEnd);
				ranges.splice(i, 1);
			}
		}

		ranges.unshift(createRange(newStart, newEnd));
	}

	function unregisterRange(ranges, start, end){
		for(var i = 0, range; (range = ranges[i]); ++i){
			var existingStart = range.start,
				existingEnd = existingStart + range.count;

			if(start <= existingStart){
				if(end >= existingEnd){
					// The existing range is within the forgotten range
					ranges.splice(i, 1);
				}else{
					// The forgotten range overlaps the beginning of the existing range
					range.start = end;
					range.count = existingEnd - range.start;

					// Since the forgotten range ends before the existing range,
					// there are no more ranges to update, and we are done
					return;
				}
			}else if(start < existingEnd){
				if(end > existingStart){
					// The forgotten range is within the existing range
					ranges.splice(i, 1, createRange(existingStart, start), createRange(end, existingEnd));

					// We are done because the existing range bounded the forgotten range
					return;
				}else{
					// The forgotten range overlaps the end of the existing range
					range.count = start - range.start;
				}
			}
		}
	}

return declare(null, {
	currentRange: [],
	track: function(){
		var store = this.store || this;

		// monitor for updates by listening to these methods
		var handles = [];
		var eventTypes = {add: 1, update: 1, remove: 1};
		// register to listen for updates
		for(var type in eventTypes){
			this.on(type, (function(type){
				return function(event){
					notify(type, event.target || event.id, event);
				};
			})(type));
		}

		var observed = lang.delegate(this, {
			store: store,
			remove: function(){
				while(handles.length > 0){
					handles.pop().remove();
				}

				this.remove = function(){};
			}
		});

		var originalOn = this.on;
		// now setup our own event scope, for tracked events
		observed.on = function(type, listener){
				return on.parse(observed, type, listener, function(target, type){
				return type in eventTypes ?
					aspect.after(observed, 'ontracked' + type, listener, true) :
					originalOn.call(observed, type, listener);
			});
	};

		var ranges = [];
		if(this.hasOwnProperty('data')){
			observed.data = this.data.slice(0); // make local copy
			// Treat in-memory data as one range to allow a single code path for all stores
			registerRange(ranges, 0, observed.data.length);

			observed.releaseRange = function(){};
		}else{
			var originalRange = observed.range;
			observed.range = function(start, end){
				// trigger a request
				var rangeCollection = originalRange.apply(this, arguments).fetch(),
					partialData = this.hasOwnProperty('partialData') ? this.partialData : (this.partialData = []);

				// Wait for total in addition to data so updated objects sorted to
				// the end of the list have a known index
				whenAll({
					data: rangeCollection.data,
					total: rangeCollection.total
				}).then(function(result){
					partialData.length = result.total;

					// TODO: If the range overlaps an existing range, existing objects will be refreshed. Should there be an update notification?
					// copy the new ranged data into the parent partial data set
					var spliceArgs = [ start, end - start ].concat(result.data);
					partialData.splice.apply(partialData, spliceArgs);
					registerRange(ranges, start, end);
				});
				return rangeCollection;
			};
			observed.releaseRange = function(start, end){
				unregisterRange(ranges, start, end);

				for(var i = start; i < end; ++i){
					delete this.partialData[i];
				}
			};

			// Clear partialData because the item order is unknown after sort
			var originalSort = observed.sort;
			observed.sort = function(){
				delete this.partialData;
				return originalSort.apply(this, arguments);
			};
		}

		function notify(type, target, event){
			revision++;
			event = lang.delegate(event);
			when(observed.hasOwnProperty('data') ? observed.data : observed.partialData, function(resultsArray){
				var queryExecutor = observed.queryer;
				var atEnd = false;//resultsArray.length != options.count;
				var i, j, l, range;
				/*if(++queryRevision != revision){
					throw new Error("Query is out of date, you must observe() the query prior to any data modifications");
				}*/

				var targetId = type === "remove" ? target : store.getIdentity(target);
				// TODO: Should we explicitly define undefined `index` and `previousIndex` properties so the API is more apparent when inspecting in the debugger?
				var info = event.info = {};

				// TODO: `total` should probably be updated when items are added and removed from the data
				var removedObject, removedFrom = -1, removalRangeIndex = -1, insertedInto = -1, insertionRangeIndex = -1;
				if(type === "remove" || type === "update"){
					// remove the old one
					for(i = 0; removedFrom === -1 && i < ranges.length; ++i){
						range = ranges[i];
						for(j = range.start, l = j + range.count; j < l; ++j){
							var object = resultsArray[j];
							if(store.getIdentity(object) == targetId){
								removedFrom = info.previousIndex = j;
								removalRangeIndex = i;
								resultsArray.splice(removedFrom, 1);

								range.count--;
								for(j = i + 1; j < ranges.length; ++j){
									ranges[j].start--;
								}

								break;
							}
						}
					}
				}

				if(type === "add" || type === "update"){
					if(queryExecutor){
						// with a queryExecutor, we can determine the correct sorted index for the change

						if(queryExecutor.matches ? queryer.matches(target) : queryExecutor([target]).length){
							var begin = 0,
								end = ranges.length - 1,
								sampleArray,
								sortedIndex,
								adjustedIndex;
							while (begin <= end && insertedInto === -1){
								i = begin + Math.round((end - begin) / 2);
								range = ranges[i];

								sampleArray = resultsArray.slice(range.start, range.start + range.count);

								// If the original index came from this range, put back in the original slot
								// so it doesn't move unless it needs to (relying on a stable sort below)
								if(removedFrom >= Math.max(0, range.start - 1) && removedFrom <= (range.start + range.count)){
									sampleArray.splice(removedFrom, 0, target);
								}else{
									sampleArray.push(target);
								}

								sortedIndex = arrayUtil.indexOf(queryExecutor(sampleArray), target);
								// TODO: Is there a better name than adjustedIndex?
								adjustedIndex = range.start + sortedIndex;

								if(sortedIndex === 0 && range.start !== 0){
									end = i - 1;
								}else if(sortedIndex >= (sampleArray.length - 1) && adjustedIndex < resultsArray.length){
									begin = i + 1;
								}else{
									insertedInto = adjustedIndex;
									insertionRangeIndex = i;
								}
							}
						}
					}else{
						// we don't have a queryEngine, so we can't provide any information
						// about where it was inserted or moved to. If it is an update, we leave it's position alone. other we at least indicate a new object

						if(type === "update"){
							insertedInto = removedFrom;
							insertionRangeIndex = removalRangeIndex;
						}else{
							// TODO: Should there be a default index for a new object of undetermined index?
							//		It seems like sending "add" notification with no index might be more appropriate.
							//		On the other hand, when adding to an unsorted list, I would expect the new element to be appended.
							// a new object
							insertedInto = store.defaultIndex || 0;

							var range;
							for(i = 0; insertionRangeIndex === -1 && i < ranges.length; ++i){
								range = ranges[i];
								if(range.start <= insertedInto && insertedInto < (range.start + range.count)){
									insertionRangeIndex = i;
								}
							}
						}
					}

					if(insertedInto > -1){
						info.index = insertedInto;
						resultsArray.splice(insertedInto, 0, target);

						// TODO: NOTE: This is broken for a non-zero store.defaultIndex because, when an insertion range is not found, this code assumes insertion at the beginning.
						if(insertionRangeIndex > -1){
							ranges[insertionRangeIndex].count++;
						}
						for(i = insertionRangeIndex + 1; i < ranges.length; ++i){
							ranges[i].start++;
						}
					}
				}

				// TODO: Eventually we will want to aggregate all the listener events
				// in an event turn, but we will wait until we have a reliable, performant queueing
				// mechanism for this (besides setTimeout)
				type = 'ontracked' + type;
				observed[type] && observed[type](event);
			});
		}

		return observed;
	},
	// a Comet driven store could directly call notify to notify observers when data has
	// changed on the backend
	// create a new instance
	notify: function(type, target){
	}
});
});
<commit_msg>Remove old TODOs and add default event properties<commit_after>define([
	"dojo/_base/lang",
	"dojo/_base/declare",
	"dojo/aspect",
	"dojo/when",
	"dojo/promise/all",
	"dojo/_base/array",
	"dojo/on",
	/*=====, "./api/Store" =====*/
], function(lang, declare, aspect, when, whenAll, arrayUtil, on /*=====, Store =====*/){

// module:
//		dstore/Observable
var undef, revision = 0;

	function createRange(newStart, newEnd){
		return {
			start: newStart,
			count: newEnd - newStart
		};
	}

	function registerRange(ranges, newStart, newEnd){
		for(var i = ranges.length - 1; i >= 0; --i){
			var existingRange = ranges[i],
				existingStart = existingRange.start,
				existingEnd = existingStart + existingRange.count;

			if (newStart > existingEnd){
				// existing range completely precedes new range. we are done.
				ranges.splice(i + 1, 0, createRange(newStart, newEnd));
				return;
			}else if(newEnd >= existingStart){
				// the ranges overlap and must be merged into a single range
				newStart = Math.min(newStart, existingStart);
				newEnd = Math.max(newEnd, existingEnd);
				ranges.splice(i, 1);
			}
		}

		ranges.unshift(createRange(newStart, newEnd));
	}

	function unregisterRange(ranges, start, end){
		for(var i = 0, range; (range = ranges[i]); ++i){
			var existingStart = range.start,
				existingEnd = existingStart + range.count;

			if(start <= existingStart){
				if(end >= existingEnd){
					// The existing range is within the forgotten range
					ranges.splice(i, 1);
				}else{
					// The forgotten range overlaps the beginning of the existing range
					range.start = end;
					range.count = existingEnd - range.start;

					// Since the forgotten range ends before the existing range,
					// there are no more ranges to update, and we are done
					return;
				}
			}else if(start < existingEnd){
				if(end > existingStart){
					// The forgotten range is within the existing range
					ranges.splice(i, 1, createRange(existingStart, start), createRange(end, existingEnd));

					// We are done because the existing range bounded the forgotten range
					return;
				}else{
					// The forgotten range overlaps the end of the existing range
					range.count = start - range.start;
				}
			}
		}
	}

return declare(null, {
	currentRange: [],
	track: function(){
		var store = this.store || this;

		// monitor for updates by listening to these methods
		var handles = [];
		var eventTypes = {add: 1, update: 1, remove: 1};
		// register to listen for updates
		for(var type in eventTypes){
			this.on(type, (function(type){
				return function(event){
					notify(type, event.target || event.id, event);
				};
			})(type));
		}

		var observed = lang.delegate(this, {
			store: store,
			remove: function(){
				while(handles.length > 0){
					handles.pop().remove();
				}

				this.remove = function(){};
			}
		});

		var originalOn = this.on;
		// now setup our own event scope, for tracked events
		observed.on = function(type, listener){
				return on.parse(observed, type, listener, function(target, type){
				return type in eventTypes ?
					aspect.after(observed, 'ontracked' + type, listener, true) :
					originalOn.call(observed, type, listener);
			});
	};

		var ranges = [];
		if(this.hasOwnProperty('data')){
			observed.data = this.data.slice(0); // make local copy
			// Treat in-memory data as one range to allow a single code path for all stores
			registerRange(ranges, 0, observed.data.length);

			observed.releaseRange = function(){};
		}else{
			var originalRange = observed.range;
			observed.range = function(start, end){
				// trigger a request
				var rangeCollection = originalRange.apply(this, arguments).fetch(),
					partialData = this.hasOwnProperty('partialData') ? this.partialData : (this.partialData = []);

				// Wait for total in addition to data so updated objects sorted to
				// the end of the list have a known index
				whenAll({
					data: rangeCollection.data,
					total: rangeCollection.total
				}).then(function(result){
					partialData.length = result.total;

					// copy the new ranged data into the parent partial data set
					var spliceArgs = [ start, end - start ].concat(result.data);
					partialData.splice.apply(partialData, spliceArgs);
					registerRange(ranges, start, end);
				});
				return rangeCollection;
			};
			observed.releaseRange = function(start, end){
				unregisterRange(ranges, start, end);

				for(var i = start; i < end; ++i){
					delete this.partialData[i];
				}
			};

			// Clear partialData because the item order is unknown after sort
			var originalSort = observed.sort;
			observed.sort = function(){
				delete this.partialData;
				return originalSort.apply(this, arguments);
			};
		}

		function notify(type, target, event){
			revision++;
			event = lang.delegate(event, { index: undef, previousIndex: undef });
			when(observed.hasOwnProperty('data') ? observed.data : observed.partialData, function(resultsArray){
				var queryExecutor = observed.queryer;
				var atEnd = false;//resultsArray.length != options.count;
				var i, j, l, range;
				/*if(++queryRevision != revision){
					throw new Error("Query is out of date, you must observe() the query prior to any data modifications");
				}*/

				var targetId = type === "remove" ? target : store.getIdentity(target);
				var removedObject, removedFrom = -1, removalRangeIndex = -1, insertedInto = -1, insertionRangeIndex = -1;
				if(type === "remove" || type === "update"){
					// remove the old one
					for(i = 0; removedFrom === -1 && i < ranges.length; ++i){
						range = ranges[i];
						for(j = range.start, l = j + range.count; j < l; ++j){
							var object = resultsArray[j];
							if(store.getIdentity(object) == targetId){
								removedFrom = event.previousIndex = j;
								removalRangeIndex = i;
								resultsArray.splice(removedFrom, 1);

								range.count--;
								for(j = i + 1; j < ranges.length; ++j){
									ranges[j].start--;
								}

								break;
							}
						}
					}
				}

				if(type === "add" || type === "update"){
					if(queryExecutor){
						// with a queryExecutor, we can determine the correct sorted index for the change

						if(queryExecutor.matches ? queryer.matches(target) : queryExecutor([target]).length){
							var begin = 0,
								end = ranges.length - 1,
								sampleArray,
								sortedIndex,
								adjustedIndex;
							while (begin <= end && insertedInto === -1){
								i = begin + Math.round((end - begin) / 2);
								range = ranges[i];

								sampleArray = resultsArray.slice(range.start, range.start + range.count);

								// If the original index came from this range, put back in the original slot
								// so it doesn't move unless it needs to (relying on a stable sort below)
								if(removedFrom >= Math.max(0, range.start - 1) && removedFrom <= (range.start + range.count)){
									sampleArray.splice(removedFrom, 0, target);
								}else{
									sampleArray.push(target);
								}

								sortedIndex = arrayUtil.indexOf(queryExecutor(sampleArray), target);
								adjustedIndex = range.start + sortedIndex;

								if(sortedIndex === 0 && range.start !== 0){
									end = i - 1;
								}else if(sortedIndex >= (sampleArray.length - 1) && adjustedIndex < resultsArray.length){
									begin = i + 1;
								}else{
									insertedInto = adjustedIndex;
									insertionRangeIndex = i;
								}
							}
						}
					}else{
						// we don't have a queryEngine, so we can't provide any information
						// about where it was inserted or moved to. If it is an update, we leave it's position alone. other we at least indicate a new object

						if(type === "update"){
							insertedInto = removedFrom;
							insertionRangeIndex = removalRangeIndex;
						}else{
							// TODO: Should there be a default index for a new object of undetermined index?
							//		It seems like sending "add" notification with no index might be more appropriate.
							//		On the other hand, when adding to an unsorted list, I would expect the new element to be appended.
							// a new object
							insertedInto = store.defaultIndex || 0;

							var range;
							for(i = 0; insertionRangeIndex === -1 && i < ranges.length; ++i){
								range = ranges[i];
								if(range.start <= insertedInto && insertedInto < (range.start + range.count)){
									insertionRangeIndex = i;
								}
							}
						}
					}

					if(insertedInto > -1){
						event.index = insertedInto;
						resultsArray.splice(insertedInto, 0, target);

						// TODO: NOTE: This is broken for a non-zero store.defaultIndex because, when an insertion range is not found, this code assumes insertion at the beginning.
						if(insertionRangeIndex > -1){
							ranges[insertionRangeIndex].count++;
						}
						for(i = insertionRangeIndex + 1; i < ranges.length; ++i){
							ranges[i].start++;
						}
					}
				}

				// TODO: Eventually we will want to aggregate all the listener events
				// in an event turn, but we will wait until we have a reliable, performant queueing
				// mechanism for this (besides setTimeout)
				type = 'ontracked' + type;
				observed[type] && observed[type](event);
			});
		}

		return observed;
	},
	// a Comet driven store could directly call notify to notify observers when data has
	// changed on the backend
	// create a new instance
	notify: function(type, target){
	}
});
});
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { EDITOR_FACEBOOK_GROUP } from '../../constants/urls';
import { Link } from '../../routes';
import { showDialog, logout } from '../../redux/auth';

function AppHeader({ user, onLoginClick, onLogoutClick }) {
  return (

      <Link route="home">
真的假的
</Link>

        <Link route="home">文章</Link>
        <Link route="replies">回應</Link>

        編輯求助區

      {user
        ?

            <Link route="/replies?mine=1">

                {user.get('name')}

            </Link>
            <button type="button" onClick={onLogoutClick}>Logout</button>

        : <button type="button" onClick={onLoginClick}>Login</button>}

  );
}

function mapStateToProps({ auth }) {
  return {
    user: auth.get('user'),
  };
}

function mapDispatchToProps(dispatch) {
  return {
    onLoginClick() {
      dispatch(showDialog());
    },
    onLogoutClick() {
      dispatch(logout());
    },
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(AppHeader);
<commit_msg>Add editor’s links to AppHeader<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { EDITOR_FACEBOOK_GROUP, EDITOR_REFERENCE } from '../../constants/urls';
import { Link } from '../../routes';
import { showDialog, logout } from '../../redux/auth';

function AppHeader({ user, onLoginClick, onLogoutClick }) {
  return (

      <Link route="home">
真的假的
</Link>

        <Link route="home">文章</Link>
        <Link route="replies">回應</Link>

          FB 編輯求助區

          編輯手冊

      {user
        ?

            <Link route="/replies?mine=1">

                {user.get('name')}

            </Link>
            <button type="button" onClick={onLogoutClick}>Logout</button>

        : <button type="button" onClick={onLoginClick}>Login</button>}

  );
}

function mapStateToProps({ auth }) {
  return {
    user: auth.get('user'),
  };
}

function mapDispatchToProps(dispatch) {
  return {
    onLoginClick() {
      dispatch(showDialog());
    },
    onLogoutClick() {
      dispatch(logout());
    },
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(AppHeader);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Text, View, Image, TextInput } from 'react-native';
import styles from '../styles.ios.js';
import helpers from '../helpers/helpers.js';
import FoodpairResults from './FoodpairResults.ios.js';

export default class Searchbar extends Component {
  constructor(props) {
    super(props);
    this.state = { text: '' }
  }

  searchFoodPairing() {
    this.props.rendering();
    helpers.foodpairing.getFoodpairings(this.state.text)
      .then(resp => {
        this.props.rendering();
        this.props.navigator.push({
          component: FoodpairResults,
          passProps: { foodpairs: resp.data, food: this.state.text }
        })
      })
      .catch(err => {
        console.log('error', err);
      })
  }

  render() {
    return (
      <View >
        <TextInput
          onSubmitEditing={this.searchFoodPairing.bind(this)}
          style={styles.searchBar}
          onChangeText={(text) => this.setState({text})}
          value={this.state.text}
          placeholder={'search your ingredients!'}
        />
      </View>
    )
  }
}

<commit_msg>Bug fix: put action in search bar to successfully call after 10 seconds of not rendering<commit_after>import React, { Component } from 'react';
import { Text, View, Image, TextInput, Alert } from 'react-native';
import styles from '../styles.ios.js';
import helpers from '../helpers/helpers.js';
import FoodpairResults from './FoodpairResults.ios.js';

export default class Searchbar extends Component {
  constructor(props) {
    super(props);
    this.state = { text: '' }
  }

  searchFoodPairing() {
    this.props.rendering();
    setTimeout(() => {
      if (this.props.isRendering) {
        Alert.alert('Not a valid ingredient');
      }
      this.props.lagOut();
    }, 10000)
    helpers.foodpairing.getFoodpairings(this.state.text)
      .then(resp => {
        this.props.rendering();
        this.props.navigator.push({
          component: FoodpairResults,
          passProps: { foodpairs: resp.data, food: this.state.text }
        })
      })
      .catch(err => {
        console.log('error', err);
      })
  }

  render() {
    return (
      <View >
        <TextInput
          onSubmitEditing={this.searchFoodPairing.bind(this)}
          style={styles.searchBar}
          onChangeText={(text) => this.setState({text})}
          value={this.state.text}
          placeholder={'search your ingredients!'}
        />
      </View>
    )
  }
}

<|endoftext|>