<commit_before>// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
//
// MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

wn.utils.full_name = function(fn, ln) {
	return fn + (ln ? ' ' : '') + (ln ? ln : '')
}

function fmt_money(v, format){
	return format_number(v, format);
}

function format_currency(v, currency) {
	return get_currency_symbol(currency) + " " + format_number(v);
}

function get_currency_symbol(currency) {
	if(!currency)
		currency = wn.boot.sysdefaults.currency;

	if(locals.Currency[currency])
		return locals.Currency[currency].symbol || currency;
	else
		return currency;
}

var global_number_format = null;
function get_number_format() {
	if(!global_number_format) {
		var default_format = "#,###.##";
		if(wn.boot.sysdefaults.number_format) {
			format = wn.boot.sysdefaults.number_format;
		} else if(!wn.boot.sysdefaults.currency) {
			format = default_format
		} else {
			format = locals["Currency"][wn.boot.sysdefaults.currency].number_format;
		}
		if(!format || format=="####" || format=="######") { // no flat formats!
			format = default_format
		}
		global_number_format = format;
	}
	return global_number_format;
}

var global_number_format_info = null
function get_number_format_info(format) {
	if(!global_number_format_info) {
		if(!format)
			format = get_number_format();
		var result = format.match(/[^\d\-\+#]/g);
		var info = {
			decimal_str: (result && result[result.length-1]) || '.', //treat the right most symbol as decimal
			group_sep: (result && result[1] && result[0]) || ',' //treat the left most symbol as group separator
		}
		info.precision = format.split(info.decimal_str)[1].length;
		global_number_format_info = info;
	}
	return global_number_format_info
}

// to title case
function toTitle(str){
	var word_in = str.split(" ");
	var word_out = [];

	for(w in word_in){
		word_out[w] = word_in[w].charAt(0).toUpperCase() + word_in[w].slice(1);
	}

	return word_out.join(" ");
}

function is_null(v) {
	if(v==null) {
		return 1
	} else if(v==0) {
		if((v+'').length>=1) return 0;
		else return 1;
	} else {
		return 0
	}
}

function set_value_in(ele, v, ftype, fopt) {
	$(ele).html(wn.form.get_formatter(ftype)(v, {options:fopt}));
	return;
}

function copy_dict(d) {
	var n = {};
	for(var k in d) n[k] = d[k];
	return n;
}

function replace_newlines(t) {
	return t?t.replace(/\n/g, '
'):'';
}

function cint(v, def) {
	v=v+'';
	v=lstrip(v, ['0']);
	v=parseInt(v);
	if(isNaN(v))v=def?def:0; return v;
}
function validate_email(txt) {
	return wn.utils.validate_type(txt, email);
}
function validate_spl_chars(txt) {
	return wn.utils.validate_type(txt, "alphanum")
}
function cstr(s) {
	if(s==null)return '';
	return s+'';
}
function nth(number) {
	number = cint(number);
	var s = 'th';
	if((number+'').substr(-1)=='1') s = 'st';
	if((number+'').substr(-1)=='2') s = 'nd';
	if((number+'').substr(-1)=='3') s = 'rd';
	return number+s;
}

function flt(v, decimals) {
	if(v==null || v=='')return 0;

	if(typeof v==="number")
		return v;

	v = v + "";

	// strip currency symbol if exists
	if(v.indexOf(" ")!=-1) {
		v = v.split(" ")[1];
	}

	// strip groups (,)
	var info = get_number_format_info();
	if(info.group_sep==".") {
		v = v.replace(/\./g,'');

		// sanitize decimal separator to .
		v = v.replace(/,/g, ".");
	} else {
		v=v.replace(/,/g,'');
	}

	v=parseFloat(v);
	if(isNaN(v))
		v=0;

	if(decimals!=null)
		return roundNumber(v, decimals);
	return v;
}

function esc_quotes(s) {
	if(s==null)s='';
	return s.replace(/'/, "\'");
}

var crop = function(s, len) {
	if(s.length>len)
		return s.substr(0, len-3) + '...';
	else
		return s;
}

var strip = function(s, chars) {
	var s= lstrip(s, chars)
	s = rstrip(s, chars);
	return s;
}

var lstrip = function(s, chars) {
	if(!chars) chars = ['\n', '\t', ' '];
	// strip left
	var first_char = s.substr(0,1);
	while(in_list(chars, first_char)) {
		var s = s.substr(1);
		first_char = s.substr(0,1);
	}
	return s;
}

var rstrip = function(s, chars) {
	if(!chars) chars = ['\n', '\t', ' '];
	var last_char = s.substr(s.length-1);
	while(in_list(chars, last_char)) {
		var s = s.substr(0, s.length-1);
		last_char = s.substr(s.length-1);
	}
	return s;
}

function repl(s, dict) {
	if(s==null)return '';
	for(key in dict) {
		s = s.split("%("+key+")s").join(dict[key]);
	}
	return s;
}

function replace_all(s, t1, t2) {
	return s.split(t1).join(t2);
}

function keys(obj) {
	var mykeys=[];
	for (key in obj) mykeys[mykeys.length]=key;
	return mykeys;
}
function values(obj) {
	var myvalues=[];
	for (key in obj) myvalues[myvalues.length]=obj[key];
	return myvalues;
}

function in_list(list, item) {
	if(!list) return false;
	for(var i=0; i<list.length; i++)
		if(list[i]==item) return true;
	return false;
}
function has_common(list1, list2) {
	if(!list1 || !list2) return false;
	for(var i=0; i<list1.length; i++) {
		if(in_list(list2, list1[i]))return true;
	}
	return false;
}

var inList = in_list; // bc
function add_lists(l1, l2) {
	return [].concat(l1).concat(l2);
}

function docstring(obj)  {
	return JSON.stringify(obj);
}

function roundNumber(num, dec) {
	var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
	return result;
}
<commit_msg>brought back<commit_after>// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
//
// MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

wn.utils.full_name = function(fn, ln) {
	return fn + (ln ? ' ' : '') + (ln ? ln : '')
}

function fmt_money(v, format){
	return format_number(v, format);
}

function format_currency(v, currency) {
	return get_currency_symbol(currency) + " " + format_number(v);
}

function get_currency_symbol(currency) {
	if(!currency)
		currency = wn.boot.sysdefaults.currency;

	if(locals.Currency[currency])
		return locals.Currency[currency].symbol || currency;
	else
		return currency;
}

var global_number_format = null;
function get_number_format() {
	if(!global_number_format) {
		var default_format = "#,###.##";
		if(wn.boot.sysdefaults.number_format) {
			format = wn.boot.sysdefaults.number_format;
		} else if(!wn.boot.sysdefaults.currency) {
			format = default_format
		} else {
			format = locals["Currency"][wn.boot.sysdefaults.currency].number_format;
		}
		if(!format || format=="####" || format=="######") { // no flat formats!
			format = default_format
		}
		global_number_format = format;
	}
	return global_number_format;
}

var global_number_format_info = null
function get_number_format_info(format) {
	if(!global_number_format_info) {
		if(!format)
			format = get_number_format();
		var result = format.match(/[^\d\-\+#]/g);
		var info = {
			decimal_str: (result && result[result.length-1]) || '.', //treat the right most symbol as decimal
			group_sep: (result && result[1] && result[0]) || ',' //treat the left most symbol as group separator
		}
		info.precision = format.split(info.decimal_str)[1].length;
		global_number_format_info = info;
	}
	return global_number_format_info
}

// to title case
function toTitle(str){
	var word_in = str.split(" ");
	var word_out = [];

	for(w in word_in){
		word_out[w] = word_in[w].charAt(0).toUpperCase() + word_in[w].slice(1);
	}

	return word_out.join(" ");
}

function is_null(v) {
	if(v==null) {
		return 1
	} else if(v==0) {
		if((v+'').length>=1) return 0;
		else return 1;
	} else {
		return 0
	}
}

function set_value_in(ele, v, ftype, fopt) {
	$(ele).html(wn.form.get_formatter(ftype)(v, {options:fopt}));
	return;
}
var $s = set_value_in; // used in print formats

function copy_dict(d) {
	var n = {};
	for(var k in d) n[k] = d[k];
	return n;
}

function replace_newlines(t) {
	return t?t.replace(/\n/g, '
'):'';
}

function cint(v, def) {
	v=v+'';
	v=lstrip(v, ['0']);
	v=parseInt(v);
	if(isNaN(v))v=def?def:0; return v;
}
function validate_email(txt) {
	return wn.utils.validate_type(txt, email);
}
function validate_spl_chars(txt) {
	return wn.utils.validate_type(txt, "alphanum")
}
function cstr(s) {
	if(s==null)return '';
	return s+'';
}
function nth(number) {
	number = cint(number);
	var s = 'th';
	if((number+'').substr(-1)=='1') s = 'st';
	if((number+'').substr(-1)=='2') s = 'nd';
	if((number+'').substr(-1)=='3') s = 'rd';
	return number+s;
}

function flt(v, decimals) {
	if(v==null || v=='')return 0;

	if(typeof v==="number")
		return v;

	v = v + "";

	// strip currency symbol if exists
	if(v.indexOf(" ")!=-1) {
		v = v.split(" ")[1];
	}

	// strip groups (,)
	var info = get_number_format_info();
	if(info.group_sep==".") {
		v = v.replace(/\./g,'');

		// sanitize decimal separator to .
		v = v.replace(/,/g, ".");
	} else {
		v=v.replace(/,/g,'');
	}

	v=parseFloat(v);
	if(isNaN(v))
		v=0;

	if(decimals!=null)
		return roundNumber(v, decimals);
	return v;
}

function esc_quotes(s) {
	if(s==null)s='';
	return s.replace(/'/, "\'");
}

var crop = function(s, len) {
	if(s.length>len)
		return s.substr(0, len-3) + '...';
	else
		return s;
}

var strip = function(s, chars) {
	var s= lstrip(s, chars)
	s = rstrip(s, chars);
	return s;
}

var lstrip = function(s, chars) {
	if(!chars) chars = ['\n', '\t', ' '];
	// strip left
	var first_char = s.substr(0,1);
	while(in_list(chars, first_char)) {
		var s = s.substr(1);
		first_char = s.substr(0,1);
	}
	return s;
}

var rstrip = function(s, chars) {
	if(!chars) chars = ['\n', '\t', ' '];
	var last_char = s.substr(s.length-1);
	while(in_list(chars, last_char)) {
		var s = s.substr(0, s.length-1);
		last_char = s.substr(s.length-1);
	}
	return s;
}

function repl(s, dict) {
	if(s==null)return '';
	for(key in dict) {
		s = s.split("%("+key+")s").join(dict[key]);
	}
	return s;
}

function replace_all(s, t1, t2) {
	return s.split(t1).join(t2);
}

function keys(obj) {
	var mykeys=[];
	for (key in obj) mykeys[mykeys.length]=key;
	return mykeys;
}
function values(obj) {
	var myvalues=[];
	for (key in obj) myvalues[myvalues.length]=obj[key];
	return myvalues;
}

function in_list(list, item) {
	if(!list) return false;
	for(var i=0; i<list.length; i++)
		if(list[i]==item) return true;
	return false;
}
function has_common(list1, list2) {
	if(!list1 || !list2) return false;
	for(var i=0; i<list1.length; i++) {
		if(in_list(list2, list1[i]))return true;
	}
	return false;
}

var inList = in_list; // bc
function add_lists(l1, l2) {
	return [].concat(l1).concat(l2);
}

function docstring(obj)  {
	return JSON.stringify(obj);
}

function roundNumber(num, dec) {
	var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
	return result;
}
<|endoftext|>


<commit_before>$(document).on('cmsify:load', function() {
  // TODO: rename this, it isn't searching, it is searching a form
  $(".js-filter-search").each(function(e) {
    $(this).on('keyup', function(e) {
      if (e.keyCode === 13) {
        $(this).closest('form').submit();
      }
    });
  });

  $(".js-filter-input").each(function(e) {
    $(this).on('change', function(e) {
      $(this).closest('form').submit();
    });
  });

  $('[data-filter-search]').each(function() {
    var $this = $(this);
    var filterTarget = $this.data('filter-search');
    $(this).on('input', function(e) {
      if (this.value.length) {
        $(filterTarget).children().addClass('uk-hidden');
        $(filterTarget).children(':contains('+ $this.val() +')').removeClass('uk-hidden');
      } else {
        $(filterTarget).children().removeClass('uk-hidden');
      }
    });
  });
});
<commit_msg>Overwrite jquery contains method to not care about case sensitivity.<commit_after>$(document).on('cmsify:load', function() {
  // TODO: rename this, it isn't searching, it is searching a form
  $(".js-filter-search").each(function(e) {
    $(this).on('keyup', function(e) {
      if (e.keyCode === 13) {
        $(this).closest('form').submit();
      }
    });
  });

  $(".js-filter-input").each(function(e) {
    $(this).on('change', function(e) {
      $(this).closest('form').submit();
    });
  });
  // Overwrite the contains method to not be case sensitive
  // NEW selector
  jQuery.expr[':'].Contains = function(a, i, m) {
    return jQuery(a).text().toUpperCase()
        .indexOf(m[3].toUpperCase()) >= 0;
  };

  // OVERWRITES old selecor
  jQuery.expr[':'].contains = function(a, i, m) {
    return jQuery(a).text().toUpperCase()
        .indexOf(m[3].toUpperCase()) >= 0;
  };

  $('[data-filter-search]').each(function() {
    var $this = $(this);
    var filterTarget = $this.data('filter-search');
    $(this).on('input', function(e) {
      if (this.value.length) {
        $(filterTarget).children().addClass('uk-hidden');
        $(filterTarget).children(':contains('+ $this.val() +')').removeClass('uk-hidden');
      } else {
        $(filterTarget).children().removeClass('uk-hidden');
      }
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

import React, {
  PropTypes,
} from 'react';

import {
  StyleSheet,
  PanResponder,
  View,
  TouchableHighlight,
  Platform,
} from 'react-native';

import DefaultMarker from './DefaultMarker';
import { createArray, valueToPosition, positionToValue } from './converters';

export default class MultiSlider extends React.Component {
    static propTypes = {
      values: PropTypes.arrayOf(PropTypes.number),

      onValuesChangeStart: PropTypes.func,
      onValuesChange: PropTypes.func,
      onValuesChangeFinish: PropTypes.func,

      sliderLength: PropTypes.number,
      sliderOrientation: PropTypes.string,
      touchDimensions: PropTypes.object,

      customMarker: PropTypes.func,

      min: PropTypes.number,
      max: PropTypes.number,
      step: PropTypes.number,

      optionsArray: PropTypes.array,

      containerStyle: View.propTypes.style,
      trackStyle: View.propTypes.style,
      selectedStyle: View.propTypes.style,
      unselectedStyle: View.propTypes.style,
      markerStyle: View.propTypes.style,
      pressedMarkerStyle: View.propTypes.style,
    };

    static defaultProps = {
      values: [0],
      onValuesChangeStart: () => {
        console.log('press started');
      },
      onValuesChange: (values) => {
        console.log('changing', values);
      },
      onValuesChangeFinish: (values) => {
        console.log('changed', values);
      },
      step: 1,
      min: 0,
      max: 10,
      touchDimensions: {
        height: 50,
        width: 50,
        borderRadius: 15,
        slipDisplacement: 200,
      },
      customMarker: DefaultMarker,
      sliderLength: 280,
    };

    constructor(props) {
      super(props);

      this.optionsArray = this.props.optionsArray || createArray(this.props.min, this.props.max, this.props.step);
      this.stepLength = this.props.sliderLength / this.optionsArray.length;

      var initialValues = this.props.values.map(value => valueToPosition(value, this.optionsArray, this.props.sliderLength));

      this.state = {
        pressedOne: true,
        valueOne: this.props.values[0],
        valueTwo: this.props.values[1],
        pastOne: initialValues[0],
        pastTwo: initialValues[1],
        positionOne: initialValues[0],
        positionTwo: initialValues[1],
      };
    }

    componentWillMount() {
      var customPanResponder = (start, move, end) => {
        return PanResponder.create({
          onStartShouldSetPanResponder: (evt, gestureState) => true,
          onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
          onMoveShouldSetPanResponder: (evt, gestureState) => true,
          onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
          onPanResponderGrant: (evt, gestureState) => start(),
          onPanResponderMove: (evt, gestureState) => move(gestureState),
          onPanResponderTerminationRequest: (evt, gestureState) => true,
          onPanResponderRelease: (evt, gestureState) => end(gestureState),
          onPanResponderTerminate: (evt, gestureState) => end(gestureState),
          onShouldBlockNativeResponder: (evt, gestureState) => true
        })
      };

      this._panResponderOne = customPanResponder(this.startOne, this.moveOne, this.endOne);
      this._panResponderTwo = customPanResponder(this.startTwo, this.moveTwo, this.endTwo);
    }

    startOne = () => {
      this.props.onValuesChangeStart();
      this.setState({
        onePressed: !this.state.onePressed
      });
    }

    startTwo = () => {
      this.props.onValuesChangeStart();
      this.setState({
        twoPressed: !this.state.twoPressed
      });
    }

    moveOne = (gestureState) => {
      var unconfined = gestureState.dx + this.state.pastOne;
      var bottom     = 0;
      var top        = (this.state.positionTwo - this.stepLength) || this.props.sliderLength;
      var confined   = unconfined < bottom ? bottom : (unconfined > top ? top : unconfined);
      var value      = positionToValue(this.state.positionOne, this.optionsArray, this.props.sliderLength);

      var slipDisplacement = this.props.touchDimensions.slipDisplacement;

      if (Math.abs(gestureState.dy) < slipDisplacement || !slipDisplacement) {
        this.setState({
          positionOne: confined,
        });
      }

      if (value !== this.state.valueOne) {
        this.setState({
          valueOne: value
        }, () => {
          var change = [this.state.valueOne];
          if (this.state.valueTwo) {
            change.push(this.state.valueTwo);
          }
          this.props.onValuesChange(change);
        });
      }
    }

    moveTwo = (gestureState) => {
      var unconfined  = gestureState.dx + this.state.pastTwo;
      var bottom      = this.state.positionOne + this.stepLength;
      var top         = this.props.sliderLength;
      var confined    = unconfined < bottom ? bottom : (unconfined > top ? top : unconfined);
      var value       = positionToValue(this.state.positionTwo, this.optionsArray, this.props.sliderLength);
      var slipDisplacement = this.props.touchDimensions.slipDisplacement;

      if (Math.abs(gestureState.dy) < slipDisplacement || !slipDisplacement) {
        this.setState({
          positionTwo: confined,
        });
      }
      if ( value !== this.state.valueTwo ) {
        this.setState({
          valueTwo: value,
        }, () => {
          this.props.onValuesChange([this.state.valueOne, this.state.valueTwo]);
        });
      }
    }

    endOne = (gestureState) => {
      this.setState({
        pastOne: this.state.positionOne,
        onePressed: !this.state.onePressed
      }, () => {
        var change = [this.state.valueOne];
        if (this.state.valueTwo) {
          change.push(this.state.valueTwo);
        }
        this.props.onValuesChangeFinish(change);
      });
    }

    endTwo = (gestureState) => {
      this.setState({
        twoPressed: !this.state.twoPressed,
        pastTwo: this.state.positionTwo,
      }, () => {
        this.props.onValuesChangeFinish([this.state.valueOne, this.state.valueTwo]);
      });
    }

    render() {

      const { positionOne, positionTwo } = this.state;
      const { selectedStyle, unselectedStyle, sliderLength } = this.props;
      const twoMarkers = positionTwo;

      const trackOneLength = positionOne;
      const trackOneStyle = twoMarkers ? unselectedStyle : selectedStyle || styles.selectedTrack;
      const trackThreeLength = twoMarkers ? sliderLength - (positionTwo) : 0;
      const trackThreeStyle = unselectedStyle;
      const trackTwoLength = sliderLength - trackOneLength - trackThreeLength;
      const trackTwoStyle = twoMarkers ? selectedStyle || styles.selectedTrack : unselectedStyle;
      const Marker = this.props.customMarker;
      const { slipDisplacement, height, width, borderRadius } = this.props.touchDimensions;
      const touchStyle = {
        borderRadius: borderRadius || 0
      };

      const markerContainerOne = { top: -24, left : trackOneLength - 24 };

      const markerContainerTwo = { top: -24, right: trackThreeLength - 24 };

      return (
        <View style={[styles.container, this.props.containerStyle]}>
          <View style={[styles.fullTrack, { width: sliderLength }]}>
            <View style={[styles.track, this.props.trackStyle, trackOneStyle, { width: trackOneLength } ]} />
            <View style={[styles.track, this.props.trackStyle, trackTwoStyle, { width: trackTwoLength } ]} />
            {twoMarkers && (
              <View style={[styles.track, this.props.trackStyle, trackThreeStyle, { width: trackThreeLength } ]} />
            )}
            <View style={[styles.markerContainer, markerContainerOne]}>
              <View
                style={[styles.touch, touchStyle]}
                ref={component => this._markerOne = component}
                {...this._panResponderOne.panHandlers}
              >
                <Marker
                  pressed={this.state.onePressed}
                  markerStyle={[styles.marker, this.props.markerStyle]}
                  pressedMarkerStyle={this.props.pressedMarkerStyle}
                />
              </View>
            </View>
            {twoMarkers && (positionOne !== this.props.sliderLength) && (
              <View style={[styles.markerContainer, markerContainerTwo]}>
                <View
                  style={[styles.touch, touchStyle]}
                  ref={component => this._markerTwo = component}
                  {...this._panResponderTwo.panHandlers}
                >
                  <Marker
                    pressed={this.state.twoPressed}
                    markerStyle={this.props.markerStyle}
                    pressedMarkerStyle={this.props.pressedMarkerStyle}
                  />
                </View>
              </View>
            )}
          </View>
        </View>
      );
    }
}

const styles = StyleSheet.create({
  container: {
    position: 'relative',
    height: 50,
  },
  fullTrack: {
    flexDirection: 'row',
  },
  track: {
    ...Platform.select({
      ios: {
        height: 2,
        borderRadius: 2,
        backgroundColor: '#A7A7A7',
      },
      android: {
        height: 2,
        backgroundColor: '#CECECE',
      }
    }),
  },
  selectedTrack: {
    ...Platform.select({
      ios: {
        backgroundColor: '#095FFF',
      },
      android: {
        backgroundColor: '#0D8675',
      },
    }),
  },
  markerContainer: {
    position: 'absolute',
    width: 48,
    height: 48,
    backgroundColor: 'transparent',
    justifyContent: 'center',
    alignItems: 'center',
  },
  touch: {
    backgroundColor: 'transparent',
    justifyContent: 'center',
    alignItems: 'center',
    alignSelf: 'stretch',
  },
});
<commit_msg>Removed sliderOrientation<commit_after>'use strict';

import React, {
  PropTypes,
} from 'react';

import {
  StyleSheet,
  PanResponder,
  View,
  TouchableHighlight,
  Platform,
} from 'react-native';

import DefaultMarker from './DefaultMarker';
import { createArray, valueToPosition, positionToValue } from './converters';

export default class MultiSlider extends React.Component {
    static propTypes = {
      values: PropTypes.arrayOf(PropTypes.number),

      onValuesChangeStart: PropTypes.func,
      onValuesChange: PropTypes.func,
      onValuesChangeFinish: PropTypes.func,

      sliderLength: PropTypes.number,
      touchDimensions: PropTypes.object,

      customMarker: PropTypes.func,

      min: PropTypes.number,
      max: PropTypes.number,
      step: PropTypes.number,

      optionsArray: PropTypes.array,

      containerStyle: View.propTypes.style,
      trackStyle: View.propTypes.style,
      selectedStyle: View.propTypes.style,
      unselectedStyle: View.propTypes.style,
      markerStyle: View.propTypes.style,
      pressedMarkerStyle: View.propTypes.style,
    };

    static defaultProps = {
      values: [0],
      onValuesChangeStart: () => {
        console.log('press started');
      },
      onValuesChange: (values) => {
        console.log('changing', values);
      },
      onValuesChangeFinish: (values) => {
        console.log('changed', values);
      },
      step: 1,
      min: 0,
      max: 10,
      touchDimensions: {
        height: 50,
        width: 50,
        borderRadius: 15,
        slipDisplacement: 200,
      },
      customMarker: DefaultMarker,
      sliderLength: 280,
    };

    constructor(props) {
      super(props);

      this.optionsArray = this.props.optionsArray || createArray(this.props.min, this.props.max, this.props.step);
      this.stepLength = this.props.sliderLength / this.optionsArray.length;

      var initialValues = this.props.values.map(value => valueToPosition(value, this.optionsArray, this.props.sliderLength));

      this.state = {
        pressedOne: true,
        valueOne: this.props.values[0],
        valueTwo: this.props.values[1],
        pastOne: initialValues[0],
        pastTwo: initialValues[1],
        positionOne: initialValues[0],
        positionTwo: initialValues[1],
      };
    }

    componentWillMount() {
      var customPanResponder = (start, move, end) => {
        return PanResponder.create({
          onStartShouldSetPanResponder: (evt, gestureState) => true,
          onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
          onMoveShouldSetPanResponder: (evt, gestureState) => true,
          onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
          onPanResponderGrant: (evt, gestureState) => start(),
          onPanResponderMove: (evt, gestureState) => move(gestureState),
          onPanResponderTerminationRequest: (evt, gestureState) => true,
          onPanResponderRelease: (evt, gestureState) => end(gestureState),
          onPanResponderTerminate: (evt, gestureState) => end(gestureState),
          onShouldBlockNativeResponder: (evt, gestureState) => true
        })
      };

      this._panResponderOne = customPanResponder(this.startOne, this.moveOne, this.endOne);
      this._panResponderTwo = customPanResponder(this.startTwo, this.moveTwo, this.endTwo);
    }

    startOne = () => {
      this.props.onValuesChangeStart();
      this.setState({
        onePressed: !this.state.onePressed
      });
    }

    startTwo = () => {
      this.props.onValuesChangeStart();
      this.setState({
        twoPressed: !this.state.twoPressed
      });
    }

    moveOne = (gestureState) => {
      var unconfined = gestureState.dx + this.state.pastOne;
      var bottom     = 0;
      var top        = (this.state.positionTwo - this.stepLength) || this.props.sliderLength;
      var confined   = unconfined < bottom ? bottom : (unconfined > top ? top : unconfined);
      var value      = positionToValue(this.state.positionOne, this.optionsArray, this.props.sliderLength);

      var slipDisplacement = this.props.touchDimensions.slipDisplacement;

      if (Math.abs(gestureState.dy) < slipDisplacement || !slipDisplacement) {
        this.setState({
          positionOne: confined,
        });
      }

      if (value !== this.state.valueOne) {
        this.setState({
          valueOne: value
        }, () => {
          var change = [this.state.valueOne];
          if (this.state.valueTwo) {
            change.push(this.state.valueTwo);
          }
          this.props.onValuesChange(change);
        });
      }
    }

    moveTwo = (gestureState) => {
      var unconfined  = gestureState.dx + this.state.pastTwo;
      var bottom      = this.state.positionOne + this.stepLength;
      var top         = this.props.sliderLength;
      var confined    = unconfined < bottom ? bottom : (unconfined > top ? top : unconfined);
      var value       = positionToValue(this.state.positionTwo, this.optionsArray, this.props.sliderLength);
      var slipDisplacement = this.props.touchDimensions.slipDisplacement;

      if (Math.abs(gestureState.dy) < slipDisplacement || !slipDisplacement) {
        this.setState({
          positionTwo: confined,
        });
      }
      if ( value !== this.state.valueTwo ) {
        this.setState({
          valueTwo: value,
        }, () => {
          this.props.onValuesChange([this.state.valueOne, this.state.valueTwo]);
        });
      }
    }

    endOne = (gestureState) => {
      this.setState({
        pastOne: this.state.positionOne,
        onePressed: !this.state.onePressed
      }, () => {
        var change = [this.state.valueOne];
        if (this.state.valueTwo) {
          change.push(this.state.valueTwo);
        }
        this.props.onValuesChangeFinish(change);
      });
    }

    endTwo = (gestureState) => {
      this.setState({
        twoPressed: !this.state.twoPressed,
        pastTwo: this.state.positionTwo,
      }, () => {
        this.props.onValuesChangeFinish([this.state.valueOne, this.state.valueTwo]);
      });
    }

    render() {

      const { positionOne, positionTwo } = this.state;
      const { selectedStyle, unselectedStyle, sliderLength } = this.props;
      const twoMarkers = positionTwo;

      const trackOneLength = positionOne;
      const trackOneStyle = twoMarkers ? unselectedStyle : selectedStyle || styles.selectedTrack;
      const trackThreeLength = twoMarkers ? sliderLength - (positionTwo) : 0;
      const trackThreeStyle = unselectedStyle;
      const trackTwoLength = sliderLength - trackOneLength - trackThreeLength;
      const trackTwoStyle = twoMarkers ? selectedStyle || styles.selectedTrack : unselectedStyle;
      const Marker = this.props.customMarker;
      const { slipDisplacement, height, width, borderRadius } = this.props.touchDimensions;
      const touchStyle = {
        borderRadius: borderRadius || 0
      };

      const markerContainerOne = { top: -24, left : trackOneLength - 24 };

      const markerContainerTwo = { top: -24, right: trackThreeLength - 24 };

      return (
        <View style={[styles.container, this.props.containerStyle]}>
          <View style={[styles.fullTrack, { width: sliderLength }]}>
            <View style={[styles.track, this.props.trackStyle, trackOneStyle, { width: trackOneLength } ]} />
            <View style={[styles.track, this.props.trackStyle, trackTwoStyle, { width: trackTwoLength } ]} />
            {twoMarkers && (
              <View style={[styles.track, this.props.trackStyle, trackThreeStyle, { width: trackThreeLength } ]} />
            )}
            <View style={[styles.markerContainer, markerContainerOne]}>
              <View
                style={[styles.touch, touchStyle]}
                ref={component => this._markerOne = component}
                {...this._panResponderOne.panHandlers}
              >
                <Marker
                  pressed={this.state.onePressed}
                  markerStyle={[styles.marker, this.props.markerStyle]}
                  pressedMarkerStyle={this.props.pressedMarkerStyle}
                />
              </View>
            </View>
            {twoMarkers && (positionOne !== this.props.sliderLength) && (
              <View style={[styles.markerContainer, markerContainerTwo]}>
                <View
                  style={[styles.touch, touchStyle]}
                  ref={component => this._markerTwo = component}
                  {...this._panResponderTwo.panHandlers}
                >
                  <Marker
                    pressed={this.state.twoPressed}
                    markerStyle={this.props.markerStyle}
                    pressedMarkerStyle={this.props.pressedMarkerStyle}
                  />
                </View>
              </View>
            )}
          </View>
        </View>
      );
    }
}

const styles = StyleSheet.create({
  container: {
    position: 'relative',
    height: 50,
  },
  fullTrack: {
    flexDirection: 'row',
  },
  track: {
    ...Platform.select({
      ios: {
        height: 2,
        borderRadius: 2,
        backgroundColor: '#A7A7A7',
      },
      android: {
        height: 2,
        backgroundColor: '#CECECE',
      }
    }),
  },
  selectedTrack: {
    ...Platform.select({
      ios: {
        backgroundColor: '#095FFF',
      },
      android: {
        backgroundColor: '#0D8675',
      },
    }),
  },
  markerContainer: {
    position: 'absolute',
    width: 48,
    height: 48,
    backgroundColor: 'transparent',
    justifyContent: 'center',
    alignItems: 'center',
  },
  touch: {
    backgroundColor: 'transparent',
    justifyContent: 'center',
    alignItems: 'center',
    alignSelf: 'stretch',
  },
});
<|endoftext|>


<commit_before>({
    // a naming exclusion checkbox has been modified, notify our container
    onCheck : function(component, event, helper) {
        helper.fireContactChangedEvent(component, event);
    },

    // the remove button has been pressed, notify our container
    removeContact : function(component, event, helper) {
        helper.fireContactRemoveEvent(component, event);
    },
})<commit_msg>Preventing default button behavior<commit_after>({
    // a naming exclusion checkbox has been modified, notify our container
    onCheck : function(component, event, helper) {
        helper.fireContactChangedEvent(component, event);
    },

    // the remove button has been pressed, notify our container
    removeContact : function(component, event, helper) {
    	event.preventDefault();
        helper.fireContactRemoveEvent(component, event);
    },
})<|endoftext|>


<commit_before>"use strict";

AjabShahar = AjabShahar || {};
AjabShahar.SongExploreHelper = (function(){
    var self = {};

    self.createWordThumbnails = function(words){
        var wordThumbnails = [];
        if(!_.isEmpty(words)){
            words.forEach(function(word){
                if(word.publish && word.rootWord){
                    wordThumbnails.push(new AjabShahar.ThumbnailObject(word,"word"));
                }
            });
        }
        return wordThumbnails;
    };
    self.createReflectionThumbnails = function(reflections){
        var reflectionThumbnails = [];
        if(!_.isEmpty(reflections)){
            reflections.forEach(function(reflection){
                if(reflection.published){
                    reflectionThumbnails.push(new AjabShahar.ThumbnailObject(reflection,"reflection"));
                }
            });
        }
        return reflectionThumbnails;
    };

    self.createSongThumbnails = function(songs){
        var songThumbnails = [];
        if(!_.isEmpty(songs)){
            songs.forEach(function(song){
                if(song.publish){
                    songThumbnails.push(new AjabShahar.ThumbnailObject(song,"song"));
                }
            });
        }
        return songThumbnails;
    };

    var containsItem = function(existingItems, newItem) {
        return _.some(existingItems, function (existingItem) {
            return existingItem ? newItem.id === existingItem.id : false;
        });
    };

    var getUnique = function(newItems,existingItems ){
        var uniqueItems= newItems || [];
        if(!_.isEmpty(existingItems) && !_.isEmpty(newItems)){
            uniqueItems = _.reject(newItems,function(newItem){
                return containsItem(existingItems, newItem)
            });
        }
        return uniqueItems;
    };

    self.getReflectionsFromWordReflections = function(wordReflections,existingReflections){
        function reflectionsWithoutTheDefault(reflectionsRelatedToWord, wordReflection) {
            return reflectionsRelatedToWord.concat(_.reject(wordReflection.reflections, function (reflection) {
                if (!_.isEmpty(wordReflection.defaultReflection)) {
                    return reflection.id === wordReflection.defaultReflection.id;
                }
                return false;
            }));
        }

        var reflections =_.reduce(wordReflections,function(result,wordReflection){
            var reflectionsRelatedToWord =[];
            reflectionsRelatedToWord = reflectionsWithoutTheDefault(reflectionsRelatedToWord, wordReflection);
            var uniqueReflections = getUnique(reflectionsRelatedToWord,result.concat(existingReflections));
            var reflectionThumbnails = _.sample(self.createReflectionThumbnails(uniqueReflections),3);
            if(!_.isEmpty(wordReflection.defaultReflection) && !containsItem(reflectionThumbnails,wordReflection.defaultReflection) ){
                reflectionThumbnails.unshift(wordReflection.defaultReflection)
            }
            result = result.concat(reflectionThumbnails);
            return result;
        },[]);
        return reflections;
    };

    self.getSongsFromWordReflections = function(wordSongs,existingSongs){
        var songs =_.reduce(wordSongs,function(result,wordSong){
            var uniqueSongs = getUnique(wordSong.songs,result.concat(existingSongs));
            var songThumbnails = _.sample(self.createSongThumbnails(uniqueSongs),3);
            result = result.concat(songThumbnails);
            return result;
        },[]);
        return songs;
    };

    return self;
})();<commit_msg>Fixed bug, on song explore page, displaying empty reflection<commit_after>"use strict";

AjabShahar = AjabShahar || {};
AjabShahar.SongExploreHelper = (function(){
    var self = {};

    self.createWordThumbnails = function(words){
        var wordThumbnails = [];
        if(!_.isEmpty(words)){
            words.forEach(function(word){
                if(word.publish && word.rootWord){
                    wordThumbnails.push(new AjabShahar.ThumbnailObject(word,"word"));
                }
            });
        }
        return wordThumbnails;
    };
    self.createReflectionThumbnails = function(reflections){
        var reflectionThumbnails = [];
        if(!_.isEmpty(reflections)){
            reflections.forEach(function(reflection){
                if(reflection.published){
                    reflectionThumbnails.push(new AjabShahar.ThumbnailObject(reflection,"reflection"));
                }
            });
        }
        return reflectionThumbnails;
    };

    self.createSongThumbnails = function(songs){
        var songThumbnails = [];
        if(!_.isEmpty(songs)){
            songs.forEach(function(song){
                if(song.publish){
                    songThumbnails.push(new AjabShahar.ThumbnailObject(song,"song"));
                }
            });
        }
        return songThumbnails;
    };

    var containsItem = function(existingItems, newItem) {
        return _.some(existingItems, function (existingItem) {
            return existingItem ? newItem.id === existingItem.id : false;
        });
    };

    var getUnique = function(newItems,existingItems ){
        var uniqueItems= newItems || [];
        if(!_.isEmpty(existingItems) && !_.isEmpty(newItems)){
            uniqueItems = _.reject(newItems,function(newItem){
                return containsItem(existingItems, newItem)
            });
        }
        return uniqueItems;
    };

    self.getReflectionsFromWordReflections = function(wordReflections,existingReflections){
        function reflectionsWithoutTheDefault(reflectionsRelatedToWord, wordReflection) {
            return reflectionsRelatedToWord.concat(_.reject(wordReflection.reflections, function (reflection) {
                if (!_.isEmpty(wordReflection.defaultReflection)) {
                    return reflection.id === wordReflection.defaultReflection.id;
                }
                return false;
            }));
        }

        var reflections =_.reduce(wordReflections,function(result,wordReflection){
            var reflectionsRelatedToWord =[];
            reflectionsRelatedToWord = reflectionsWithoutTheDefault(reflectionsRelatedToWord, wordReflection);
            var uniqueReflections = getUnique(reflectionsRelatedToWord,result.concat(existingReflections));
            var reflectionThumbnails = _.sample(self.createReflectionThumbnails(uniqueReflections),3);
            if(!_.isEmpty(wordReflection.defaultReflection) && !containsItem(reflectionThumbnails,wordReflection.defaultReflection) ){
                reflectionThumbnails.unshift(new AjabShahar.ThumbnailObject(wordReflection.defaultReflection,"reflection"));
            }
            result = result.concat(reflectionThumbnails);
            return result;
        },[]);
        return reflections;
    };

    self.getSongsFromWordReflections = function(wordSongs,existingSongs){
        var songs =_.reduce(wordSongs,function(result,wordSong){
            var uniqueSongs = getUnique(wordSong.songs,result.concat(existingSongs));
            var songThumbnails = _.sample(self.createSongThumbnails(uniqueSongs),3);
            result = result.concat(songThumbnails);
            return result;
        },[]);
        return songs;
    };

    return self;
})();<|endoftext|>


<commit_before>// https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#3qghes
import { translate } from '../../../../../common/i18n';
import config from '../../../../common/const';
import { expectValue } from '../shared';

Blockly.Blocks.macda = {
    init: function init() {
        this.appendDummyInput()
            .appendField(translate('MACD Array'))
            .appendField(new Blockly.FieldDropdown(config.macdFields), 'MACDFIELDS_LIST');
        this.appendValueInput('INPUT')
            .setCheck('Array')
            .appendField(translate('Input List'));
        this.appendValueInput('FAST_EMA_PERIOD')
            .setCheck('Number')
            .appendField(translate('Fast EMA Period'));
        this.appendValueInput('SLOW_EMA_PERIOD')
            .setCheck('Number')
            .appendField(translate('Slow EMA Period'));
        this.appendValueInput('SMA_PERIOD')
            .setCheck('Number')
            .appendField(translate('SMA Period (for Signal)'));
        this.setOutput(true, 'Array');
        this.setColour('#dedede');
        this.setTooltip(translate('Calculates Moving Average Convergence Divergence (MACD) list from a list'));
        this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki');
    },
};

Blockly.JavaScript.macda = block => {
    const macdField = block.getFieldValue('MACDFIELDS_LIST');
    const input = expectValue(block, 'INPUT');
    const fastEmaPeriod =
        Blockly.JavaScript.valueToCode(block, 'FAST_EMA_PERIOD', Blockly.JavaScript.ORDER_ATOMIC) || '12';
    const slowEmaPeriod =
        Blockly.JavaScript.valueToCode(block, 'SLOW_EMA_PERIOD', Blockly.JavaScript.ORDER_ATOMIC) || '26';
    const smaPeriod = Blockly.JavaScript.valueToCode(block, 'SMA_PERIOD', Blockly.JavaScript.ORDER_ATOMIC) || '9';
    return [
        `Bot.macda(${input}, {
    fastEmaPeriod: ${fastEmaPeriod},
    slowEmaPeriod: ${slowEmaPeriod},
    signalSmaPeriod: ${smaPeriod},
  }, ${macdField})`,
        Blockly.JavaScript.ORDER_NONE,
    ];
};
<commit_msg>Update Block-text<commit_after>// https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#3qghes
import { translate } from '../../../../../common/i18n';
import config from '../../../../common/const';
import { expectValue } from '../shared';

Blockly.Blocks.macda = {
    init: function init() {
        this.appendDummyInput()
            .appendField(translate('MACD Array'))
            .appendField(new Blockly.FieldDropdown(config.macdFields), 'MACDFIELDS_LIST');
        this.appendValueInput('INPUT')
            .setCheck('Array')
            .appendField(translate('Input List'));
        this.appendValueInput('FAST_EMA_PERIOD')
            .setCheck('Number')
            .appendField(translate('Fast EMA Period'));
        this.appendValueInput('SLOW_EMA_PERIOD')
            .setCheck('Number')
            .appendField(translate('Slow EMA Period'));
        this.appendValueInput('SIGNAL_EMA_PERIOD')
            .setCheck('Number')
            .appendField(translate('Signal EMA Period'));
        this.setOutput(true, 'Array');
        this.setColour('#dedede');
        this.setTooltip(translate('Calculates Moving Average Convergence Divergence (MACD) list from a list'));
        this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki');
    },
};

Blockly.JavaScript.macda = block => {
    const macdField = block.getFieldValue('MACDFIELDS_LIST');
    const input = expectValue(block, 'INPUT');
    const fastEmaPeriod =
        Blockly.JavaScript.valueToCode(block, 'FAST_EMA_PERIOD', Blockly.JavaScript.ORDER_ATOMIC) || '12';
    const slowEmaPeriod =
        Blockly.JavaScript.valueToCode(block, 'SLOW_EMA_PERIOD', Blockly.JavaScript.ORDER_ATOMIC) || '26';
    const signalEmaPeriod =
        Blockly.JavaScript.valueToCode(block, 'SIGNAL_EMA_PERIOD', Blockly.JavaScript.ORDER_ATOMIC) || '9';
    return [
        `Bot.macda(${input}, {
    fastEmaPeriod: ${fastEmaPeriod},
    slowEmaPeriod: ${slowEmaPeriod},
    signalEmaPeriod: ${signalEmaPeriod},
  }, ${macdField})`,
        Blockly.JavaScript.ORDER_NONE,
    ];
};
<|endoftext|>


<commit_before>import React from 'react';
import _ from 'lodash';
import {
  XAxis,
  YAxis,
  FlexibleWidthXYPlot,
  VerticalGridLines,
  HorizontalGridLines,
  LineSeries,
  AreaSeries,
  DiscreteColorLegend,
  Crosshair,
} from 'react-vis';
import {truncateString, displayValue} from '../../util/runhelpers.js';

class LinePlotPlot extends React.PureComponent {
  // Implements the actual plot and data as a PureComponent, so that we don't
  // re-render every time the crosshair (highlight) changes.
  render() {
    const smallSizeThresh = 50;

    let {height, sizeKey, xAxis, yScale, lines, disabled, xScale} = this.props;
    let xType = 'linear';
    if (xAxis == 'Absolute Time') {
      xType = 'time';
    } else if (xScale == 'log') {
      xType = 'log';
    }

    let nullGraph = false;
    let smallGraph = false;

    let maxDataLength = _.max(lines.map((line, i) => line.data.length));

    if (
      maxDataLength < smallSizeThresh &&
      lines &&
      _.max(
        lines.map(
          (line, i) =>
            line ? _.max(line.data.map((points, i) => points.x)) : 0,
        ),
      ) < smallSizeThresh
    ) {
      if (maxDataLength < 2) {
        nullGraph = true;
      }
      smallGraph = true;
    }

    return (
      <FlexibleWidthXYPlot
        animation={smallGraph}
        key={sizeKey}
        yType={yScale}
        xType={xType}
        height={height}>
        <VerticalGridLines />
        <HorizontalGridLines />
        <XAxis
          title={xAxis}
          tickTotal={5}
          tickValues={smallGraph ? _.range(0, smallSizeThresh) : null}
        />
        <YAxis tickValues={nullGraph ? [0, 1, 2] : null} />

        {lines
          .map(
            (line, i) =>
              !disabled[line.title] ? (
                line.area ? (
                  <AreaSeries key={i} color={line.color} data={line.data} />
                ) : (
                  <LineSeries key={i} color={line.color} data={line.data} />
                )
              ) : null,
          )
          .filter(o => o)}
      </FlexibleWidthXYPlot>
    );
  }
}

class LinePlotCrosshair extends React.PureComponent {
  // This doesn't contain the data, just the react-vis crosshair (the hover popup
  // that shows y-values at the current x-value).

  _setup(props) {
    // highlights is a map from x-value to list of y-information for each line at
    // that x-value
    this.highlights = {};

    let enabledLines = props.lines.filter(
      line => !props.disabled[line.title] && line.data.length > 0,
    );

    for (var line of enabledLines) {
      for (var point of line.data) {
        if (_.isNil(this.highlights[point.x])) {
          this.highlights[point.x] = [];
        }
        this.highlights[point.x].push({
          title: line.title,
          color: line.color,
          x: point.x,
          y: point.y,
        });
      }
    }

    // False line is a straight line that fits within the chart data. We render
    // it transparently and just use it for it's onNearestX callback.
    this.falseLine = [];
    if (enabledLines.length > 0) {
      let maxLength = _.max(enabledLines.map(line => line.data.length));
      let y = enabledLines[0].data[0].y;
      this.falseLine = _.range(0, maxLength).map(x => ({x: x, y: y}));
    }
  }

  componentWillMount() {
    this._setup(this.props);
  }

  componentWillReceiveProps(nextProps) {
    if (
      this.props.lines !== nextProps.lines ||
      this.props.disabled !== nextProps.disabled
    ) {
      this._setup(nextProps);
    }
  }

  render() {
    let {height, xAxis, highlightX, onMouseLeave, onHighlight} = this.props;
    let crosshairValues = null;
    if (highlightX && this.highlights[highlightX]) {
      crosshairValues = this.highlights[highlightX];
    }
    return (
      <FlexibleWidthXYPlot onMouseLeave={() => onMouseLeave()} height={height}>
        <LineSeries
          onNearestX={item => onHighlight(item.x)}
          color="black"
          opacity={0}
          data={this.falseLine}
        />
        {crosshairValues && (
          <Crosshair values={crosshairValues}>

              {this.props.xAxis + ': ' + crosshairValues[0].x}
              {crosshairValues
                .sort((a, b) => b.y - a.y)
                .filter(point => !point.title.startsWith('_'))
                .map((point, i) => (

                      {point.title + ': ' + displayValue(point.y)}

                ))}

          </Crosshair>
        )}
      </FlexibleWidthXYPlot>
    );
  }
}

export default class LinePlot extends React.PureComponent {
  state = {disabled: {}, highlightX: null};

  render() {
    return (

        <DiscreteColorLegend
          orientation="horizontal"
          onItemClick={(item, i) => {
            this.setState({
              ...this.state,
              disabled: {
                ...this.state.disabled,
                [item.title]: !this.state.disabled[item.title],
              },
            });
          }}
          items={this.props.lines
            .map((line, i) => ({
              title: truncateString(line.title, 40, 10),
              disabled: this.state.disabled[line.title],
              color: line.color,
            }))
            .filter(line => !line.title.startsWith('_'))}
        />

          <LinePlotPlot
            height={this.props.currentHeight - 60 || 240}
            sizeKey={this.props.sizeKey}
            xAxis={this.props.xAxis}
            yScale={this.props.yScale}
            lines={this.props.lines}
            disabled={this.state.disabled}
          />

            <LinePlotCrosshair
              height={240}
              xAxis={this.props.xAxis}
              lines={this.props.lines}
              disabled={this.state.disabled}
              highlightX={this.state.highlightX}
              onMouseLeave={() => this.setState({highlightX: null})}
              onHighlight={xValue => this.setState({highlightX: xValue})}
            />

    );
  }
}
<commit_msg>Fix line plot height.<commit_after>import React from 'react';
import _ from 'lodash';
import {
  XAxis,
  YAxis,
  FlexibleWidthXYPlot,
  VerticalGridLines,
  HorizontalGridLines,
  LineSeries,
  AreaSeries,
  DiscreteColorLegend,
  Crosshair,
} from 'react-vis';
import {truncateString, displayValue} from '../../util/runhelpers.js';

class LinePlotPlot extends React.PureComponent {
  // Implements the actual plot and data as a PureComponent, so that we don't
  // re-render every time the crosshair (highlight) changes.
  render() {
    const smallSizeThresh = 50;

    let {height, sizeKey, xAxis, yScale, lines, disabled, xScale} = this.props;
    let xType = 'linear';
    if (xAxis == 'Absolute Time') {
      xType = 'time';
    } else if (xScale == 'log') {
      xType = 'log';
    }

    let nullGraph = false;
    let smallGraph = false;

    let maxDataLength = _.max(lines.map((line, i) => line.data.length));

    if (
      maxDataLength < smallSizeThresh &&
      lines &&
      _.max(
        lines.map(
          (line, i) =>
            line ? _.max(line.data.map((points, i) => points.x)) : 0,
        ),
      ) < smallSizeThresh
    ) {
      if (maxDataLength < 2) {
        nullGraph = true;
      }
      smallGraph = true;
    }

    return (
      <FlexibleWidthXYPlot
        animation={smallGraph}
        key={sizeKey}
        yType={yScale}
        xType={xType}
        height={height}>
        <VerticalGridLines />
        <HorizontalGridLines />
        <XAxis
          title={xAxis}
          tickTotal={5}
          tickValues={smallGraph ? _.range(0, smallSizeThresh) : null}
        />
        <YAxis tickValues={nullGraph ? [0, 1, 2] : null} />

        {lines
          .map(
            (line, i) =>
              !disabled[line.title] ? (
                line.area ? (
                  <AreaSeries key={i} color={line.color} data={line.data} />
                ) : (
                  <LineSeries key={i} color={line.color} data={line.data} />
                )
              ) : null,
          )
          .filter(o => o)}
      </FlexibleWidthXYPlot>
    );
  }
}

class LinePlotCrosshair extends React.PureComponent {
  // This doesn't contain the data, just the react-vis crosshair (the hover popup
  // that shows y-values at the current x-value).

  _setup(props) {
    // highlights is a map from x-value to list of y-information for each line at
    // that x-value
    this.highlights = {};

    let enabledLines = props.lines.filter(
      line => !props.disabled[line.title] && line.data.length > 0,
    );

    for (var line of enabledLines) {
      for (var point of line.data) {
        if (_.isNil(this.highlights[point.x])) {
          this.highlights[point.x] = [];
        }
        this.highlights[point.x].push({
          title: line.title,
          color: line.color,
          x: point.x,
          y: point.y,
        });
      }
    }

    // False line is a straight line that fits within the chart data. We render
    // it transparently and just use it for it's onNearestX callback.
    this.falseLine = [];
    if (enabledLines.length > 0) {
      let maxLength = _.max(enabledLines.map(line => line.data.length));
      let y = enabledLines[0].data[0].y;
      this.falseLine = _.range(0, maxLength).map(x => ({x: x, y: y}));
    }
  }

  componentWillMount() {
    this._setup(this.props);
  }

  componentWillReceiveProps(nextProps) {
    if (
      this.props.lines !== nextProps.lines ||
      this.props.disabled !== nextProps.disabled
    ) {
      this._setup(nextProps);
    }
  }

  render() {
    let {height, xAxis, highlightX, onMouseLeave, onHighlight} = this.props;
    let crosshairValues = null;
    if (highlightX && this.highlights[highlightX]) {
      crosshairValues = this.highlights[highlightX];
    }
    return (
      <FlexibleWidthXYPlot onMouseLeave={() => onMouseLeave()} height={height}>
        <LineSeries
          onNearestX={item => onHighlight(item.x)}
          color="black"
          opacity={0}
          data={this.falseLine}
        />
        {crosshairValues && (
          <Crosshair values={crosshairValues}>

              {this.props.xAxis + ': ' + crosshairValues[0].x}
              {crosshairValues
                .sort((a, b) => b.y - a.y)
                .filter(point => !point.title.startsWith('_'))
                .map((point, i) => (

                      {point.title + ': ' + displayValue(point.y)}

                ))}

          </Crosshair>
        )}
      </FlexibleWidthXYPlot>
    );
  }
}

export default class LinePlot extends React.PureComponent {
  state = {disabled: {}, highlightX: null};

  render() {
    return (

        <DiscreteColorLegend
          orientation="horizontal"
          onItemClick={(item, i) => {
            this.setState({
              ...this.state,
              disabled: {
                ...this.state.disabled,
                [item.title]: !this.state.disabled[item.title],
              },
            });
          }}
          items={this.props.lines
            .map((line, i) => ({
              title: truncateString(line.title, 40, 10),
              disabled: this.state.disabled[line.title],
              color: line.color,
            }))
            .filter(line => !line.title.startsWith('_'))}
        />

          <LinePlotPlot
            height={this.props.currentHeight - 70 || 220}
            sizeKey={this.props.sizeKey}
            xAxis={this.props.xAxis}
            yScale={this.props.yScale}
            lines={this.props.lines}
            disabled={this.state.disabled}
          />

            <LinePlotCrosshair
              height={240}
              xAxis={this.props.xAxis}
              lines={this.props.lines}
              disabled={this.state.disabled}
              highlightX={this.state.highlightX}
              onMouseLeave={() => this.setState({highlightX: null})}
              onHighlight={xValue => this.setState({highlightX: xValue})}
            />

    );
  }
}
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { Helmet } from 'react-helmet'
import { get } from 'lodash/object'
import Paper from '@material-ui/core/Paper'
import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'
import Typography from '@material-ui/core/Typography'
import { isEqual } from 'lodash/lang'
import { Route, Switch } from 'react-router-dom'
import FirebaseAuthenticationUI from 'js/components/Authentication/FirebaseAuthenticationUI'
import VerifyEmailMessage from 'js/components/Authentication/VerifyEmailMessage'
import EnterUsernameForm from 'js/components/Authentication/EnterUsernameForm'
import SignInIframeMessage from 'js/components/Authentication/SignInIframeMessage'
import MissingEmailMessage from 'js/components/Authentication/MissingEmailMessage'
import { sendVerificationEmail } from 'js/authentication/user'
import {
  redirectToAuthIfNeeded,
  createNewUser,
} from 'js/authentication/helpers'
import {
  constructUrl,
  goTo,
  replaceUrl,
  authMessageURL,
  dashboardURL,
  enterUsernameURL,
  missingEmailMessageURL,
  searchBaseURL,
  verifyEmailURL,
} from 'js/navigation/navigation'
import { externalRedirect } from 'js/navigation/utils'
import Logo from 'js/components/Logo/Logo'
import searchFavicon from 'js/assets/logos/search-favicon.png'
import tabFavicon from 'js/assets/logos/favicon.ico'
import { parseUrlSearchString, validateAppName } from 'js/utils/utils'
import AssignExperimentGroups from 'js/components/Dashboard/AssignExperimentGroupsContainer'
import logger from 'js/utils/logger'
import tabTheme from 'js/theme/defaultV1'
import searchTheme from 'js/theme/searchTheme'
import { SEARCH_APP, TAB_APP } from 'js/constants'
import optIntoV4Beta from 'js/utils/v4-beta-opt-in'
import { isTabV4BetaUser } from 'js/utils/local-user-data-mgr'
import SetV4BetaMutation from 'js/mutations/SetV4BetaMutation'

// Handle the authentication flow:
//   check if current user is fully authenticated and redirect
//     to the app if they are; otherwise: ->
//   authenticate with Firebase ->
//   receive onSignInSuccess callback (called for both new
//     and existing users) ->
//   idempotently create a new user in our database ->
//   check if user's email is verified. If not, send a
//     verification email and ask the user to check their email;
//     otherwise: ->
//   fetch the user from our database and ensure they have a
//     username. If not, ask the user to enter a username;
//     otherwise, set the username in localStorage so we know it
//     exists in other views without needing to fetch it from the
//     database. Then, redirect to the app.
// This is somewhat convoluted for a few reasons:
//  * we're using Firebase UI for most authentication views, and it
//    does not support mandatory email verification or extra fields
//    (such as a username)
//  * we're making the username mandatory but can't rely on a field
//    from the authentication user token to store this info
class Authentication extends React.Component {
  async componentDidMount() {
    await this.navigateToAuthStep()
  }

  componentWillReceiveProps(nextProps) {
    // After the authUser object changes, automatically redirect
    // to the current authentication step.
    if (!isEqual(nextProps.authUser, this.props.authUser)) {
      this.navigateToAuthStep()
    }
  }

  // Whether this is rendering an /auth/action/ page, which include
  // email confirmation links and password reset links.
  isAuthActionURL() {
    return this.props.location.pathname.indexOf('/auth/action/') !== -1
  }

  getApp() {
    const { location } = this.props
    const urlParams = parseUrlSearchString(location.search)

    // To determine the app, we also need to check the "app" URL
    // parameter value in the "continueUrl" parameter for when users
    // verify their email via Firebase.
    let firebaseContinueURLAppVal = null
    try {
      const continueURL = urlParams.continueUrl
      if (continueURL) {
        firebaseContinueURLAppVal = new URL(continueURL).searchParams.get('app')
      }
    } catch (e) {
      console.error(e)
    }
    return validateAppName(urlParams.app || firebaseContinueURLAppVal)
  }

  getNextURLAfterSignIn() {
    const { location } = this.props
    const urlParams = parseUrlSearchString(location.search)
    const app = this.getApp()

    // If the "next" URL param specifies a destination URL for after
    // sign-in, use it. Otherwise, go to the main app page.
    const nextURL = urlParams.next ? decodeURIComponent(urlParams.next) : null
    const mainAppDestination = app === SEARCH_APP ? searchBaseURL : dashboardURL
    return nextURL ? nextURL : mainAppDestination
  }

  // What we should do after the auth process is finished,
  // after account creation, email verifcatiion, and username
  // creation.
  async onAuthProcessCompleted() {
    const { user } = this.props

    // If needed, opt the user into Tab v4. We need to
    // do this *after* all of the auth logic on this app
    // because v4 does not yet have a complete auth flow.
    const enableTabV4 = get(user, 'v4BetaEnabled') || isTabV4BetaUser()
    const destinationURL = this.getNextURLAfterSignIn()
    if (enableTabV4) {
      try {
        await optIntoV4Beta()
      } catch (e) {
        // TODO: show error to user / handle error more gracefully
        logger.error(e)
      }

      // If the local storage value is set to V4 but the user's
      // profile is not, update the user's profile.
      if (!get(user, 'v4BetaEnabled')) {
        await SetV4BetaMutation({
          userId: user.id,
          enabled: true,
        })
      }

      externalRedirect(destinationURL)
    } else {
      replaceUrl(destinationURL)
    }
  }

  async navigateToAuthStep() {
    // Don't do anything on /auth/action/ pages, which include
    // email confirmation links and password reset links.
    if (this.isAuthActionURL()) {
      return
    }
    const { authUser, location, user } = this.props
    const urlParams = parseUrlSearchString(location.search)

    const redirected = redirectToAuthIfNeeded({
      authUser,
      user,
      urlParams: { app: urlParams.app, next: urlParams.next },
    })

    // When anonymous users choose to sign in, do not go back to the
    // dashboard.
    const stayOnAuthPage = urlParams.noredirect === 'true'

    // The user is fully authed, so go to the dashboard.
    if (!redirected && !stayOnAuthPage) {
      await this.onAuthProcessCompleted()
    }
  }

  /**
   * Called when the user signs in (for both new and existing users).
   * At this point, the user may or may not have a verified email
   * or a username.
   * @param {object} currentUser - A Firebase user instance
   * @returns {Promise<boolean>}  A promise that resolves into a boolean,
   *   whether or not the email was sent successfully.
   */
  onSignInSuccess(currentUser) {
    // Check that the user has an email address.
    // An email address may be missing if the user signs in
    // with a social provider that does not share their
    // email address. In this case, ask the user to sign in
    // via another method.
    if (!currentUser.email) {
      goTo(missingEmailMessageURL, null, { keepURLParams: true })
      return
    }

    const app = this.getApp()

    // Get or create the user.
    // Important: we expect to call this on every sign-in event,
    // even for anonymous users who already have a user in our
    // database, because this is when we add their email address
    // and email verification status to their profile.
    return createNewUser()
      .then(async createdOrFetchedUser => {
        // Check if the user has verified their email.
        // Note: later versions of firebaseui-web might support mandatory
        // email verification:
        // https://github.com/firebase/firebaseui-web/issues/21
        if (!currentUser.emailVerified) {
          // Ask the user to verify their email.
          sendVerificationEmail({
            // Pass the "app" and "next" URL parameter values in the
            // "continue URL" after email verification.
            continueURL: constructUrl(
              enterUsernameURL,
              { app: app, next: this.getNextURLAfterSignIn() },
              { absolute: true }
            ),
          })
            .then(emailSent => {
              goTo(verifyEmailURL, null, { keepURLParams: true })
            })
            .catch(err => {
              // TODO: show error message to the user
              logger.error(err)
            })
        } else {
          // Fetch the user from our database. This will update the `user`
          // prop, which will let us navigate to the appropriate step in
          // authentication.
          // https://github.com/gladly-team/tab/issues/589
          this.props.fetchUser()
        }
      })
      .catch(err => {
        // TODO: show error message to the user
        logger.error(err)
      })
  }

  render() {
    const { user, location } = this.props

    // Show a different logo depending on the app for which the user is
    // signing in.
    const app = this.getApp()

    // Set a different theme depending on the app.
    let theme = tabTheme
    switch (app) {
      case TAB_APP:
        theme = tabTheme
        break
      case SEARCH_APP:
        theme = searchTheme
        break
      default:
        theme = tabTheme
    }
    const defaultTheme = createMuiTheme(theme)

    // Whether we are requiring the anonymous user to sign in.
    const urlParams = parseUrlSearchString(location.search)
    const isMandatoryAnonymousSignIn = urlParams.mandatory === 'true'
    const showRequiredSignInExplanation =
      isMandatoryAnonymousSignIn &&
      // Don't display the message on the iframe auth message page, because
      // it will have its own message.
      location.pathname.indexOf(authMessageURL) === -1
    const nextURL = this.getNextURLAfterSignIn()

    return (
      <MuiThemeProvider theme={defaultTheme}>

          {app === TAB_APP ? (
            <Helmet>
              <title>Sign in - Tab for a Cause</title>
              <link rel="icon" href={tabFavicon} />
            </Helmet>
          ) : (
            <Helmet>
              <title>Sign in - Search for a Cause</title>
              <link rel="icon" href={searchFavicon} key={'favicon'} />
            </Helmet>
          )}
          {/* This is a similar style to the homepage */}

            <Logo brand={app} includeText style={{ height: 40 }} />

              <Switch>
                <Route
                  exact
                  path="/newtab/auth/verify-email/"
                  render={props => (
                    <VerifyEmailMessage {...props} user={user} app={app} />
                  )}
                />
                <Route
                  exact
                  path="/newtab/auth/username/"
                  render={props => (
                    <EnterUsernameForm
                      {...props}
                      user={user}
                      app={app}
                      onCompleted={this.onAuthProcessCompleted.bind(this)}
                    />
                  )}
                />
                <Route
                  exact
                  path="/newtab/auth/welcome/"
                  render={props => (
                    <SignInIframeMessage {...props} user={user} app={app} />
                  )}
                />
                <Route
                  exact
                  path="/newtab/auth/missing-email/"
                  component={MissingEmailMessage}
                />
                <Route
                  path="/newtab/auth/"
                  render={props => (
                    <FirebaseAuthenticationUI
                      {...props}
                      onSignInSuccess={this.onSignInSuccess.bind(this)}
                      user={user}
                      app={app}
                    />
                  )}
                />
              </Switch>

            {!showRequiredSignInExplanation ? (
              // Using same style as homepage

"One of the simplest ways to raise money"

                  - USA Today

            ) : null}

          {showRequiredSignInExplanation ? (

              <Paper elevation={1}>

                  <Typography variant={'body2'} style={{ fontWeight: 'bold' }}>
                    Hey there!
                  </Typography>
                  <Typography variant={'body2'}>
                    We ask you to sign in after a while so you don't lose access
                    to your notes, bookmarks, and Hearts (even if you drop your
                    computer in a puddle). Signing in also lets you sync your
                    tab between browsers – nice!
                  </Typography>

              </Paper>

          ) : null}
          {/*
          If we don't assign experiment groups here because we redirect or the
          user does not yet exist, that's okay. We'll try to assign the user to
          experiments on the dashboard as well.
        */}
          {user ? <AssignExperimentGroups user={user} isNewUser /> : null}

      </MuiThemeProvider>
    )
  }
}

Authentication.propTypes = {
  location: PropTypes.shape({
    pathname: PropTypes.string.isRequired,
    search: PropTypes.string.isRequired,
  }),
  fetchUser: PropTypes.func.isRequired,
  // User fetched from the auth service.
  authUser: PropTypes.shape({
    id: PropTypes.string,
    email: PropTypes.string,
    username: PropTypes.string,
    isAnonymous: PropTypes.bool,
    emailVerified: PropTypes.bool,
  }),
  // User fetched from our database (not the auth service user).
  user: PropTypes.shape({
    id: PropTypes.string,
    username: PropTypes.string,
    v4BetaEnabled: PropTypes.bool,
  }),
}

export default Authentication
<commit_msg>Remove unused var<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { Helmet } from 'react-helmet'
import { get } from 'lodash/object'
import Paper from '@material-ui/core/Paper'
import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'
import Typography from '@material-ui/core/Typography'
import { isEqual } from 'lodash/lang'
import { Route, Switch } from 'react-router-dom'
import FirebaseAuthenticationUI from 'js/components/Authentication/FirebaseAuthenticationUI'
import VerifyEmailMessage from 'js/components/Authentication/VerifyEmailMessage'
import EnterUsernameForm from 'js/components/Authentication/EnterUsernameForm'
import SignInIframeMessage from 'js/components/Authentication/SignInIframeMessage'
import MissingEmailMessage from 'js/components/Authentication/MissingEmailMessage'
import { sendVerificationEmail } from 'js/authentication/user'
import {
  redirectToAuthIfNeeded,
  createNewUser,
} from 'js/authentication/helpers'
import {
  constructUrl,
  goTo,
  replaceUrl,
  authMessageURL,
  dashboardURL,
  enterUsernameURL,
  missingEmailMessageURL,
  searchBaseURL,
  verifyEmailURL,
} from 'js/navigation/navigation'
import { externalRedirect } from 'js/navigation/utils'
import Logo from 'js/components/Logo/Logo'
import searchFavicon from 'js/assets/logos/search-favicon.png'
import tabFavicon from 'js/assets/logos/favicon.ico'
import { parseUrlSearchString, validateAppName } from 'js/utils/utils'
import AssignExperimentGroups from 'js/components/Dashboard/AssignExperimentGroupsContainer'
import logger from 'js/utils/logger'
import tabTheme from 'js/theme/defaultV1'
import searchTheme from 'js/theme/searchTheme'
import { SEARCH_APP, TAB_APP } from 'js/constants'
import optIntoV4Beta from 'js/utils/v4-beta-opt-in'
import { isTabV4BetaUser } from 'js/utils/local-user-data-mgr'
import SetV4BetaMutation from 'js/mutations/SetV4BetaMutation'

// Handle the authentication flow:
//   check if current user is fully authenticated and redirect
//     to the app if they are; otherwise: ->
//   authenticate with Firebase ->
//   receive onSignInSuccess callback (called for both new
//     and existing users) ->
//   idempotently create a new user in our database ->
//   check if user's email is verified. If not, send a
//     verification email and ask the user to check their email;
//     otherwise: ->
//   fetch the user from our database and ensure they have a
//     username. If not, ask the user to enter a username;
//     otherwise, set the username in localStorage so we know it
//     exists in other views without needing to fetch it from the
//     database. Then, redirect to the app.
// This is somewhat convoluted for a few reasons:
//  * we're using Firebase UI for most authentication views, and it
//    does not support mandatory email verification or extra fields
//    (such as a username)
//  * we're making the username mandatory but can't rely on a field
//    from the authentication user token to store this info
class Authentication extends React.Component {
  async componentDidMount() {
    await this.navigateToAuthStep()
  }

  componentWillReceiveProps(nextProps) {
    // After the authUser object changes, automatically redirect
    // to the current authentication step.
    if (!isEqual(nextProps.authUser, this.props.authUser)) {
      this.navigateToAuthStep()
    }
  }

  // Whether this is rendering an /auth/action/ page, which include
  // email confirmation links and password reset links.
  isAuthActionURL() {
    return this.props.location.pathname.indexOf('/auth/action/') !== -1
  }

  getApp() {
    const { location } = this.props
    const urlParams = parseUrlSearchString(location.search)

    // To determine the app, we also need to check the "app" URL
    // parameter value in the "continueUrl" parameter for when users
    // verify their email via Firebase.
    let firebaseContinueURLAppVal = null
    try {
      const continueURL = urlParams.continueUrl
      if (continueURL) {
        firebaseContinueURLAppVal = new URL(continueURL).searchParams.get('app')
      }
    } catch (e) {
      console.error(e)
    }
    return validateAppName(urlParams.app || firebaseContinueURLAppVal)
  }

  getNextURLAfterSignIn() {
    const { location } = this.props
    const urlParams = parseUrlSearchString(location.search)
    const app = this.getApp()

    // If the "next" URL param specifies a destination URL for after
    // sign-in, use it. Otherwise, go to the main app page.
    const nextURL = urlParams.next ? decodeURIComponent(urlParams.next) : null
    const mainAppDestination = app === SEARCH_APP ? searchBaseURL : dashboardURL
    return nextURL ? nextURL : mainAppDestination
  }

  // What we should do after the auth process is finished,
  // after account creation, email verifcatiion, and username
  // creation.
  async onAuthProcessCompleted() {
    const { user } = this.props

    // If needed, opt the user into Tab v4. We need to
    // do this *after* all of the auth logic on this app
    // because v4 does not yet have a complete auth flow.
    const enableTabV4 = get(user, 'v4BetaEnabled') || isTabV4BetaUser()
    const destinationURL = this.getNextURLAfterSignIn()
    if (enableTabV4) {
      try {
        await optIntoV4Beta()
      } catch (e) {
        // TODO: show error to user / handle error more gracefully
        logger.error(e)
      }

      // If the local storage value is set to V4 but the user's
      // profile is not, update the user's profile.
      if (!get(user, 'v4BetaEnabled')) {
        await SetV4BetaMutation({
          userId: user.id,
          enabled: true,
        })
      }

      externalRedirect(destinationURL)
    } else {
      replaceUrl(destinationURL)
    }
  }

  async navigateToAuthStep() {
    // Don't do anything on /auth/action/ pages, which include
    // email confirmation links and password reset links.
    if (this.isAuthActionURL()) {
      return
    }
    const { authUser, location, user } = this.props
    const urlParams = parseUrlSearchString(location.search)

    const redirected = redirectToAuthIfNeeded({
      authUser,
      user,
      urlParams: { app: urlParams.app, next: urlParams.next },
    })

    // When anonymous users choose to sign in, do not go back to the
    // dashboard.
    const stayOnAuthPage = urlParams.noredirect === 'true'

    // The user is fully authed, so go to the dashboard.
    if (!redirected && !stayOnAuthPage) {
      await this.onAuthProcessCompleted()
    }
  }

  /**
   * Called when the user signs in (for both new and existing users).
   * At this point, the user may or may not have a verified email
   * or a username.
   * @param {object} currentUser - A Firebase user instance
   * @returns {Promise<boolean>}  A promise that resolves into a boolean,
   *   whether or not the email was sent successfully.
   */
  onSignInSuccess(currentUser) {
    // Check that the user has an email address.
    // An email address may be missing if the user signs in
    // with a social provider that does not share their
    // email address. In this case, ask the user to sign in
    // via another method.
    if (!currentUser.email) {
      goTo(missingEmailMessageURL, null, { keepURLParams: true })
      return
    }

    const app = this.getApp()

    // Get or create the user.
    // Important: we expect to call this on every sign-in event,
    // even for anonymous users who already have a user in our
    // database, because this is when we add their email address
    // and email verification status to their profile.
    return createNewUser()
      .then(async createdOrFetchedUser => {
        // Check if the user has verified their email.
        // Note: later versions of firebaseui-web might support mandatory
        // email verification:
        // https://github.com/firebase/firebaseui-web/issues/21
        if (!currentUser.emailVerified) {
          // Ask the user to verify their email.
          sendVerificationEmail({
            // Pass the "app" and "next" URL parameter values in the
            // "continue URL" after email verification.
            continueURL: constructUrl(
              enterUsernameURL,
              { app: app, next: this.getNextURLAfterSignIn() },
              { absolute: true }
            ),
          })
            .then(emailSent => {
              goTo(verifyEmailURL, null, { keepURLParams: true })
            })
            .catch(err => {
              // TODO: show error message to the user
              logger.error(err)
            })
        } else {
          // Fetch the user from our database. This will update the `user`
          // prop, which will let us navigate to the appropriate step in
          // authentication.
          // https://github.com/gladly-team/tab/issues/589
          this.props.fetchUser()
        }
      })
      .catch(err => {
        // TODO: show error message to the user
        logger.error(err)
      })
  }

  render() {
    const { user, location } = this.props

    // Show a different logo depending on the app for which the user is
    // signing in.
    const app = this.getApp()

    // Set a different theme depending on the app.
    let theme = tabTheme
    switch (app) {
      case TAB_APP:
        theme = tabTheme
        break
      case SEARCH_APP:
        theme = searchTheme
        break
      default:
        theme = tabTheme
    }
    const defaultTheme = createMuiTheme(theme)

    // Whether we are requiring the anonymous user to sign in.
    const urlParams = parseUrlSearchString(location.search)
    const isMandatoryAnonymousSignIn = urlParams.mandatory === 'true'
    const showRequiredSignInExplanation =
      isMandatoryAnonymousSignIn &&
      // Don't display the message on the iframe auth message page, because
      // it will have its own message.
      location.pathname.indexOf(authMessageURL) === -1

    return (
      <MuiThemeProvider theme={defaultTheme}>

          {app === TAB_APP ? (
            <Helmet>
              <title>Sign in - Tab for a Cause</title>
              <link rel="icon" href={tabFavicon} />
            </Helmet>
          ) : (
            <Helmet>
              <title>Sign in - Search for a Cause</title>
              <link rel="icon" href={searchFavicon} key={'favicon'} />
            </Helmet>
          )}
          {/* This is a similar style to the homepage */}

            <Logo brand={app} includeText style={{ height: 40 }} />

              <Switch>
                <Route
                  exact
                  path="/newtab/auth/verify-email/"
                  render={props => (
                    <VerifyEmailMessage {...props} user={user} app={app} />
                  )}
                />
                <Route
                  exact
                  path="/newtab/auth/username/"
                  render={props => (
                    <EnterUsernameForm
                      {...props}
                      user={user}
                      app={app}
                      onCompleted={this.onAuthProcessCompleted.bind(this)}
                    />
                  )}
                />
                <Route
                  exact
                  path="/newtab/auth/welcome/"
                  render={props => (
                    <SignInIframeMessage {...props} user={user} app={app} />
                  )}
                />
                <Route
                  exact
                  path="/newtab/auth/missing-email/"
                  component={MissingEmailMessage}
                />
                <Route
                  path="/newtab/auth/"
                  render={props => (
                    <FirebaseAuthenticationUI
                      {...props}
                      onSignInSuccess={this.onSignInSuccess.bind(this)}
                      user={user}
                      app={app}
                    />
                  )}
                />
              </Switch>

            {!showRequiredSignInExplanation ? (
              // Using same style as homepage

"One of the simplest ways to raise money"

                  - USA Today

            ) : null}

          {showRequiredSignInExplanation ? (

              <Paper elevation={1}>

                  <Typography variant={'body2'} style={{ fontWeight: 'bold' }}>
                    Hey there!
                  </Typography>
                  <Typography variant={'body2'}>
                    We ask you to sign in after a while so you don't lose access
                    to your notes, bookmarks, and Hearts (even if you drop your
                    computer in a puddle). Signing in also lets you sync your
                    tab between browsers – nice!
                  </Typography>

              </Paper>

          ) : null}
          {/*
          If we don't assign experiment groups here because we redirect or the
          user does not yet exist, that's okay. We'll try to assign the user to
          experiments on the dashboard as well.
        */}
          {user ? <AssignExperimentGroups user={user} isNewUser /> : null}

      </MuiThemeProvider>
    )
  }
}

Authentication.propTypes = {
  location: PropTypes.shape({
    pathname: PropTypes.string.isRequired,
    search: PropTypes.string.isRequired,
  }),
  fetchUser: PropTypes.func.isRequired,
  // User fetched from the auth service.
  authUser: PropTypes.shape({
    id: PropTypes.string,
    email: PropTypes.string,
    username: PropTypes.string,
    isAnonymous: PropTypes.bool,
    emailVerified: PropTypes.bool,
  }),
  // User fetched from our database (not the auth service user).
  user: PropTypes.shape({
    id: PropTypes.string,
    username: PropTypes.string,
    v4BetaEnabled: PropTypes.bool,
  }),
}

export default Authentication
<|endoftext|>