<commit_before>angular.module('myApp.common.services.appStrings', []).

factory('appStrings', function() {
	var ERROR = 'danger', SUCCESS = 'success', INFO = 'info';

	// Notify strings (need to be functionalized)
	var STRINGS = {
		cart: { // Cart Pages
			disc_dup: { // Duplicate Discount (notify object)
				pre: 'Duplicate Code!',
				msg: 'Are you trying to cheat us?',
				type:ERROR
			},
			disc_inv: { // Invalid Discount (notify object)
				pre: 'Invalid!',
				msg: 'We do not have this code listed.
Please try again or contact us at info@upstreamacademy.com with questions.',
				type:ERROR
			},
			disc_exp: { // Expired Discount (notify object)
				pre: 'Expired!',
				msg: 'This code has expired!',
				type:ERROR
			},
			disc_unr: { // Unrelated Discount (notify object)
				pre: 'Unrelated!',
				msg: 'Not associated with any items in your cart.',
				type:ERROR
			},
			disc_yep: { // Unrelated Discount (notify object)
				pre: 'Success!',
				msg: 'Added discount to current order!',
				type:SUCCESS
			},
			needOpt: { // Options needed (notify object)
				pre: 'Information Needed',
				msg: 'Some of the items in your cart require you to fill out a form.
Please click the orange buttons to provide this information.',
				type:ERROR, delay:20
			},
			prevPur: { // Previous Purchase (notify object)
				pre: 'Previous Purchase',
				msg: 'An item in your cart has already been purchased (shown in red).  Please remove it before continuing to checkout.',
				type:ERROR, delay:20
			},
			checkOut: { // Checkout Complete (notify object)
				pre: 'Checkout Complete',
				msg: 'You will be redirected to your appropriate payment processing method',
				type:SUCCESS, delay:20
			},
			negative: { // Negative Cart
				pre: 'Negative Cart',
				msg: 'You have provided information in such a way that we are paying you.  Please contact us directly if we owe you money',
				type:ERROR, delay:20
			},
			warn: {
				pre: 'Previously purchased items in cart',
				msg: 'You will have to remove the items marked in RED before you can checkout',
				type:INFO, delay:60
			}
		},
		contact: { // Modal address form (register/xx > add attendee > add employee)
			address: { // No contact address (notify object)
				pre: 'No address!',
				msg: 'Please assign an address to this new contact.',
				type:ERROR
			},
			error: { // Server error (notify object)
				pre: 'Duplicate Email!',
				msg: 'This email already has an account.  Please choose a different email address.',
				type:ERROR
			},
			duplicate: { // Duplicate email
				pre:'Duplicate Email!',
				msg:'This email is already associated with an account in the system.  Please choose "Select Employee" to find them or choose a different email.',
				type:ERROR
			}
		},
		payment: { // Custom payment form
			success: {
				pre:'Thank you!',
				msg:'The payment amount you enterd has been added to your cart.',
				type:SUCCESS, delay:5
			},
			failure: { // Server Error
				pre:'Error!', msg:'A payment with this name and value is already in your cart.',
				type:ERROR
			}
		},
		register: { // Registration Form
			passMatch: { // Passwords Match
				pre:'Passwords do not match!',
				msg:'Please try again.',
				type:ERROR
			},
			firmAddr: { // Assign Firm Address
				pre:'No firm Address!',
				msg:'Please assign a firm address.',
				type:ERROR
			},
			userAddr: { // Assign User Address
				pre:'No user Address!',
				msg:'Please assign a user address.',
				type:ERROR
			},
			success: { // Account created
				pre:'Success!',
				msg:'Your account has successfully been created',
				type:SUCCESS
			},
			duplicate: { // Duplicate email
				pre:'Duplicate Email!',
				msg:'This email already has an account.  Please click the login button and attempt a password reset.',
				type:ERROR
			},
			failure: { // Server Error
				pre:'Error!',
				msg:'There was an unknown error creating your account.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			},
			resetGood: { // successful reset
				pre:'Reset Complete',
				msg:'You have been sent an email with instructions on resetting you password.',
				type:SUCCESS
			},
			resetBad: { // un-successful reset
				pre:'Reset Error',
				msg:'There has been an error resetting your password.  Please try again soon',
				type:ERROR
			},
			invalidPhone: { // invalid phone number
				pre:'Invalid Phone Number',
				msg:'Please include only 10 digits in your phone number',
				type:ERROR
			}
		},
		user: { // Account settings Form
			passMatch: { // Passwords Match
				pre:'Passwords do not match!',
				msg:'Please try again.',
				type:ERROR
			},
			firmAddr: { // Assign Firm Address
				pre:'No firm Address!',
				msg:'Please assign a firm address.',
				type:ERROR
			},
			userAddr: { // Assign User Address
				pre:'No user Address!',
				msg:'Please assign a user address.',
				type:ERROR
			},
			success: { // Account created
				pre:'Success!',
				msg:'Your Account has been updated.',
				type:SUCCESS
			},
			dupEmail: { // Duplicate email
				pre:'Duplicate Email!',
				msg:'This email already has an account. Please click the login button and attempt a password reset.',
				type:ERROR
			},
			badPass: { // Bad password
				pre:'bad Password!',
				msg:'Your password is incorrect. Please try again or attempt to reset you password.',
				type:ERROR
			},
			failure: { // Server Error
				pre:'Error!',
				msg:'There was an unknown error creating your account. Please try again or contact Upstream Academy for help.',
				type:ERROR
			},
			dupCode: { // duplicate membership
				pre:'Duplicate Membership!',
				msg:'Your firm already has membership in this group.',
				type:ERROR
			},
			dneCode: { // membership does not exist
				pre:'Does Not Exist!',
				msg:'This firm code does not exist.',
				type:ERROR
			},
			errCode: {
				pre:'Error!',
				msg:'There was an unknown error checking you group access code.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			},
			goodCode: {
				pre:'Success!',
				msg:'Your Firm membership has been approved and has been added to your account.',
				type:SUCCESS
			},
			invalidPhone: { // invalid phone number
				pre:'Invalid Phone Number',
				msg:'Please include only 10 digits in your phone number',
				type:ERROR
			}
		},
		address: {
			error: { // server error
				pre:'Error!',
				msg:'There was an unknown error creating your address.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			}
		},
		reset: {
			match: { // Passwords Match
				pre:'Passwords do not match!',
				msg:'Please try again.',
				type:ERROR
			},
			error: { // some server error
				pre:'Error!',
				msg: 'There was an unknown error creating your address.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			}
		},
		conference: {
			attendee: { // Passwords Match
				pre:'Whose coming?',
				msg:'Please add at least one Attendee.',
				type:ERROR
			},
			error: { // some server error
				pre:'Error!',
				msg: 'There was an unknown error saving your information.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			},
			immutable: {
				pre:'Heads up!',
				msg:'Previously purchased attendees are visible in this view.  They cannot be removed and they will not be added toward your total.',
				type:INFO, delay: 60
			}
		}
	};

	// interpolate + functionalize!
	var interpolate = function(str, obj) { // http://stackoverflow.com/a/1408373/3220865
		return str.replace(/{([^{}]+)}/g, function (match, p1) {
			return typeof obj[p1] === 'string' || typeof obj[p1] === 'number' ? obj[p1] : match;
		});
	};
	angular.forEach(STRINGS, function (area) {
		angular.forEach(area, function (obj, key) {
			area[key] = function (interObj) {
				if (interObj) obj.msg = interpolate(obj.msg, interObj);
				obj.random = Math.random();
				return obj;
			};
		});
	});

	// Not functionalized
	angular.extend(STRINGS, {
		ERROR: ERROR,
		SUCCESS: SUCCESS,
		INFO: INFO,
		paypal: { // Pay-pal (this is for the app)
			url: 'https://payflowlink.paypal.com',
			uri: { // used $.param()
				'AMOUNT': '0',
				'DESCRIPTION': 'Upstream Academy Purchase',
				'LOGIN': 'UpstreamAcademy',
				// 'MODE': 'TEST',
				'PARTNER': 'PayPal',
				'SHOWCONFIRM': 'FALSE',
				'TYPE': 'S'
			},
			totalParam: 'AMOUNT'
		},
		pay: {
			customPayName: 'Custom Payment' // shows up in cart
		}
	});

	return STRINGS;
});<commit_msg>Kelsey: fixing credit card payments<commit_after>angular.module('myApp.common.services.appStrings', []).

factory('appStrings', function() {
	var ERROR = 'danger', SUCCESS = 'success', INFO = 'info';

	// Notify strings (need to be functionalized)
	var STRINGS = {
		cart: { // Cart Pages
			disc_dup: { // Duplicate Discount (notify object)
				pre: 'Duplicate Code!',
				msg: 'Are you trying to cheat us?',
				type:ERROR
			},
			disc_inv: { // Invalid Discount (notify object)
				pre: 'Invalid!',
				msg: 'We do not have this code listed.
Please try again or contact us at info@upstreamacademy.com with questions.',
				type:ERROR
			},
			disc_exp: { // Expired Discount (notify object)
				pre: 'Expired!',
				msg: 'This code has expired!',
				type:ERROR
			},
			disc_unr: { // Unrelated Discount (notify object)
				pre: 'Unrelated!',
				msg: 'Not associated with any items in your cart.',
				type:ERROR
			},
			disc_yep: { // Unrelated Discount (notify object)
				pre: 'Success!',
				msg: 'Added discount to current order!',
				type:SUCCESS
			},
			needOpt: { // Options needed (notify object)
				pre: 'Information Needed',
				msg: 'Some of the items in your cart require you to fill out a form.
Please click the orange buttons to provide this information.',
				type:ERROR, delay:20
			},
			prevPur: { // Previous Purchase (notify object)
				pre: 'Previous Purchase',
				msg: 'An item in your cart has already been purchased (shown in red).  Please remove it before continuing to checkout.',
				type:ERROR, delay:20
			},
			checkOut: { // Checkout Complete (notify object)
				pre: 'Checkout Complete',
				msg: 'You will be redirected to your appropriate payment processing method',
				type:SUCCESS, delay:20
			},
			negative: { // Negative Cart
				pre: 'Negative Cart',
				msg: 'You have provided information in such a way that we are paying you.  Please contact us directly if we owe you money',
				type:ERROR, delay:20
			},
			warn: {
				pre: 'Previously purchased items in cart',
				msg: 'You will have to remove the items marked in RED before you can checkout',
				type:INFO, delay:60
			}
		},
		contact: { // Modal address form (register/xx > add attendee > add employee)
			address: { // No contact address (notify object)
				pre: 'No address!',
				msg: 'Please assign an address to this new contact.',
				type:ERROR
			},
			error: { // Server error (notify object)
				pre: 'Duplicate Email!',
				msg: 'This email already has an account.  Please choose a different email address.',
				type:ERROR
			},
			duplicate: { // Duplicate email
				pre:'Duplicate Email!',
				msg:'This email is already associated with an account in the system.  Please choose "Select Employee" to find them or choose a different email.',
				type:ERROR
			}
		},
		payment: { // Custom payment form
			success: {
				pre:'Thank you!',
				msg:'The payment amount you enterd has been added to your cart.',
				type:SUCCESS, delay:5
			},
			failure: { // Server Error
				pre:'Error!', msg:'A payment with this name and value is already in your cart.',
				type:ERROR
			}
		},
		register: { // Registration Form
			passMatch: { // Passwords Match
				pre:'Passwords do not match!',
				msg:'Please try again.',
				type:ERROR
			},
			firmAddr: { // Assign Firm Address
				pre:'No firm Address!',
				msg:'Please assign a firm address.',
				type:ERROR
			},
			userAddr: { // Assign User Address
				pre:'No user Address!',
				msg:'Please assign a user address.',
				type:ERROR
			},
			success: { // Account created
				pre:'Success!',
				msg:'Your account has successfully been created',
				type:SUCCESS
			},
			duplicate: { // Duplicate email
				pre:'Duplicate Email!',
				msg:'This email already has an account.  Please click the login button and attempt a password reset.',
				type:ERROR
			},
			failure: { // Server Error
				pre:'Error!',
				msg:'There was an unknown error creating your account.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			},
			resetGood: { // successful reset
				pre:'Reset Complete',
				msg:'You have been sent an email with instructions on resetting you password.',
				type:SUCCESS
			},
			resetBad: { // un-successful reset
				pre:'Reset Error',
				msg:'There has been an error resetting your password.  Please try again soon',
				type:ERROR
			},
			invalidPhone: { // invalid phone number
				pre:'Invalid Phone Number',
				msg:'Please include only 10 digits in your phone number',
				type:ERROR
			}
		},
		user: { // Account settings Form
			passMatch: { // Passwords Match
				pre:'Passwords do not match!',
				msg:'Please try again.',
				type:ERROR
			},
			firmAddr: { // Assign Firm Address
				pre:'No firm Address!',
				msg:'Please assign a firm address.',
				type:ERROR
			},
			userAddr: { // Assign User Address
				pre:'No user Address!',
				msg:'Please assign a user address.',
				type:ERROR
			},
			success: { // Account created
				pre:'Success!',
				msg:'Your Account has been updated.',
				type:SUCCESS
			},
			dupEmail: { // Duplicate email
				pre:'Duplicate Email!',
				msg:'This email already has an account. Please click the login button and attempt a password reset.',
				type:ERROR
			},
			badPass: { // Bad password
				pre:'bad Password!',
				msg:'Your password is incorrect. Please try again or attempt to reset you password.',
				type:ERROR
			},
			failure: { // Server Error
				pre:'Error!',
				msg:'There was an unknown error creating your account. Please try again or contact Upstream Academy for help.',
				type:ERROR
			},
			dupCode: { // duplicate membership
				pre:'Duplicate Membership!',
				msg:'Your firm already has membership in this group.',
				type:ERROR
			},
			dneCode: { // membership does not exist
				pre:'Does Not Exist!',
				msg:'This firm code does not exist.',
				type:ERROR
			},
			errCode: {
				pre:'Error!',
				msg:'There was an unknown error checking you group access code.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			},
			goodCode: {
				pre:'Success!',
				msg:'Your Firm membership has been approved and has been added to your account.',
				type:SUCCESS
			},
			invalidPhone: { // invalid phone number
				pre:'Invalid Phone Number',
				msg:'Please include only 10 digits in your phone number',
				type:ERROR
			}
		},
		address: {
			error: { // server error
				pre:'Error!',
				msg:'There was an unknown error creating your address.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			}
		},
		reset: {
			match: { // Passwords Match
				pre:'Passwords do not match!',
				msg:'Please try again.',
				type:ERROR
			},
			error: { // some server error
				pre:'Error!',
				msg: 'There was an unknown error creating your address.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			}
		},
		conference: {
			attendee: { // Passwords Match
				pre:'Whose coming?',
				msg:'Please add at least one Attendee.',
				type:ERROR
			},
			error: { // some server error
				pre:'Error!',
				msg: 'There was an unknown error saving your information.  Please try again or contact Upstream Academy for help.',
				type:ERROR
			},
			immutable: {
				pre:'Heads up!',
				msg:'Previously purchased attendees are visible in this view.  They cannot be removed and they will not be added toward your total.',
				type:INFO, delay: 60
			}
		}
	};

	// interpolate + functionalize!
	var interpolate = function(str, obj) { // http://stackoverflow.com/a/1408373/3220865
		return str.replace(/{([^{}]+)}/g, function (match, p1) {
			return typeof obj[p1] === 'string' || typeof obj[p1] === 'number' ? obj[p1] : match;
		});
	};
	angular.forEach(STRINGS, function (area) {
		angular.forEach(area, function (obj, key) {
			area[key] = function (interObj) {
				if (interObj) obj.msg = interpolate(obj.msg, interObj);
				obj.random = Math.random();
				return obj;
			};
		});
	});

	// Not functionalized
	angular.extend(STRINGS, {
		ERROR: ERROR,
		SUCCESS: SUCCESS,
		INFO: INFO,
		paypal: { // Pay-pal (this is for the app)
			url: 'https://payflowlink.paypal.com',
			uri: { // used $.param()
				// 'AMOUNT': '0',
				// 'DESCRIPTION': 'Upstream Academy Purchase',
				// 'LOGIN': 'UpstreamAcademy',
				// // 'MODE': 'TEST',
				// 'PARTNER': 'PayPal',
				// 'SHOWCONFIRM': 'FALSE',
				// 'TYPE': 'S'
				'AMOUNT': '0',
				'DESCRIPTION': 'Upstream Academy Purchase',
				'LOGIN': 'us227961398',
				'PARTNER': 'PayPal',
				'SHOWCONFIRM': 'FALSE',
				'TYPE': 'S'
			},
			totalParam: 'AMOUNT'
		},
		pay: {
			customPayName: 'Custom Payment' // shows up in cart
		}
	});

	return STRINGS;
});<|endoftext|>


<commit_before>import AsciinemaPlayerCore from '../core';
import { batch, createState, Match, onCleanup, onMount, reconcile, Switch } from 'solid-js';
import Terminal from './Terminal';
import ControlBar from './ControlBar';
import LoaderOverlay from './LoaderOverlay';
import StartOverlay from './StartOverlay';

export default props => {
  const [state, setState] = createState({
    state: 'initial',
    width: props.cols,
    height: props.rows,
    duration: null,
    lines: [],
    cursor: undefined,
    terminalScale: 1.0,
    showControls: false,
    currentTime: null,
    remainingTime: null,
    progress: null,
    blink: true
  });

  let frameRequestId;
  let userActivityTimeoutId;
  let timeUpdateIntervalId;
  let blinkIntervalId;

  let wrapperRef;
  let terminalRef;
  let charW;
  let charH;
  let bordersW;
  let bordersH;

  const core = AsciinemaPlayerCore.build(props.src, {
    loop: props.loop || false,
    cols: props.cols,
    rows: props.rows
  }, () => onFinish());

  onMount(() => {
    console.log('mounted!');

    charW = terminalRef.clientWidth / (state.width || 80);
    charH = terminalRef.clientHeight / (state.height || 24);
    bordersW = terminalRef.offsetWidth - terminalRef.clientWidth;
    bordersH = terminalRef.offsetHeight - terminalRef.clientHeight;

    resize();
  });

  onCleanup(() => {
    core.stop()
    stopTimeUpdates();
    cancelAnimationFrame(frameRequestId);
    stopBlinking();
  });

  const play = async () => {
    setState('state', 'loading');

    const timeoutId = setTimeout(() => {
      setState('state', 'waiting');
    }, 1000);

    const { width, height, duration } = await core.start();
    clearTimeout(timeoutId);
    setState('state', 'playing');

    if (state.width) {
      setState('duration', duration);
    } else {
      setState({ duration: duration, width: width, height: height });
      resize(); // make this reactive - createEffect ?
    }

    frameRequestId = requestAnimationFrame(frame);

    startTimeUpdates();
    startBlinking();
  }

  const pauseOrResume = () => {
    const isPlaying = core.pauseOrResume();

    if (isPlaying) {
      setState('state', 'playing');
      startTimeUpdates();
      startBlinking();
    } else {
      setState('state', 'paused');
      updateTime();
      stopTimeUpdates();
      stopBlinking();
    }
  }

  const frame = () => {
    frameRequestId = requestAnimationFrame(frame);

    const cursor = core.getCursor();
    const changedLines = core.getChangedLines();

    batch(() => {
      setState('cursor', reconcile(cursor));

      if (changedLines.size > 0) {
        changedLines.forEach((line, i) => {
          setState('lines', i, reconcile(line));
        })
      }
    });
  }

  const resize = () => {
    const container = wrapperRef;

    console.log(container);
    console.log('resizing terminal');

    const maxTerminalW = container.offsetWidth;
    console.log(`maxTerminalW = ${maxTerminalW}`);

    const newTerminalW = (charW * (state.width || 80)) + bordersW;
    const newTerminalH = (charH * (state.height || 24)) + bordersH;
    const isFullscreen = !!document.fullscreenElement;

    if (props.size == 'fitboth' || isFullscreen) {
      const containerRatio = container.offsetWidth / container.offsetHeight;
      const terminalRatio = newTerminalW / newTerminalH;

      if (containerRatio < terminalRatio) {
        const scale = maxTerminalW / newTerminalW;

        setState({
          terminalScale: scale,
          tw: maxTerminalW,
          th: newTerminalH * scale
        });
      } else {
        const scale = container.offsetHeight / newTerminalH;

        setState({
          terminalScale: scale,
          tw: newTerminalW * scale,
          th: container.offsetHeight
        });
      }
    } else if (props.size == 'fit') {
      const scale = maxTerminalW / newTerminalW;
      console.log(scale);

      setState({
        terminalScale: scale,
        tw: maxTerminalW,
        th: newTerminalH * scale
      });
    } else {
      setState({
        terminalScale: 1,
        tw: 200,
        th: 100
      });
    }
  }

  const toggleFullscreen = () => {
    if (document.fullscreenElement) {
      document.exitFullscreen();
    } else {
      wrapperRef.requestFullscreen().then(resize);
    }
  }

  const onFinish = () => {
    console.log('finished');
    setState('state', 'paused');
    updateTime();
    stopTimeUpdates();
    stopBlinking();
  }

  const onKeyPress = (e) => {
    // TODO
  }

  const startTimeUpdates = () => {
    timeUpdateIntervalId = setInterval(() => {updateTime()}, 100);
  }

  const stopTimeUpdates = () => {
    clearInterval(timeUpdateIntervalId);
  }

  const updateTime = () => {
    let t = core.getCurrentTime();
    let r = core.getRemainingTime();
    let p = core.getProgress();

    setState({ currentTime: t, remainingTime: r, progress: p});
  }

  const startBlinking = () => {
    blinkIntervalId = setInterval(() => {
      setState('blink', blink => !blink);
    }, 500);
  }

  const stopBlinking = () => {
    clearInterval(blinkIntervalId);
    setState('blink', true);
  }

  const showControls = (show) => {
    if (show) {
      clearTimeout(userActivityTimeoutId);
      setState('showControls', true);
      userActivityTimeoutId = setTimeout(() => showControls(false), 2000);
    } else {
      clearTimeout(userActivityTimeoutId);
      setState('showControls', false);
    }
  }

  const playerStyle = () => {
    if (state.tw) {
      return {
        width: `${state.tw}px`,
        height: `${state.th}px`
      }
    } else {
      return {
        height: 0
      }
    }
  }

  // TODO visibility: hidden until loaded/resized
  return (

 showControls(true)} onMouseLeave={() => showControls(false)} onMouseMove={() => showControls(true)}>
        <Terminal width={state.width || 80} height={state.height || 24} scale={state.terminalScale} blink={state.blink} lines={state.lines} cursor={state.cursor} ref={terminalRef} />
        <ControlBar currentTime={state.currentTime} remainingTime={state.remainingTime} progress={state.progress} isPlaying={state.state == 'playing'} isPausable={core.isPausable()} isSeekable={core.isSeekable()} onPlayClick={pauseOrResume} onFullscreenClick={toggleFullscreen} />
        <Switch>
          <Match when={state.state == 'initial'}><StartOverlay onClick={play} /></Match>
          <Match when={state.state == 'waiting'}><LoaderOverlay /></Match>
        </Switch>

  );
}
<commit_msg>Real-time resizing by following the wrapper container<commit_after>import AsciinemaPlayerCore from '../core';
import { batch, createMemo, createState, Match, onCleanup, onMount, reconcile, Switch } from 'solid-js';
import Terminal from './Terminal';
import ControlBar from './ControlBar';
import LoaderOverlay from './LoaderOverlay';
import StartOverlay from './StartOverlay';

export default props => {
  const [state, setState] = createState({
    state: 'initial',
    width: props.cols,
    height: props.rows,
    duration: null,
    lines: [],
    cursor: undefined,
    terminalScale: 1.0,
    showControls: false,
    currentTime: null,
    remainingTime: null,
    progress: null,
    blink: true
  });

  let frameRequestId;
  let userActivityTimeoutId;
  let timeUpdateIntervalId;
  let blinkIntervalId;

  let wrapperRef;
  let terminalRef;

  let resizeObserver;

  const core = AsciinemaPlayerCore.build(props.src, {
    loop: props.loop || false,
    cols: props.cols,
    rows: props.rows
  }, () => onFinish());

  onMount(() => {
    console.log('mounted!');

    setState({
      charW: terminalRef.clientWidth / (state.width || 80),
      charH: terminalRef.clientHeight / (state.height || 24),
      bordersW: terminalRef.offsetWidth - terminalRef.clientWidth,
      bordersH: terminalRef.offsetHeight - terminalRef.clientHeight,
      containerW: wrapperRef.offsetWidth,
      containerH: wrapperRef.offsetHeight
    });

    resizeObserver = new ResizeObserver(_entries => {
      console.log('container resized!')

      setState({
        containerW: wrapperRef.offsetWidth,
        containerH: wrapperRef.offsetHeight
      });
    });

    resizeObserver.observe(wrapperRef);
  });

  onCleanup(() => {
    core.stop()
    stopTimeUpdates();
    cancelAnimationFrame(frameRequestId);
    stopBlinking();
    resizeObserver.disconnect();
  });

  const play = async () => {
    setState('state', 'loading');

    const timeoutId = setTimeout(() => {
      setState('state', 'waiting');
    }, 1000);

    const { width, height, duration } = await core.start();
    clearTimeout(timeoutId);
    setState('state', 'playing');

    if (state.width) {
      setState('duration', duration);
    } else {
      setState({ duration: duration, width: width, height: height });
    }

    frameRequestId = requestAnimationFrame(frame);

    startTimeUpdates();
    startBlinking();
  }

  const pauseOrResume = () => {
    const isPlaying = core.pauseOrResume();

    if (isPlaying) {
      setState('state', 'playing');
      startTimeUpdates();
      startBlinking();
    } else {
      setState('state', 'paused');
      updateTime();
      stopTimeUpdates();
      stopBlinking();
    }
  }

  const frame = () => {
    frameRequestId = requestAnimationFrame(frame);

    const cursor = core.getCursor();
    const changedLines = core.getChangedLines();

    batch(() => {
      setState('cursor', reconcile(cursor));

      if (changedLines.size > 0) {
        changedLines.forEach((line, i) => {
          setState('lines', i, reconcile(line));
        })
      }
    });
  }

  const terminalSize = createMemo(() => {
    console.log('terminalSize');

    if (!state.charW) {
      return {};
    }

    console.log(`containerW = ${state.containerW}`);

    const terminalW = (state.charW * (state.width || 80)) + state.bordersW;
    const terminalH = (state.charH * (state.height || 24)) + state.bordersH;

    if (props.size) {
      let priority = 'width';

      if (props.size == 'fitboth' || !!document.fullscreenElement) {
        const containerRatio = state.containerW / state.containerH;
        const terminalRatio = terminalW / terminalH;

        if (containerRatio > terminalRatio) {
          priority = 'height';
        }
      }

      if (priority == 'width') {
        const scale = state.containerW / terminalW;

        return {
          scale: scale,
          width: state.containerW,
          height: terminalH * scale
        };
      } else {
        const scale = state.containerH / terminalH;

        return {
          scale: scale,
          width: terminalW * scale,
          height: state.containerH
        };
      }
    } else {
      return {
        scale: 1,
        width: 200,
        height: 100
      };
    }
  });

  const toggleFullscreen = () => {
    if (document.fullscreenElement) {
      document.exitFullscreen();
    } else {
      wrapperRef.requestFullscreen();
    }
  }

  const onFinish = () => {
    console.log('finished');
    setState('state', 'paused');
    updateTime();
    stopTimeUpdates();
    stopBlinking();
  }

  const onKeyPress = (e) => {
    // TODO
  }

  const startTimeUpdates = () => {
    timeUpdateIntervalId = setInterval(() => {updateTime()}, 100);
  }

  const stopTimeUpdates = () => {
    clearInterval(timeUpdateIntervalId);
  }

  const updateTime = () => {
    let t = core.getCurrentTime();
    let r = core.getRemainingTime();
    let p = core.getProgress();

    setState({ currentTime: t, remainingTime: r, progress: p});
  }

  const startBlinking = () => {
    blinkIntervalId = setInterval(() => {
      setState('blink', blink => !blink);
    }, 500);
  }

  const stopBlinking = () => {
    clearInterval(blinkIntervalId);
    setState('blink', true);
  }

  const showControls = (show) => {
    if (show) {
      clearTimeout(userActivityTimeoutId);
      setState('showControls', true);
      userActivityTimeoutId = setTimeout(() => showControls(false), 2000);
    } else {
      clearTimeout(userActivityTimeoutId);
      setState('showControls', false);
    }
  }

  const playerStyle = () => {
    const size = terminalSize();

    if (size.width) {
      return {
        width: `${size.width}px`,
        height: `${size.height}px`
      }
    } else {
      return {
        height: 0
      }
    }
  }

  const terminalScale = () => terminalSize().scale;

  // TODO visibility: hidden until loaded/resized
  return (

 showControls(true)} onMouseLeave={() => showControls(false)} onMouseMove={() => showControls(true)}>
        <Terminal width={state.width || 80} height={state.height || 24} scale={terminalScale()} blink={state.blink} lines={state.lines} cursor={state.cursor} ref={terminalRef} />
        <ControlBar currentTime={state.currentTime} remainingTime={state.remainingTime} progress={state.progress} isPlaying={state.state == 'playing'} isPausable={core.isPausable()} isSeekable={core.isSeekable()} onPlayClick={pauseOrResume} onFullscreenClick={toggleFullscreen} />
        <Switch>
          <Match when={state.state == 'initial'}><StartOverlay onClick={play} /></Match>
          <Match when={state.state == 'waiting'}><LoaderOverlay /></Match>
        </Switch>

  );
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import Toolbar from 'kettle-ui/lib/Toolbar'
import ConfirmDialog from 'kettle-ui/lib/ConfirmDialog'
import muiThemeable from 'material-ui/styles/muiThemeable'
import Snackbar from 'material-ui/Snackbar'

const SNACKBAR_AUTOHIDE = 5000

class ChildrenToolbar extends Component {

  getAddButton(parent) {

    if(this.props.isEditable && !this.props.isEditable(parent)) return null

    const items = this.props.getDescriptors ?
      this.props.getDescriptors(parent).map((item) => {
        return Object.assign({}, item, {
          handler:() => {
            this.props.onAdd(this.props.node, item)
          }
        })
      }) :
      []

    return {
      id:'add',
      type:'dropdown',
      title:'Add',
      items
    }
  }

  getLeftButtons() {
    let actions = []
    let leftbuttons = []

    const selected = this.props.selected || []

    if(selected.length==0){
      let addButton = this.getAddButton(this.props.node)
      if(addButton) leftbuttons.push(addButton)
      actions.push({
        id:'edit',
        title:'Edit',
        handler:() => {
          this.props.onEdit && this.props.onEdit(this.props.node)
        }
      })

      if(this.props.clipboard.length>0){
        actions.push({
          id:'paste',
          title:'Paste',
          handler:() => {
            this.props.onPaste && this.props.onPaste(this.props.node, this.props.clipboardMode, this.props.clipboard)
          }
        })
      }
    }
    else if(selected.length==1){

      actions.push({
        id:'open',
        title:'Open',
        handler:() => {
          this.props.onOpen && this.props.onOpen(selected[0])
        }
      })

      actions.push({
        id:'edit',
        title:'Edit',
        handler:() => {
          this.props.onEdit && this.props.onEdit(this.props.node, selected[0])
        }
      })

    }

    if(selected.length>0){
      actions.push({
        id:'delete',
        title:'Delete',
        handler:() => {
          this.props.onDelete && this.props.onDelete(selected)
        }
      },{
        divider:true
      },{
        id:'copy',
        title:'Copy',
        handler:() => {
          this.props.onCopy && this.props.onCopy(selected)
        }
      },{
        id:'cut',
        title:'Cut',
        handler:() => {
          this.props.onCut && this.props.onCut(selected)
        }
      })
    }

    actions = this.props.filterActions ?
      actions = this.props.filterActions(this.getContext(), actions) :
      actions

    if(actions.length>0){
      leftbuttons.push({
        id:'actions',
        type:'dropdown',
        title:'Actions',
        items:actions
      })
    }

    return leftbuttons
  }

  // the info we pass to functions
  getContext() {
    return {
      parent:this.props.node,
      selected:this.props.selected,
      clipboard:this.props.clipboard,
      clipboardMode:this.props.clipboardMode,
      getState:this.props.getState,
      dispatch:this.props.dispatch,
      actions:this.props.actions,
      params:this.props.params
    }
  }

  render() {

    const toolbarChildren = this.props.getChildren ?
      this.props.getChildren(this.getContext()) :
      null

    const icon = this.props.getIcon && this.props.node?
      this.props.getIcon(this.props.node, 'toolbar', this.props.muiTheme) :
      null

    const newProps = Object.assign({}, this.props, {
      leftbuttons:this.getLeftButtons(),
      icon
    })

    const selected = this.props.selected || []

    return (

        <Toolbar {...newProps}>
          {toolbarChildren}
        </Toolbar>
        {
          this.props.deleting ?
            (
              <ConfirmDialog
                confirmTitle="Delete"
                isOpen={true}
                isModal={true}
                onConfirm={() => {
                  this.props.onConfirmDelete(this.props.node, this.props.selected || [])
                }}
                onClose={this.props.onCancelDelete}
                >
                Are you sure you want to delete {this.props.selected.length} item{this.props.selected.length==1 ? '' : 's'}?
              </ConfirmDialog>
            ) : null
        }
        {
          this.props.message ?
            (
              <Snackbar
                open={true}
                message={this.props.message}
                autoHideDuration={SNACKBAR_AUTOHIDE}
                onRequestClose={this.props.onCloseMessage} />
            ) : null
        }

    )
  }

}

export default muiThemeable()(ChildrenToolbar)<commit_msg>use the icon of the selected item<commit_after>import React, { Component, PropTypes } from 'react'
import Toolbar from 'kettle-ui/lib/Toolbar'
import ConfirmDialog from 'kettle-ui/lib/ConfirmDialog'
import muiThemeable from 'material-ui/styles/muiThemeable'
import Snackbar from 'material-ui/Snackbar'

const SNACKBAR_AUTOHIDE = 5000

class ChildrenToolbar extends Component {

  getAddButton(parent) {

    if(this.props.isEditable && !this.props.isEditable(parent)) return null

    const items = this.props.getDescriptors ?
      this.props.getDescriptors(parent).map((item) => {
        return Object.assign({}, item, {
          handler:() => {
            this.props.onAdd(this.props.node, item)
          }
        })
      }) :
      []

    return {
      id:'add',
      type:'dropdown',
      title:'Add',
      items
    }
  }

  getLeftButtons() {
    let actions = []
    let leftbuttons = []

    const selected = this.props.selected || []

    if(selected.length==0){
      let addButton = this.getAddButton(this.props.node)
      if(addButton) leftbuttons.push(addButton)
      actions.push({
        id:'edit',
        title:'Edit',
        handler:() => {
          this.props.onEdit && this.props.onEdit(this.props.node)
        }
      })

      if(this.props.clipboard.length>0){
        actions.push({
          id:'paste',
          title:'Paste',
          handler:() => {
            this.props.onPaste && this.props.onPaste(this.props.node, this.props.clipboardMode, this.props.clipboard)
          }
        })
      }
    }
    else if(selected.length==1){

      actions.push({
        id:'open',
        title:'Open',
        handler:() => {
          this.props.onOpen && this.props.onOpen(selected[0])
        }
      })

      actions.push({
        id:'edit',
        title:'Edit',
        handler:() => {
          this.props.onEdit && this.props.onEdit(this.props.node, selected[0])
        }
      })

    }

    if(selected.length>0){
      actions.push({
        id:'delete',
        title:'Delete',
        handler:() => {
          this.props.onDelete && this.props.onDelete(selected)
        }
      },{
        divider:true
      },{
        id:'copy',
        title:'Copy',
        handler:() => {
          this.props.onCopy && this.props.onCopy(selected)
        }
      },{
        id:'cut',
        title:'Cut',
        handler:() => {
          this.props.onCut && this.props.onCut(selected)
        }
      })
    }

    actions = this.props.filterActions ?
      actions = this.props.filterActions(this.getContext(), actions) :
      actions

    if(actions.length>0){
      leftbuttons.push({
        id:'actions',
        type:'dropdown',
        title:'Actions',
        items:actions
      })
    }

    return leftbuttons
  }

  // the info we pass to functions
  getContext() {
    return {
      parent:this.props.node,
      selected:this.props.selected,
      clipboard:this.props.clipboard,
      clipboardMode:this.props.clipboardMode,
      getState:this.props.getState,
      dispatch:this.props.dispatch,
      actions:this.props.actions,
      params:this.props.params
    }
  }

  render() {

    const toolbarChildren = this.props.getChildren ?
      this.props.getChildren(this.getContext()) :
      null

    let icon = this.props.getIcon && this.props.node && this.props.selected.length == 0 ?
      this.props.getIcon(this.props.node, 'toolbar', this.props.muiTheme) :
      null

    if(this.props.selected.length==1){
      icon = this.props.getIcon(this.props.selected[0], 'toolbar', this.props.muiTheme)
    }

    const newProps = Object.assign({}, this.props, {
      leftbuttons:this.getLeftButtons(),
      icon
    })

    const selected = this.props.selected || []

    return (

        <Toolbar {...newProps}>
          {toolbarChildren}
        </Toolbar>
        {
          this.props.deleting ?
            (
              <ConfirmDialog
                confirmTitle="Delete"
                isOpen={true}
                isModal={true}
                onConfirm={() => {
                  this.props.onConfirmDelete(this.props.node, this.props.selected || [])
                }}
                onClose={this.props.onCancelDelete}
                >
                Are you sure you want to delete {this.props.selected.length} item{this.props.selected.length==1 ? '' : 's'}?
              </ConfirmDialog>
            ) : null
        }
        {
          this.props.message ?
            (
              <Snackbar
                open={true}
                message={this.props.message}
                autoHideDuration={SNACKBAR_AUTOHIDE}
                onRequestClose={this.props.onCloseMessage} />
            ) : null
        }

    )
  }

}

export default muiThemeable()(ChildrenToolbar)<|endoftext|>


<commit_before>import React, {Component} from 'react';

import {validateAge, validateNihssPoints, validateCopeptinLevel} from '../core/coRiskScore';

const INITIAL = Number.MAX_SAFE_INTEGER;

const checkSubmittable = (state) => {
  if (state.ageFormHint || state.nihssFormHint || state.copeptinFormHint || state.age === INITIAL || state.nihss === INITIAL || state.copeptin === INITIAL) {
    return false;
  }
  return true;
}

const getFormHint = (value, validator) => {
  const validationResult = validator(value);
  if (!validationResult.isValid) {
    return validationResult.reason;
  }
  return '';
};

const updateState = (newState) => {
  if (newState.age !== INITIAL) {
    newState.ageFormHint = getFormHint(newState.age, validateAge);
  }

  if (newState.nihss !== INITIAL) {
    newState.nihssFormHint = getFormHint(newState.nihss, validateNihssPoints);
  }

  if (newState.copeptin !== INITIAL) {
    newState.copeptinFormHint = getFormHint(newState.copeptin, validateCopeptinLevel);
  }

  newState.isSubmittable = checkSubmittable(newState);
  return newState;
}

/**
 * Note that the default values are undefined
 */
class CoRiskScoreForm extends Component {

  constructor(props) {
    super(props);

    const {
      age = INITIAL,
      nihss = INITIAL,
      copeptin = INITIAL
    } = props;

    const state = {
      age: age,
      ageFormHint: '',
      nihss: nihss,
      nihssFormHint: '',
      copeptin: copeptin,
      copeptinFormHint: '',
      isSubmittable: false
    };

    const newState = updateState(state);
    this.state = newState;
  }

  setLocalState(newState) {
    newState = updateState(newState);
    this.setState(newState);
  }

  handleAgeChange(event) {
    const newState = {
      ...this.state,
      age: event.target.value
    };

    this.setLocalState(newState);
  }

  handleNihssChange(event) {
    const newState = {
      ...this.state,
      nihss: event.target.value
    };

    this.setLocalState(newState);
  }

  handleCopeptinChange(event) {
    const newState = {
      ...this.state,
      copeptin: event.target.value
    }

    this.setLocalState(newState);
  }

  handleSubmit = (event) => {
    event.preventDefault();
    const state = this.state;

    this
      .props
      .onCalculate({age: state.age, nihss: state.nihss, copeptin: state.copeptin});
  }

  render() {
    return (

The CoRisk Score

            The CoRisk score is the probability of an unfavorable 3-month outcome for
            patients using Copeptin.

            Use the form below to calculate your patient's score. All fields are required.

          <form
            className="form-horizontal"
            action=""
            type="GET"
            onSubmit={this.handleSubmit}>

                Age

                <input
                  className="form-input"
                  name="age"
                  type="number"
                  min="0"
                  max="200"
                  value={this.state.age === INITIAL
                  ? ''
                  : this.state.age}
                  onChange={this
                  .handleAgeChange
                  .bind(this)}/>

                {this.state.ageFormHint}

                NIHSS points

                <input
                  className="form-input"
                  name="nihss"
                  type="number"
                  min="0"
                  max="42"
                  value={this.state.nihss === INITIAL
                  ? ''
                  : this.state.nihss}
                  onChange={this
                  .handleNihssChange
                  .bind(this)}/>

                {this.state.nihssFormHint}

                Copeptin blood level (pmol/L)

                <input
                  className="form-input"
                  name="copeptin"
                  value={this.state.copeptin === INITIAL
                  ? ''
                  : this.state.copeptin}
                  onChange={this
                  .handleCopeptinChange
                  .bind(this)}/>

                {this.state.copeptinFormHint}

                <button
                  className="btn btn-primary btn-lg"
                  disabled={this.state.isSubmittable
                  ? ''
                  : 'disabled'}>
                  Calculate
                </button>

          </form>

    );
  }
}

export default CoRiskScoreForm;
<commit_msg>Refactor to use class properties, reduce re-rendering<commit_after>import React, {Component} from 'react';

import {validateAge, validateNihssPoints, validateCopeptinLevel} from '../core/coRiskScore';

const INITIAL = Number.MAX_SAFE_INTEGER;

const checkSubmittable = (state) => {
  if (state.ageFormHint || state.nihssFormHint || state.copeptinFormHint || state.age === INITIAL || state.nihss === INITIAL || state.copeptin === INITIAL) {
    return false;
  }
  return true;
}

const getFormHint = (value, validator) => {
  const validationResult = validator(value);
  if (!validationResult.isValid) {
    return validationResult.reason;
  }
  return '';
};

const updateState = (newState) => {
  if (newState.age !== INITIAL) {
    newState.ageFormHint = getFormHint(newState.age, validateAge);
  }

  if (newState.nihss !== INITIAL) {
    newState.nihssFormHint = getFormHint(newState.nihss, validateNihssPoints);
  }

  if (newState.copeptin !== INITIAL) {
    newState.copeptinFormHint = getFormHint(newState.copeptin, validateCopeptinLevel);
  }

  newState.isSubmittable = checkSubmittable(newState);
  return newState;
}

/**
 * Note that the default values are undefined
 */
class CoRiskScoreForm extends Component {

  constructor(props) {
    super(props);

    const {
      age = INITIAL,
      nihss = INITIAL,
      copeptin = INITIAL
    } = props;

    const state = {
      age: age,
      ageFormHint: '',
      nihss: nihss,
      nihssFormHint: '',
      copeptin: copeptin,
      copeptinFormHint: '',
      isSubmittable: false
    };

    const newState = updateState(state);
    this.state = newState;
  }

  setLocalState(newState) {
    newState = updateState(newState);
    this.setState(newState);
  }

  handleAgeChange = (event) => {
    const newState = {
      ...this.state,
      age: event.target.value
    };

    this.setLocalState(newState);
  }

  handleNihssChange = (event) => {
    const newState = {
      ...this.state,
      nihss: event.target.value
    };

    this.setLocalState(newState);
  }

  handleCopeptinChange = (event) => {
    const newState = {
      ...this.state,
      copeptin: event.target.value
    }

    this.setLocalState(newState);
  }

  handleSubmit = (event) => {
    event.preventDefault();
    const state = this.state;

    this
      .props
      .onCalculate({age: state.age, nihss: state.nihss, copeptin: state.copeptin});
  }

  render() {
    return (

The CoRisk Score

            The CoRisk score is the probability of an unfavorable 3-month outcome for
            patients using Copeptin.

            Use the form below to calculate your patient's score. All fields are required.

          <form
            className="form-horizontal"
            action=""
            type="GET"
            onSubmit={this.handleSubmit}>

                Age

                <input
                  className="form-input"
                  name="age"
                  type="number"
                  min="0"
                  max="200"
                  value={this.state.age === INITIAL
                  ? ''
                  : this.state.age}
                  onChange={this
                  .handleAgeChange}/>

                {this.state.ageFormHint}

                NIHSS points

                <input
                  className="form-input"
                  name="nihss"
                  type="number"
                  min="0"
                  max="42"
                  value={this.state.nihss === INITIAL
                  ? ''
                  : this.state.nihss}
                  onChange={this
                  .handleNihssChange}/>

                {this.state.nihssFormHint}

                Copeptin blood level (pmol/L)

                <input
                  className="form-input"
                  name="copeptin"
                  value={this.state.copeptin === INITIAL
                  ? ''
                  : this.state.copeptin}
                  onChange={this
                  .handleCopeptinChange}/>

                {this.state.copeptinFormHint}

                <button
                  className="btn btn-primary btn-lg"
                  disabled={this.state.isSubmittable
                  ? ''
                  : 'disabled'}>
                  Calculate
                </button>

          </form>

    );
  }
}

export default CoRiskScoreForm;
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import withIntl from '../lib/withIntl';
import { defineMessages, FormattedMessage } from 'react-intl';
import { union, get, uniqBy } from 'lodash';
import { prettyUrl, formatCurrency, imagePreview } from '../lib/utils';
import { Router } from '../server/pages';
import Currency from './Currency';
import Avatar from './Avatar';
import Logo from './Logo';
import { defaultBackgroundImage } from '../constants/collectives';
import Link from './Link';
import Button from './Button';
import GoalsCover from './GoalsCover';
import MenuBar from './MenuBar';
import TopBackersCoverWithData from './TopBackersCoverWithData';

class CollectiveCover extends React.Component {

  static propTypes = {
    collective: PropTypes.object.isRequired,
    href: PropTypes.string,
    title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    style: PropTypes.object,
    cta: PropTypes.object // { href, label }
  }

  constructor(props) {
    super(props);
    this.messages = defineMessages({
      'contribute': { id: 'collective.contribute', defaultMessage: 'contribute' },
      'apply': { id: 'host.apply.btn', defaultMessage: "Apply to create a collective" },
      'ADMIN': { id: 'roles.admin.label', defaultMessage: 'Core Contributor' },
      'MEMBER': { id: 'roles.member.label', defaultMessage: 'Contributor' }
    });

    if (props.cta) {
      const label = props.cta.label;
      this.cta = { href: props.cta.href, label: this.messages[label] ? props.intl.formatMessage(this.messages[label]) : label };
    }
  }

  getMemberTooltip(member) {
    const { intl } = this.props;
    let tooltip = member.member.name;
    if (this.messages[member.role]) {
      tooltip += `
${intl.formatMessage(this.messages[member.role])}`;
    }
    const description = member.description || member.member.description;
    if (description) {
      tooltip += `
${description}`
    }
    return tooltip;
  }

  render() {
    const {
      collective,
      className,
      LoggedInUser
    } = this.props;

    const {
      company,
      type,
      website,
      twitterHandle,
      stats
    } = collective;

    const href = this.props.href || collective.path || `/${collective.slug}`;
    const title = this.props.title || collective.name;
    const description = this.props.description || collective.description;
    const formattedYearlyIncome = stats && stats.yearlyBudget > 0 && formatCurrency(stats.yearlyBudget, collective.currency, { precision: 0 });
    const backgroundImage = imagePreview(collective.backgroundImage || get(collective,'parentCollective.backgroundImage'), defaultBackgroundImage[collective.type], { height: 500 });
    const customStyles = get(collective, 'settings.style.hero.cover') || get(collective.parentCollective, 'settings.style.hero.cover');
    const style = {
      backgroundImage: `url('${backgroundImage}')`,
      backgroundPosition: 'center center',
      backgroundSize: 'cover',
      backgroundRepeat: 'no-repeat',
      ...customStyles
    };

    const logo = collective.image || get(collective.parentCollective, 'image');

    let membersPreview = [];
    if (collective.members) {
      const admins = collective.members.filter(m => m.role === 'ADMIN');
      const members = collective.members.filter(m => m.role === 'MEMBER');
      const backers = collective.members.filter(m => m.role === 'BACKER');
      backers.sort((a, b) => b.stats && b.stats.totalDonations - a.stats && a.stats.totalDonations);
      membersPreview = uniqBy(union(admins, members, backers).filter(m => m.member), m => m.member.id).slice(0, 5);
    }
    const additionalBackers = (get(stats, 'backers.all') || (get(collective, 'members') || []).length) - membersPreview.length;

    return (
      <div className={`CollectiveCover ${className} ${type}`}>

            <Link route={href} className="goBack">
              { collective.type === 'USER' && <Avatar src={logo} className="logo" radius="10rem" /> }
              { collective.type !== 'USER' && <Logo src={logo} className="logo" type={collective.type} website={collective.website} height="10rem" /> }
            </Link>

{title}

            { company && company.substr(0,1) === '@' &&
<Link route={`/${company.substr(1)}`}>{company.substr(1)}</Link>
 }
            { company && company.substr(0,1) !== '@' &&
{company}
 }
            { description &&
{description}
 }
            { collective.type !== 'EVENT' &&

                { collective.host && collective.isActive &&
<FormattedMessage id="collective.cover.hostedBy" defaultMessage="Hosted by" /><Link route={`/${collective.host.slug}`}>{collective.host.name} </Link>
 }
                { collective.host && !collective.isActive &&
<FormattedMessage id="collective.cover.pendingApprovalFrom" defaultMessage="Pending approval from" /><Link route={`/${collective.host.slug}`}>{collective.host.name} </Link>
 }
                { twitterHandle &&
<a href={`https://twitter.com/${twitterHandle}`} target="_blank">@{twitterHandle}
 }
                { website &&
{prettyUrl(website) }
 }

            }
            { collective.type === 'EVENT' &&

                  <Link route={`/${collective.parentCollective.slug}`}>{collective.parentCollective.name}</Link>

            }

            { collective.type !== 'COLLECTIVE' && this.props.cta &&

                <Button className="blue" href={this.cta.href}>{this.cta.label}</Button>

            }

          { ['USER','ORGANIZATION'].indexOf(collective.type) !== -1 && stats && stats.totalAmountSent > 0 && !collective.isHost &&

                  <Currency value={stats.totalAmountSent} currency={collective.currency} />

                <FormattedMessage id="collective.stats.totalAmountSent.label" defaultMessage="Total amount donated" />

              { stats.totalAmountRaised > 0 &&

                    <Currency value={stats.totalAmountRaised} currency={collective.currency} />

                  <FormattedMessage id="collective.stats.totalAmountRaised.label" defaultMessage="Total amount raised" />

              }

          }

          { collective.type === 'COLLECTIVE' &&

              { className !== "small" && collective.type === "COLLECTIVE" &&

                  <TopBackersCoverWithData
                    collective={this.props.collective}
                    LoggedInUser={LoggedInUser}
                    limit={10}
                    />

              }

              { className !== "small" && collective.type === "COLLECTIVE" &&
                <GoalsCover
                  collective={collective}
                  LoggedInUser={LoggedInUser}
                  />
              }

              { this.props.cta &&
                <Button className="blue" href={this.cta.href}>{this.cta.label}</Button>
              }

          }

        { className !== "small" &&
          <MenuBar
            collective={collective}
            LoggedInUser={LoggedInUser}
            cta={this.cta}
            />
        }

    );
  }
}

export default withIntl(CollectiveCover);
<commit_msg>small css fix for user stats on user profile page<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import withIntl from '../lib/withIntl';
import { defineMessages, FormattedMessage } from 'react-intl';
import { union, get, uniqBy } from 'lodash';
import { prettyUrl, formatCurrency, imagePreview } from '../lib/utils';
import { Router } from '../server/pages';
import Currency from './Currency';
import Avatar from './Avatar';
import Logo from './Logo';
import { defaultBackgroundImage } from '../constants/collectives';
import Link from './Link';
import Button from './Button';
import GoalsCover from './GoalsCover';
import MenuBar from './MenuBar';
import TopBackersCoverWithData from './TopBackersCoverWithData';

class CollectiveCover extends React.Component {

  static propTypes = {
    collective: PropTypes.object.isRequired,
    href: PropTypes.string,
    title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    style: PropTypes.object,
    cta: PropTypes.object // { href, label }
  }

  constructor(props) {
    super(props);
    this.messages = defineMessages({
      'contribute': { id: 'collective.contribute', defaultMessage: 'contribute' },
      'apply': { id: 'host.apply.btn', defaultMessage: "Apply to create a collective" },
      'ADMIN': { id: 'roles.admin.label', defaultMessage: 'Core Contributor' },
      'MEMBER': { id: 'roles.member.label', defaultMessage: 'Contributor' }
    });

    if (props.cta) {
      const label = props.cta.label;
      this.cta = { href: props.cta.href, label: this.messages[label] ? props.intl.formatMessage(this.messages[label]) : label };
    }
  }

  getMemberTooltip(member) {
    const { intl } = this.props;
    let tooltip = member.member.name;
    if (this.messages[member.role]) {
      tooltip += `
${intl.formatMessage(this.messages[member.role])}`;
    }
    const description = member.description || member.member.description;
    if (description) {
      tooltip += `
${description}`
    }
    return tooltip;
  }

  render() {
    const {
      collective,
      className,
      LoggedInUser
    } = this.props;

    const {
      company,
      type,
      website,
      twitterHandle,
      stats
    } = collective;

    const href = this.props.href || collective.path || `/${collective.slug}`;
    const title = this.props.title || collective.name;
    const description = this.props.description || collective.description;
    const formattedYearlyIncome = stats && stats.yearlyBudget > 0 && formatCurrency(stats.yearlyBudget, collective.currency, { precision: 0 });
    const backgroundImage = imagePreview(collective.backgroundImage || get(collective,'parentCollective.backgroundImage'), defaultBackgroundImage[collective.type], { height: 500 });
    const customStyles = get(collective, 'settings.style.hero.cover') || get(collective.parentCollective, 'settings.style.hero.cover');
    const style = {
      backgroundImage: `url('${backgroundImage}')`,
      backgroundPosition: 'center center',
      backgroundSize: 'cover',
      backgroundRepeat: 'no-repeat',
      ...customStyles
    };

    const logo = collective.image || get(collective.parentCollective, 'image');

    let membersPreview = [];
    if (collective.members) {
      const admins = collective.members.filter(m => m.role === 'ADMIN');
      const members = collective.members.filter(m => m.role === 'MEMBER');
      const backers = collective.members.filter(m => m.role === 'BACKER');
      backers.sort((a, b) => b.stats && b.stats.totalDonations - a.stats && a.stats.totalDonations);
      membersPreview = uniqBy(union(admins, members, backers).filter(m => m.member), m => m.member.id).slice(0, 5);
    }
    const additionalBackers = (get(stats, 'backers.all') || (get(collective, 'members') || []).length) - membersPreview.length;

    return (
      <div className={`CollectiveCover ${className} ${type}`}>

            <Link route={href} className="goBack">
              { collective.type === 'USER' && <Avatar src={logo} className="logo" radius="10rem" /> }
              { collective.type !== 'USER' && <Logo src={logo} className="logo" type={collective.type} website={collective.website} height="10rem" /> }
            </Link>

{title}

            { company && company.substr(0,1) === '@' &&
<Link route={`/${company.substr(1)}`}>{company.substr(1)}</Link>
 }
            { company && company.substr(0,1) !== '@' &&
{company}
 }
            { description &&
{description}
 }
            { collective.type !== 'EVENT' &&

                { collective.host && collective.isActive &&
<FormattedMessage id="collective.cover.hostedBy" defaultMessage="Hosted by" /><Link route={`/${collective.host.slug}`}>{collective.host.name} </Link>
 }
                { collective.host && !collective.isActive &&
<FormattedMessage id="collective.cover.pendingApprovalFrom" defaultMessage="Pending approval from" /><Link route={`/${collective.host.slug}`}>{collective.host.name} </Link>
 }
                { twitterHandle &&
<a href={`https://twitter.com/${twitterHandle}`} target="_blank">@{twitterHandle}
 }
                { website &&
{prettyUrl(website) }
 }

            }
            { collective.type === 'EVENT' &&

                  <Link route={`/${collective.parentCollective.slug}`}>{collective.parentCollective.name}</Link>

            }

            { collective.type !== 'COLLECTIVE' && this.props.cta &&

                <Button className="blue" href={this.cta.href}>{this.cta.label}</Button>

            }

          { ['USER','ORGANIZATION'].indexOf(collective.type) !== -1 && stats && stats.totalAmountSent > 0 && !collective.isHost &&

                  <Currency value={stats.totalAmountSent} currency={collective.currency} />

                <FormattedMessage id="collective.stats.totalAmountSent.label" defaultMessage="Total amount donated" />

              { stats.totalAmountRaised > 0 &&

                    <Currency value={stats.totalAmountRaised} currency={collective.currency} />

                  <FormattedMessage id="collective.stats.totalAmountRaised.label" defaultMessage="Total amount raised" />

              }

          }

          { collective.type === 'COLLECTIVE' &&

              { className !== "small" && collective.type === "COLLECTIVE" &&

                  <TopBackersCoverWithData
                    collective={this.props.collective}
                    LoggedInUser={LoggedInUser}
                    limit={10}
                    />

              }

              { className !== "small" && collective.type === "COLLECTIVE" &&
                <GoalsCover
                  collective={collective}
                  LoggedInUser={LoggedInUser}
                  />
              }

              { this.props.cta &&
                <Button className="blue" href={this.cta.href}>{this.cta.label}</Button>
              }

          }

        { className !== "small" &&
          <MenuBar
            collective={collective}
            LoggedInUser={LoggedInUser}
            cta={this.cta}
            />
        }

    );
  }
}

export default withIntl(CollectiveCover);
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'

import { OverlayTrigger, Popover } from 'react-bootstrap'

import style from './style.css'

/**
 * Renders underlined `text` with `tooltip`.
 * A popover is shown consisting of `title` and `content` on click.
 */
class FieldHelp extends React.Component {

  render () {
    console.log('help component', this)

    const tooltip = this.props.tooltip || 'Click for help'

    const popover = (
      <Popover id='popover-positioned-top' title={this.props.title}>
        {this.props.content}
      </Popover>)

    return (
      <OverlayTrigger trigger='click' rootClose placement='top' overlay={popover} container={this}>

          {this.props.text}
          {this.props.children}

      </OverlayTrigger>
    )
  }
}

FieldHelp.propTypes = {
  title: PropTypes.string,                                            // popover title
  content: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), // popover content
  text: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),    // decorated text
  tooltip: PropTypes.string,                                          // tooltip shown when hovering the text
  children: PropTypes.any,
}

export default FieldHelp
<commit_msg>Remove unsused log message<commit_after>import React from 'react'
import PropTypes from 'prop-types'

import { OverlayTrigger, Popover } from 'react-bootstrap'

import style from './style.css'

/**
 * Renders underlined `text` with `tooltip`.
 * A popover is shown consisting of `title` and `content` on click.
 */
class FieldHelp extends React.Component {

  render () {
    const tooltip = this.props.tooltip || 'Click for help'

    const popover = (
      <Popover id='popover-positioned-top' title={this.props.title}>
        {this.props.content}
      </Popover>)

    return (
      <OverlayTrigger trigger='click' rootClose placement='top' overlay={popover} container={this}>

          {this.props.text}
          {this.props.children}

      </OverlayTrigger>
    )
  }
}

FieldHelp.propTypes = {
  title: PropTypes.string,                                            // popover title
  content: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), // popover content
  text: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),    // decorated text
  tooltip: PropTypes.string,                                          // tooltip shown when hovering the text
  children: PropTypes.any,
}

export default FieldHelp
<|endoftext|>


<commit_before>import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import {
    RouteContext, History
}
from 'react-router';
import {
    ipcRenderer
}
from 'electron';
import {
    mouseTrap
}
from 'react-mousetrap';
import plugins from '../utils/plugins';
import Modal from './Modal';
import DarkModal from './Player/components/Modal';
import Message from './Message';
import Header from './Header';
import historyActions from '../actions/historyActions';
import traktUtil from './Player/utils/trakt';
import filmonUtil from './Player/utils/filmon';
import request from 'request';
import subUtil from './Player/utils/subtitles';
import updater from './Player/utils/updates';
import remote from 'remote';
import clArgs from '../utils/clArgs';
import ls from 'local-storage';
import Promise from 'bluebird';
import _ from 'lodash';

const Framework = React.createClass({

    mixins: [PureRenderMixin, RouteContext, History],

    componentWillMount() {

        plugins.update();

        Promise.config({
            warnings: {
                wForgottenReturn: false
            }
        });

		updater.checkUpdates();

        if (!ls.isSet('ytdlQuality')) ls('ytdlQuality', 2);
        if (!ls.isSet('renderFreq')) ls('renderFreq', 500);
        if (!ls.isSet('renderHidden')) ls('renderHidden', true);
        if (!ls.isSet('subEncoding')) ls('subEncoding', 'auto');
        if (!ls.isSet('peerPort')) ls('peerPort', 6881);
        if (!ls.isSet('maxPeers')) ls('maxPeers', 200);
        if (!ls.isSet('bufferSize')) ls('bufferSize', 7000);
        if (!ls.isSet('removeLogic')) ls('removeLogic', 0);
        if (!ls.isSet('downloadType')) ls('downloadType', 0);
        if (!ls.isSet('playerType')) ls('playerType', false);
        if (!ls.isSet('adultContent')) ls('adultContent', false);
        if (!ls.isSet('myFilmonPlugins')) ls('myFilmonPlugins', []);

        filmonUtil.init();

        this.props.bindShortcut('ctrl+d', () => ipcRenderer.send('app:toggleDevTools'));

        window.addEventListener('mouseup', function() {
            // removes polymer's element focus which hijacks my enter / space hotkeys
            _.delay(() => {
                if (document.activeElement.tagName != "INPUT")
                    document.querySelector('body').focus();
            }, 500);
        });

        subUtil.fetchOsCookie(true);

        historyActions.history(this.history);
        this.history.listen(this.updatehistory);
    },

    componentDidMount() {
        ipcRenderer.send('app:startup', new Date().getTime());

        request('https://www.google.com'); // Connect once to avoid cloggage

        // login trakt
        if (ls('traktTokens'))
            traktUtil.autoLogin();

        if (remote.process.argv.length > 1) {
            // load command line args
            var args = remote.process.argv;
            args.shift();
            clArgs.process(args);
        }
    },

    componentWillUnmount() {
        window.removeEventListener('resize', this.handleResize);
    },

    updatehistory() {
        historyActions.history(this.history);
    },

    render() {
        return (

              {React.cloneElement(this.props.children, {query: this.props.query})}
              <Modal />
              <DarkModal />
              <Message />
              <canvas id="fake-canvas" style={{display: 'none'}} />

        );
    }
});

export
default mouseTrap(Framework)<commit_msg>Indent<commit_after>import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import {
    RouteContext, History
}
from 'react-router';
import {
    ipcRenderer
}
from 'electron';
import {
    mouseTrap
}
from 'react-mousetrap';
import plugins from '../utils/plugins';
import Modal from './Modal';
import DarkModal from './Player/components/Modal';
import Message from './Message';
import Header from './Header';
import historyActions from '../actions/historyActions';
import traktUtil from './Player/utils/trakt';
import filmonUtil from './Player/utils/filmon';
import request from 'request';
import subUtil from './Player/utils/subtitles';
import updater from './Player/utils/updates';
import remote from 'remote';
import clArgs from '../utils/clArgs';
import ls from 'local-storage';
import Promise from 'bluebird';
import _ from 'lodash';

const Framework = React.createClass({

    mixins: [PureRenderMixin, RouteContext, History],

    componentWillMount() {

        plugins.update();

        Promise.config({
            warnings: {
                wForgottenReturn: false
            }
        });

        updater.checkUpdates();

        if (!ls.isSet('ytdlQuality')) ls('ytdlQuality', 2);
        if (!ls.isSet('renderFreq')) ls('renderFreq', 500);
        if (!ls.isSet('renderHidden')) ls('renderHidden', true);
        if (!ls.isSet('subEncoding')) ls('subEncoding', 'auto');
        if (!ls.isSet('peerPort')) ls('peerPort', 6881);
        if (!ls.isSet('maxPeers')) ls('maxPeers', 200);
        if (!ls.isSet('bufferSize')) ls('bufferSize', 7000);
        if (!ls.isSet('removeLogic')) ls('removeLogic', 0);
        if (!ls.isSet('downloadType')) ls('downloadType', 0);
        if (!ls.isSet('playerType')) ls('playerType', false);
        if (!ls.isSet('adultContent')) ls('adultContent', false);
        if (!ls.isSet('myFilmonPlugins')) ls('myFilmonPlugins', []);

        filmonUtil.init();

        this.props.bindShortcut('ctrl+d', () => ipcRenderer.send('app:toggleDevTools'));

        window.addEventListener('mouseup', function() {
            // removes polymer's element focus which hijacks my enter / space hotkeys
            _.delay(() => {
                if (document.activeElement.tagName != "INPUT")
                    document.querySelector('body').focus();
            }, 500);
        });

        subUtil.fetchOsCookie(true);

        historyActions.history(this.history);
        this.history.listen(this.updatehistory);
    },

    componentDidMount() {
        ipcRenderer.send('app:startup', new Date().getTime());

        request('https://www.google.com'); // Connect once to avoid cloggage

        // login trakt
        if (ls('traktTokens'))
            traktUtil.autoLogin();

        if (remote.process.argv.length > 1) {
            // load command line args
            var args = remote.process.argv;
            args.shift();
            clArgs.process(args);
        }
    },

    componentWillUnmount() {
        window.removeEventListener('resize', this.handleResize);
    },

    updatehistory() {
        historyActions.history(this.history);
    },

    render() {
        return (

              {React.cloneElement(this.props.children, {query: this.props.query})}
              <Modal />
              <DarkModal />
              <Message />
              <canvas id="fake-canvas" style={{display: 'none'}} />

        );
    }
});

export
default mouseTrap(Framework)<|endoftext|>


<commit_before>import React, { Component, PropTypes as pt } from 'react';
import { connect } from 'react-redux';
import { mapStateToProps } from 'containers/Archive/ArchiveSelectors';
import Modal from 'react-modal';
import findIndex from 'lodash/findIndex';
import { withRouter } from 'react-router';
import { Picture } from 'components';
import {
  MODAL_STYLES,
  IMG_STYLES,
  IMG_CONTAINER_STYLES,
  PREV_DEFAULT,
  CURRENT_DEFAULT,
  NEXT_DEFAULT,
  SUMMARY_STYLES,
  SUMMARY_CONTAINER_STYLES
} from './Gallery.styles';
import Swipeable from 'react-swipeable';

@withRouter
@connect(mapStateToProps, {})
export default class Gallery extends Component {

  static propTypes = {
    history: pt.object.isRequired,
    params: pt.object.isRequired,
    slides: pt.array.isRequired,
    router: pt.object.isRequired,
  }

  constructor(...args) {
    super(...args);
    const index = findIndex(this.props.slides, slide =>
      parseInt(this.props.params.index, 10) === slide.id
    );
    this.state = {
      goToCurrent: false,
      goToNext: false,
      goToPrev: false,
      swipe: 0,
      transitionDuration: 0.3,
      showSummary: false,
      index,
    };
  }

  componentDidMount() {
    this.showSummary();
  }

  handleRequestClose = () => this.props.router.goBack()

  handleSwiping = (e, abs) => {
    this.setState({
      swipe: abs,
    });
  }

  showSummary = () => {
    clearTimeout(this.summaryTimeout);
    this.summaryTimeout = setTimeout(() => {
      this.setState({
        showSummary: true,
      });
    }, 500);
  }

  handleSwiped = (e, abs) => {
    const {
      index,
      transitionDuration,
    } = this.state;
    const timeout = transitionDuration * 1000;
    this.setState({
      swipe: 0
    });
    clearTimeout(this.summaryTimeout);
    if (abs > 50) {
      this.setState({
        goToNext: true,
        showSummary: false,
      });
      setTimeout(() => {
        const actualIndex = index + 1 === this.props.slides.length ? 0 : index + 1;
        this.setState({
          goToNext: false,
          index: actualIndex
        });
        this.showSummary();
      }, timeout);
    } else if (abs < -50) {
      this.setState({
        goToPrev: true,
        showSummary: false,
      });
      setTimeout(() => {
        const actualIndex = index - 1 < 0 ? this.props.slides.length - 1 : index - 1;
        this.setState({
          goToPrev: false,
          index: actualIndex
        });
        this.showSummary();
      }, timeout);
    } else {
      this.setState({
        goToCurrent: true,
      });
      setTimeout(() => {
        this.setState({
          goToCurrent: false,
        });
      }, timeout);
    }
  }

  render() {
    const {
      handleSwiping,
      handleSwiped,
      handleRequestClose,
      props: {
        slides
      },
      state: {
        swipe,
        index,
        goToPrev,
        goToNext,
        goToCurrent,
        transitionDuration,
        showSummary,
      }
    } = this;
    let firstSlideStyle;
    let secondSlideStyle;
    let thirdSlideStyle;
    firstSlideStyle = secondSlideStyle = thirdSlideStyle = {};
    const TRANSITION_STYLES = {
      transitionProperty: 'transform',
      transitionDuration: `${transitionDuration}s`
    };
    switch (true) {
      case goToPrev:
        firstSlideStyle = {
          ...TRANSITION_STYLES,
          ...CURRENT_DEFAULT,
          zIndex: 2
        };
        secondSlideStyle = {
          ...TRANSITION_STYLES,
          ...NEXT_DEFAULT,
        };
        thirdSlideStyle = {
          display: 'none'
        };
        break;
      case goToNext:
        firstSlideStyle = {
          display: 'none'
        };
        secondSlideStyle = {
          ...TRANSITION_STYLES,
          ...PREV_DEFAULT,
        };
        thirdSlideStyle = {
          ...TRANSITION_STYLES,
          ...CURRENT_DEFAULT,

        };
        break;
      case goToCurrent:
        firstSlideStyle = {
          ...TRANSITION_STYLES,
          ...PREV_DEFAULT,
        };
        secondSlideStyle = {
          ...TRANSITION_STYLES,
          ...CURRENT_DEFAULT,
        };
        thirdSlideStyle = {
          ...TRANSITION_STYLES,
          ...NEXT_DEFAULT,
        };

        break;
      default:
        firstSlideStyle = {
          zIndex: swipe < 0 ? 2 : 0,
          transform: `rotateY(${-110 + (-swipe / 2)}deg)`,
        };
        secondSlideStyle = {
          transform: `rotateY(${-swipe / 2}deg)`,
        };
        thirdSlideStyle = {
          transform: `rotateY(${110 + (-swipe / 2)}deg)`,
        };
        break;
      // default:
      //   break;

    }
    const prevIndex = index - 1 < 0 ? this.props.slides.length - 1 : index - 1;
    const prevPreload1 = prevIndex - 1 < 0 ? this.props.slides.length - 1 : prevIndex - 1;
    const nextIndex = index + 1 === this.props.slides.length ? 0 : index + 1;
    const preload1 = nextIndex + 1 === this.props.slides.length ? 0 : nextIndex + 1;
    const preload2 = preload1 + 1 === this.props.slides.length ? 0 : preload1 + 1;
    return (
      <Modal
        isOpen
        onRequestClose={handleRequestClose}
        style={MODAL_STYLES}
      >
        <Swipeable
          onSwiping={handleSwiping}
          onSwiped={handleSwiped}
          style={{
            position: 'relative',
            width: '100%',
            height: '100%',
            perspective: '1000px',
            transformOrigin: 'left',
          }}
        >

            <Picture
              src={slides[prevIndex].url}
              ratio={slides[prevIndex].ratio}
              style={{
                ...IMG_STYLES,
              }}

            />

            <Picture
              src={slides[index].url}
              ratio={slides[index].ratio}
              style={{
                ...IMG_STYLES,
              }}
            />

            <Picture
              src={slides[nextIndex].url}
              ratio={slides[nextIndex].ratio}
              style={{
                ...IMG_STYLES,
              }}

            />

{slides[index].summary}

          <Picture
            src={slides[preload1].url}
            ratio={slides[preload1].ratio}
            style={{
              ...IMG_STYLES,
              opacity: 0,
            }}
          />
          <Picture
            src={slides[preload2].url}
            ratio={slides[preload2].ratio}
            style={{
              ...IMG_STYLES,
              opacity: 0,
              height: 0,
              width: 0,
            }}
          />
          <Picture
            src={slides[prevPreload1].url}
            ratio={slides[prevPreload1].ratio}
            style={{
              ...IMG_STYLES,
              opacity: 0,
              height: 0,
              width: 0,
            }}
          />

        </Swipeable>

      </Modal>

    );
  }
}
<commit_msg>Disabled scrolling.<commit_after>import React, { Component, PropTypes as pt } from 'react';
import { connect } from 'react-redux';
import { mapStateToProps } from 'containers/Archive/ArchiveSelectors';
import Modal from 'react-modal';
import findIndex from 'lodash/findIndex';
import { withRouter } from 'react-router';
import { Picture } from 'components';
import {
  MODAL_STYLES,
  IMG_STYLES,
  IMG_CONTAINER_STYLES,
  PREV_DEFAULT,
  CURRENT_DEFAULT,
  NEXT_DEFAULT,
  SUMMARY_STYLES,
  SUMMARY_CONTAINER_STYLES
} from './Gallery.styles';
import Swipeable from 'react-swipeable';

@withRouter
@connect(mapStateToProps, {})
export default class Gallery extends Component {

  static propTypes = {
    history: pt.object.isRequired,
    params: pt.object.isRequired,
    slides: pt.array.isRequired,
    router: pt.object.isRequired,
  }

  constructor(...args) {
    super(...args);
    const index = findIndex(this.props.slides, slide =>
      parseInt(this.props.params.index, 10) === slide.id
    );
    this.state = {
      goToCurrent: false,
      goToNext: false,
      goToPrev: false,
      swipe: 0,
      transitionDuration: 0.3,
      showSummary: false,
      index,
    };
  }

  componentDidMount() {
    this.showSummary();
  }

  handleRequestClose = () => this.props.router.goBack()

  handleSwiping = (e, abs) => {
    e.preventDefault();
    this.setState({
      swipe: abs,
    });
  }

  showSummary = () => {
    clearTimeout(this.summaryTimeout);
    this.summaryTimeout = setTimeout(() => {
      this.setState({
        showSummary: true,
      });
    }, 500);
  }

  handleSwiped = (e, abs) => {
    const {
      index,
      transitionDuration,
    } = this.state;
    const timeout = transitionDuration * 1000;
    this.setState({
      swipe: 0
    });
    clearTimeout(this.summaryTimeout);
    if (abs > 50) {
      this.setState({
        goToNext: true,
        showSummary: false,
      });
      setTimeout(() => {
        const actualIndex = index + 1 === this.props.slides.length ? 0 : index + 1;
        this.setState({
          goToNext: false,
          index: actualIndex
        });
        this.showSummary();
      }, timeout);
    } else if (abs < -50) {
      this.setState({
        goToPrev: true,
        showSummary: false,
      });
      setTimeout(() => {
        const actualIndex = index - 1 < 0 ? this.props.slides.length - 1 : index - 1;
        this.setState({
          goToPrev: false,
          index: actualIndex
        });
        this.showSummary();
      }, timeout);
    } else {
      this.setState({
        goToCurrent: true,
      });
      setTimeout(() => {
        this.setState({
          goToCurrent: false,
        });
      }, timeout);
    }
  }

  render() {
    const {
      handleSwiping,
      handleSwiped,
      handleRequestClose,
      props: {
        slides
      },
      state: {
        swipe,
        index,
        goToPrev,
        goToNext,
        goToCurrent,
        transitionDuration,
        showSummary,
      }
    } = this;
    let firstSlideStyle;
    let secondSlideStyle;
    let thirdSlideStyle;
    firstSlideStyle = secondSlideStyle = thirdSlideStyle = {};
    const TRANSITION_STYLES = {
      transitionProperty: 'transform',
      transitionDuration: `${transitionDuration}s`
    };
    switch (true) {
      case goToPrev:
        firstSlideStyle = {
          ...TRANSITION_STYLES,
          ...CURRENT_DEFAULT,
          zIndex: 2
        };
        secondSlideStyle = {
          ...TRANSITION_STYLES,
          ...NEXT_DEFAULT,
        };
        thirdSlideStyle = {
          display: 'none'
        };
        break;
      case goToNext:
        firstSlideStyle = {
          display: 'none'
        };
        secondSlideStyle = {
          ...TRANSITION_STYLES,
          ...PREV_DEFAULT,
        };
        thirdSlideStyle = {
          ...TRANSITION_STYLES,
          ...CURRENT_DEFAULT,

        };
        break;
      case goToCurrent:
        firstSlideStyle = {
          ...TRANSITION_STYLES,
          ...PREV_DEFAULT,
        };
        secondSlideStyle = {
          ...TRANSITION_STYLES,
          ...CURRENT_DEFAULT,
        };
        thirdSlideStyle = {
          ...TRANSITION_STYLES,
          ...NEXT_DEFAULT,
        };

        break;
      default:
        firstSlideStyle = {
          zIndex: swipe < 0 ? 2 : 0,
          transform: `rotateY(${-110 + (-swipe / 2)}deg)`,
        };
        secondSlideStyle = {
          transform: `rotateY(${-swipe / 2}deg)`,
        };
        thirdSlideStyle = {
          transform: `rotateY(${110 + (-swipe / 2)}deg)`,
        };
        break;
      // default:
      //   break;

    }
    const prevIndex = index - 1 < 0 ? this.props.slides.length - 1 : index - 1;
    const prevPreload1 = prevIndex - 1 < 0 ? this.props.slides.length - 1 : prevIndex - 1;
    const nextIndex = index + 1 === this.props.slides.length ? 0 : index + 1;
    const preload1 = nextIndex + 1 === this.props.slides.length ? 0 : nextIndex + 1;
    const preload2 = preload1 + 1 === this.props.slides.length ? 0 : preload1 + 1;
    return (
      <Modal
        isOpen
        onRequestClose={handleRequestClose}
        style={MODAL_STYLES}
      >
        <Swipeable
          onSwiping={handleSwiping}
          onSwiped={handleSwiped}
          style={{
            position: 'relative',
            width: '100%',
            height: '100%',
            perspective: '1000px',
            transformOrigin: 'left',
          }}
        >

            <Picture
              src={slides[prevIndex].url}
              ratio={slides[prevIndex].ratio}
              style={{
                ...IMG_STYLES,
              }}

            />

            <Picture
              src={slides[index].url}
              ratio={slides[index].ratio}
              style={{
                ...IMG_STYLES,
              }}
            />

            <Picture
              src={slides[nextIndex].url}
              ratio={slides[nextIndex].ratio}
              style={{
                ...IMG_STYLES,
              }}

            />

{slides[index].summary}

          <Picture
            src={slides[preload1].url}
            ratio={slides[preload1].ratio}
            style={{
              ...IMG_STYLES,
              opacity: 0,
            }}
          />
          <Picture
            src={slides[preload2].url}
            ratio={slides[preload2].ratio}
            style={{
              ...IMG_STYLES,
              opacity: 0,
              height: 0,
              width: 0,
            }}
          />
          <Picture
            src={slides[prevPreload1].url}
            ratio={slides[prevPreload1].ratio}
            style={{
              ...IMG_STYLES,
              opacity: 0,
              height: 0,
              width: 0,
            }}
          />

        </Swipeable>

      </Modal>

    );
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { useListContext } from '../context';
import ColumnChooserButton from '../../Toolbar/ColumnChooserButton';

function ColumnChooser(props) {
	const { columns, visibleColumns, setVisibleColumns } = useListContext();

	return (
		<ColumnChooserButton
			{...props}
			columns={columns.map(({ dataKey, label }, i) => ({
				key: dataKey,
				label,
				hidden: !visibleColumns?.includes(dataKey),
				order: i + 1,
			}))}
			onSubmit={(_, changes) => {
				setVisibleColumns(changes.filter(c => !c.hidden).map(c => c.key));
				if (props.onSubmit) {
					props.onSubmit(_, changes);
				}
			}}
		/>
	);
}

ColumnChooser.propTypes = {
	onSubmit: PropTypes.func,
};

export default ColumnChooser;
<commit_msg>fix(List/ColumnChooser): let's defined its own columns (#3092)<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { useListContext } from '../context';
import ColumnChooserButton from '../../Toolbar/ColumnChooserButton';

function ColumnChooser(props) {
	const { columns, visibleColumns, setVisibleColumns } = useListContext();

	return (
		<ColumnChooserButton
			columns={columns.map(({ dataKey, label }, i) => ({
				key: dataKey,
				label,
				hidden: !visibleColumns?.includes(dataKey),
				order: i + 1,
			}))}
			{...props}
			onSubmit={(_, changes) => {
				setVisibleColumns(changes.filter(c => !c.hidden).map(c => c.key));
				if (props.onSubmit) {
					props.onSubmit(_, changes);
				}
			}}
		/>
	);
}

ColumnChooser.propTypes = {
	onSubmit: PropTypes.func,
};

export default ColumnChooser;
<|endoftext|>