<commit_before>// @flow

import React from 'react';
import type { ComponentType, Element, Node } from 'react';
import classNames from 'classnames';
import EventListener from 'react-event-listener';
import withStyles from '../styles/withStyles';
import { duration } from '../styles/transitions';
import ClickAwayListener from '../internal/ClickAwayListener';
import { capitalizeFirstLetter, createChainedFunction } from '../utils/helpers';
import Slide from '../transitions/Slide';
import SnackbarContent from './SnackbarContent';
import type { TransitionDuration, TransitionCallback } from '../internal/Transition';

export const styles = (theme: Object) => {
  const gutter = theme.spacing.unit * 3;
  const top = { top: 0 };
  const bottom = { bottom: 0 };
  const right = { justifyContent: 'flex-end' };
  const left = { justifyContent: 'flex-start' };
  const topSpace = { top: gutter };
  const bottomSpace = { bottom: gutter };
  const rightSpace = { right: gutter };
  const leftSpace = { left: gutter };
  const center = {
    left: '50%',
    right: 'auto',
    transform: 'translateX(-50%)',
  };

  return {
    root: {
      zIndex: theme.zIndex.snackbar,
      position: 'fixed',
      display: 'flex',
      left: 0,
      right: 0,
      justifyContent: 'center',
      alignItems: 'center',
    },
    anchorTopCenter: {
      extend: [top, center],
    },
    anchorBottomCenter: {
      extend: [bottom, center],
    },
    anchorTopRight: {
      extend: [top, right],
      [theme.breakpoints.up('md')]: {
        left: 'auto',
        extend: [topSpace, rightSpace],
      },
    },
    anchorBottomRight: {
      extend: [bottom, right],
      [theme.breakpoints.up('md')]: {
        left: 'auto',
        extend: [bottomSpace, rightSpace],
      },
    },
    anchorTopLeft: {
      extend: [top, left],
      [theme.breakpoints.up('md')]: {
        right: 'auto',
        extend: [topSpace, leftSpace],
      },
    },
    anchorBottomLeft: {
      extend: [bottom, left],
      [theme.breakpoints.up('md')]: {
        right: 'auto',
        extend: [bottomSpace, leftSpace],
      },
    },
  };
};

export type Origin = {
  horizontal?: 'left' | 'center' | 'right' | number,
  vertical?: 'top' | 'center' | 'bottom' | number,
};

type ProvidedProps = {
  anchorOrigin: Origin,
  classes: Object,
};

export type Props = {
  /**
   * The action to display.
   */
  action?: Node,
  /**
   * The anchor of the `Snackbar`.
   */
  anchorOrigin?: Origin,
  /**
   * The number of milliseconds to wait before automatically dismissing.
   * This behavior is disabled by default with the `null` value.
   */
  autoHideDuration?: ?number,
  /**
   * The number of milliseconds to wait before dismissing after user interaction.
   * If `autoHideDuration` property isn't specified, it does nothing.
   * If `autoHideDuration` property is specified but `resumeHideDuration` isn't,
   * we default to `autoHideDuration / 2` ms.
   */
  resumeHideDuration?: number,
  /**
   * If you wish the take control over the children of the component you can use that property.
   * When using it, no `SnackbarContent` component will be rendered.
   */
  children?: Element<any>,
  /**
   * Useful to extend the style applied to components.
   */
  classes?: Object,
  /**
   * @ignore
   */
  className?: string,
  /**
   * When displaying multiple consecutive Snackbars from a parent rendering a single
   * <Snackbar/>, add the key property to ensure independent treatment of each message.
   * e.g. <Snackbar key={message} />, otherwise, the message may update-in-place and
   * features such as autoHideDuration may be canceled.
   */
  key?: any,
  /**
   * The message to display.
   */
  message?: Node,
  /**
   * Callback fired before the transition is entering.
   */
  onEnter?: TransitionCallback,
  /**
   * Callback fired when the transition is entering.
   */
  onEntering?: TransitionCallback,
  /**
   * Callback fired when the transition has entered.
   */
  onEntered?: TransitionCallback,
  /**
   * Callback fired before the transition is exiting.
   */
  onExit?: TransitionCallback,
  /**
   * Callback fired when the transition is exiting.
   */
  onExiting?: TransitionCallback,
  /**
   * Callback fired when the transition has exited.
   */
  onExited?: TransitionCallback,
  /**
   * @ignore
   */
  onMouseEnter?: Function,
  /**
   * @ignore
   */
  onMouseLeave?: Function,
  /**
   * Callback fired when the component requests to be closed.
   *
   * Typically `onRequestClose` is used to set state in the parent component,
   * which is used to control the `Snackbar` `open` prop.
   *
   * The `reason` parameter can optionally be used to control the response to `onRequestClose`,
   * for example ignoring `clickaway`.
   *
   * @param {object} event The event source of the callback
   * @param {string} reason Can be:`"timeout"` (`autoHideDuration` expired) or: `"clickaway"`
   */
  onRequestClose?: (event: ?Event, reason: string) => void,
  /**
   * If true, `Snackbar` is open.
   */
  open: boolean,
  /**
   * Properties applied to the `SnackbarContent` element.
   */
  SnackbarContentProps?: Object,
  /**
   * Object with Transition component, props & create Fn.
   */
  transition?: ComponentType<*> | Element<any>,
  /**
   * The duration for the transition, in milliseconds.
   * You may specify a single timeout for all transitions, or individually with an object.
   */
  transitionDuration?: TransitionDuration,
};

type State = {
  exited: boolean,
};

class Snackbar extends React.Component<ProvidedProps & Props, State> {
  static defaultProps = {
    anchorOrigin: { vertical: 'bottom', horizontal: 'center' },
    transitionDuration: {
      enter: duration.enteringScreen,
      exit: duration.leavingScreen,
    },
  };

  state = {
    // Used to only render active snackbars.
    exited: false,
  };

  componentWillMount() {
    if (!this.props.open) {
      this.setState({ exited: true });
    }
  }

  componentDidMount() {
    if (this.props.open) {
      this.setAutoHideTimer();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.open && this.state.exited) {
      this.setState({ exited: false });
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.open !== this.props.open) {
      if (this.props.open) {
        this.setAutoHideTimer();
      } else {
        clearTimeout(this.timerAutoHide);
      }
    }
  }

  componentWillUnmount() {
    clearTimeout(this.timerAutoHide);
  }

  timerAutoHide = null;

  // Timer that controls delay before snackbar auto hides
  setAutoHideTimer(autoHideDuration = null) {
    if (!this.props.onRequestClose || this.props.autoHideDuration === undefined) {
      return;
    }

    clearTimeout(this.timerAutoHide);
    this.timerAutoHide = setTimeout(() => {
      if (!this.props.onRequestClose || this.props.autoHideDuration === undefined) {
        return;
      }

      this.props.onRequestClose(null, 'timeout');
    }, autoHideDuration || this.props.autoHideDuration || 0);
  }

  handleMouseEnter = (event: SyntheticUIEvent<>) => {
    if (this.props.onMouseEnter) {
      this.props.onMouseEnter(event);
    }
    this.handlePause();
  };

  handleMouseLeave = (event: SyntheticUIEvent<>) => {
    if (this.props.onMouseLeave) {
      this.props.onMouseLeave(event);
    }
    this.handleResume();
  };

  handleClickAway = (event: Event) => {
    if (this.props.onRequestClose) {
      this.props.onRequestClose(event, 'clickaway');
    }
  };

  // Pause the timer when the user is interacting with the Snackbar
  // or when the user hide the window.
  handlePause = () => {
    clearTimeout(this.timerAutoHide);
  };

  // Restart the timer when the user is no longer interacting with the Snackbar
  // or when the window is shown back.
  handleResume = () => {
    if (this.props.autoHideDuration !== undefined) {
      if (this.props.resumeHideDuration !== undefined) {
        this.setAutoHideTimer(this.props.resumeHideDuration);
        return;
      }
      this.setAutoHideTimer((this.props.autoHideDuration || 0) * 0.5);
    }
  };

  handleTransitionExited = () => {
    this.setState({ exited: true });
  };

  render() {
    const {
      action,
      anchorOrigin: { vertical, horizontal },
      autoHideDuration,
      resumeHideDuration,
      children,
      classes,
      className,
      transitionDuration,
      message,
      onEnter,
      onEntering,
      onEntered,
      onExit,
      onExiting,
      onExited,
      onMouseEnter,
      onMouseLeave,
      onRequestClose,
      open,
      SnackbarContentProps,
      transition: transitionProp,
      ...other
    } = this.props;

    if (!open && this.state.exited) {
      return null;
    }

    const transitionProps = {
      in: open,
      transitionAppear: true,
      transitionDuration,
      onEnter,
      onEntering,
      onEntered,
      onExit,
      onExiting,
      onExited: createChainedFunction(this.handleTransitionExited, onExited),
    };
    const transitionContent = children || (
      <SnackbarContent message={message} action={action} {...SnackbarContentProps} />
    );

    let transition;
    if (typeof transitionProp === 'function') {
      transition = React.createElement(transitionProp, transitionProps, transitionContent);
    } else {
      // $FlowFixMe - rosskevin - figure this out later
      transition = React.cloneElement(
        transitionProp || <Slide direction={vertical === 'top' ? 'down' : 'up'} />,
        transitionProps,
        transitionContent,
      );
    }

    return (
      <EventListener target="window" onFocus={this.handleResume} onBlur={this.handlePause}>
        <ClickAwayListener onClickAway={this.handleClickAway}>
          <div
            className={classNames(
              classes.root,
              classes[
                `anchor${capitalizeFirstLetter(vertical)}${capitalizeFirstLetter(horizontal)}`
              ],
              className,
            )}
            onMouseEnter={this.handleMouseEnter}
            onMouseLeave={this.handleMouseLeave}
            {...other}
          >
            {transition}
          </div>
        </ClickAwayListener>
      </EventListener>
    );
  }
}

export default withStyles(styles, { name: 'MuiSnackbar' })(Snackbar);
<commit_msg>[Snackbar] Fix position regression (#8573)<commit_after>// @flow

import React from 'react';
import type { ComponentType, Element, Node } from 'react';
import classNames from 'classnames';
import EventListener from 'react-event-listener';
import withStyles from '../styles/withStyles';
import { duration } from '../styles/transitions';
import ClickAwayListener from '../internal/ClickAwayListener';
import { capitalizeFirstLetter, createChainedFunction } from '../utils/helpers';
import Slide from '../transitions/Slide';
import SnackbarContent from './SnackbarContent';
import type { TransitionDuration, TransitionCallback } from '../internal/Transition';

export const styles = (theme: Object) => {
  const gutter = theme.spacing.unit * 3;
  const top = { top: 0 };
  const bottom = { bottom: 0 };
  const right = { justifyContent: 'flex-end' };
  const left = { justifyContent: 'flex-start' };
  const topSpace = { top: gutter };
  const bottomSpace = { bottom: gutter };
  const rightSpace = { right: gutter };
  const leftSpace = { left: gutter };
  const center = {
    left: '50%',
    right: 'auto',
    transform: 'translateX(-50%)',
  };

  return {
    root: {
      zIndex: theme.zIndex.snackbar,
      position: 'fixed',
      display: 'flex',
      left: 0,
      right: 0,
      justifyContent: 'center',
      alignItems: 'center',
    },
    anchorTopCenter: {
      extend: [top],
      [theme.breakpoints.up('md')]: {
        extend: [center],
      },
    },
    anchorBottomCenter: {
      extend: [bottom],
      [theme.breakpoints.up('md')]: {
        extend: [center],
      },
    },
    anchorTopRight: {
      extend: [top, right],
      [theme.breakpoints.up('md')]: {
        left: 'auto',
        extend: [topSpace, rightSpace],
      },
    },
    anchorBottomRight: {
      extend: [bottom, right],
      [theme.breakpoints.up('md')]: {
        left: 'auto',
        extend: [bottomSpace, rightSpace],
      },
    },
    anchorTopLeft: {
      extend: [top, left],
      [theme.breakpoints.up('md')]: {
        right: 'auto',
        extend: [topSpace, leftSpace],
      },
    },
    anchorBottomLeft: {
      extend: [bottom, left],
      [theme.breakpoints.up('md')]: {
        right: 'auto',
        extend: [bottomSpace, leftSpace],
      },
    },
  };
};

export type Origin = {
  horizontal?: 'left' | 'center' | 'right' | number,
  vertical?: 'top' | 'center' | 'bottom' | number,
};

type ProvidedProps = {
  anchorOrigin: Origin,
  classes: Object,
};

export type Props = {
  /**
   * The action to display.
   */
  action?: Node,
  /**
   * The anchor of the `Snackbar`.
   */
  anchorOrigin?: Origin,
  /**
   * The number of milliseconds to wait before automatically dismissing.
   * This behavior is disabled by default with the `null` value.
   */
  autoHideDuration?: ?number,
  /**
   * The number of milliseconds to wait before dismissing after user interaction.
   * If `autoHideDuration` property isn't specified, it does nothing.
   * If `autoHideDuration` property is specified but `resumeHideDuration` isn't,
   * we default to `autoHideDuration / 2` ms.
   */
  resumeHideDuration?: number,
  /**
   * If you wish the take control over the children of the component you can use that property.
   * When using it, no `SnackbarContent` component will be rendered.
   */
  children?: Element<any>,
  /**
   * Useful to extend the style applied to components.
   */
  classes?: Object,
  /**
   * @ignore
   */
  className?: string,
  /**
   * When displaying multiple consecutive Snackbars from a parent rendering a single
   * <Snackbar/>, add the key property to ensure independent treatment of each message.
   * e.g. <Snackbar key={message} />, otherwise, the message may update-in-place and
   * features such as autoHideDuration may be canceled.
   */
  key?: any,
  /**
   * The message to display.
   */
  message?: Node,
  /**
   * Callback fired before the transition is entering.
   */
  onEnter?: TransitionCallback,
  /**
   * Callback fired when the transition is entering.
   */
  onEntering?: TransitionCallback,
  /**
   * Callback fired when the transition has entered.
   */
  onEntered?: TransitionCallback,
  /**
   * Callback fired before the transition is exiting.
   */
  onExit?: TransitionCallback,
  /**
   * Callback fired when the transition is exiting.
   */
  onExiting?: TransitionCallback,
  /**
   * Callback fired when the transition has exited.
   */
  onExited?: TransitionCallback,
  /**
   * @ignore
   */
  onMouseEnter?: Function,
  /**
   * @ignore
   */
  onMouseLeave?: Function,
  /**
   * Callback fired when the component requests to be closed.
   *
   * Typically `onRequestClose` is used to set state in the parent component,
   * which is used to control the `Snackbar` `open` prop.
   *
   * The `reason` parameter can optionally be used to control the response to `onRequestClose`,
   * for example ignoring `clickaway`.
   *
   * @param {object} event The event source of the callback
   * @param {string} reason Can be:`"timeout"` (`autoHideDuration` expired) or: `"clickaway"`
   */
  onRequestClose?: (event: ?Event, reason: string) => void,
  /**
   * If true, `Snackbar` is open.
   */
  open: boolean,
  /**
   * Properties applied to the `SnackbarContent` element.
   */
  SnackbarContentProps?: Object,
  /**
   * Object with Transition component, props & create Fn.
   */
  transition?: ComponentType<*> | Element<any>,
  /**
   * The duration for the transition, in milliseconds.
   * You may specify a single timeout for all transitions, or individually with an object.
   */
  transitionDuration?: TransitionDuration,
};

type State = {
  exited: boolean,
};

class Snackbar extends React.Component<ProvidedProps & Props, State> {
  static defaultProps = {
    anchorOrigin: { vertical: 'bottom', horizontal: 'center' },
    transitionDuration: {
      enter: duration.enteringScreen,
      exit: duration.leavingScreen,
    },
  };

  state = {
    // Used to only render active snackbars.
    exited: false,
  };

  componentWillMount() {
    if (!this.props.open) {
      this.setState({ exited: true });
    }
  }

  componentDidMount() {
    if (this.props.open) {
      this.setAutoHideTimer();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.open && this.state.exited) {
      this.setState({ exited: false });
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.open !== this.props.open) {
      if (this.props.open) {
        this.setAutoHideTimer();
      } else {
        clearTimeout(this.timerAutoHide);
      }
    }
  }

  componentWillUnmount() {
    clearTimeout(this.timerAutoHide);
  }

  timerAutoHide = null;

  // Timer that controls delay before snackbar auto hides
  setAutoHideTimer(autoHideDuration = null) {
    if (!this.props.onRequestClose || this.props.autoHideDuration === undefined) {
      return;
    }

    clearTimeout(this.timerAutoHide);
    this.timerAutoHide = setTimeout(() => {
      if (!this.props.onRequestClose || this.props.autoHideDuration === undefined) {
        return;
      }

      this.props.onRequestClose(null, 'timeout');
    }, autoHideDuration || this.props.autoHideDuration || 0);
  }

  handleMouseEnter = (event: SyntheticUIEvent<>) => {
    if (this.props.onMouseEnter) {
      this.props.onMouseEnter(event);
    }
    this.handlePause();
  };

  handleMouseLeave = (event: SyntheticUIEvent<>) => {
    if (this.props.onMouseLeave) {
      this.props.onMouseLeave(event);
    }
    this.handleResume();
  };

  handleClickAway = (event: Event) => {
    if (this.props.onRequestClose) {
      this.props.onRequestClose(event, 'clickaway');
    }
  };

  // Pause the timer when the user is interacting with the Snackbar
  // or when the user hide the window.
  handlePause = () => {
    clearTimeout(this.timerAutoHide);
  };

  // Restart the timer when the user is no longer interacting with the Snackbar
  // or when the window is shown back.
  handleResume = () => {
    if (this.props.autoHideDuration !== undefined) {
      if (this.props.resumeHideDuration !== undefined) {
        this.setAutoHideTimer(this.props.resumeHideDuration);
        return;
      }
      this.setAutoHideTimer((this.props.autoHideDuration || 0) * 0.5);
    }
  };

  handleTransitionExited = () => {
    this.setState({ exited: true });
  };

  render() {
    const {
      action,
      anchorOrigin: { vertical, horizontal },
      autoHideDuration,
      resumeHideDuration,
      children,
      classes,
      className,
      transitionDuration,
      message,
      onEnter,
      onEntering,
      onEntered,
      onExit,
      onExiting,
      onExited,
      onMouseEnter,
      onMouseLeave,
      onRequestClose,
      open,
      SnackbarContentProps,
      transition: transitionProp,
      ...other
    } = this.props;

    if (!open && this.state.exited) {
      return null;
    }

    const transitionProps = {
      in: open,
      transitionAppear: true,
      transitionDuration,
      onEnter,
      onEntering,
      onEntered,
      onExit,
      onExiting,
      onExited: createChainedFunction(this.handleTransitionExited, onExited),
    };
    const transitionContent = children || (
      <SnackbarContent message={message} action={action} {...SnackbarContentProps} />
    );

    let transition;
    if (typeof transitionProp === 'function') {
      transition = React.createElement(transitionProp, transitionProps, transitionContent);
    } else {
      // $FlowFixMe - rosskevin - figure this out later
      transition = React.cloneElement(
        transitionProp || <Slide direction={vertical === 'top' ? 'down' : 'up'} />,
        transitionProps,
        transitionContent,
      );
    }

    return (
      <EventListener target="window" onFocus={this.handleResume} onBlur={this.handlePause}>
        <ClickAwayListener onClickAway={this.handleClickAway}>
          <div
            className={classNames(
              classes.root,
              classes[
                `anchor${capitalizeFirstLetter(vertical)}${capitalizeFirstLetter(horizontal)}`
              ],
              className,
            )}
            onMouseEnter={this.handleMouseEnter}
            onMouseLeave={this.handleMouseLeave}
            {...other}
          >
            {transition}
          </div>
        </ClickAwayListener>
      </EventListener>
    );
  }
}

export default withStyles(styles, { name: 'MuiSnackbar' })(Snackbar);
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import {on, position, closest, width, height, isFunction,
        outerWidthWithMargin, outerHeightWithMargin} from './utils';

function handleSortableItemReadyToMove(e) {
  // if sort handle is defined then only handle sort if the target matches the sort handle
  if (this.props.sortHandleClass && !e.target.classList.contains(this.props.sortHandleClass)) {
    return;
  }

  const target = closest((e.target || e.srcElement), '.ui-sortable-item');
  const evt = {
    pageX: (e.pageX || e.clientX || e.touches[0].pageX),
    pageY: (e.pageY || e.clientY || e.touches[0].pageY),
    offset: position(target)
  };

  if (this.props.onSortableItemReadyToMove) {
    this.props.onSortableItemReadyToMove(evt, this.props.sortableIndex);
  }
}

function handleComponentDidMount() {
  const node = ReactDOM.findDOMNode(this);

  on(node, 'selectstart', (e) => {
    if (e.preventDefault) {
      e.preventDefault();
    }
    else {
      e.returnValue = false;
    }
  });

  if (isFunction(this.props.onSortableItemMount)) {
    this.props.onSortableItemMount(position(node),
                                   width(node),
                                   height(node),
                                   outerWidthWithMargin(node),
                                   outerHeightWithMargin(node),
                                   this.props.sortableIndex);
  }
}

function handleComponentDidUpdate() {
  const node = ReactDOM.findDOMNode(this);

  if (isFunction(this.props.onSortableItemMount)) {
    this.props.onSortableItemMount(position(node),
                                   width(node),
                                   height(node),
                                   outerWidthWithMargin(node),
                                   outerHeightWithMargin(node),
                                   this.props.sortableIndex);
  }
}

const _defaultProps = {
  sortableClassName: '',
  sortableStyle: {},
  onSortableItemMount: () => {},
  onSortableItemReadyToMove: () => {}
};

/**
 * @class A factory for generating either mixin or High Order Component
 *        depending if there is a argument passed in
 *
 * @param {React} Component An optinal argument for creating HOCs, leave it
 *                blank if you'd like old mixin
 */
export default (Component) => {
  if (Component) {
    return class SortableItem extends React.Component {
      static defaultProps = _defaultProps;

      handleSortableItemReadyToMove(e) {
        handleSortableItemReadyToMove.call(this, e);
      }

      componentDidMount() {
        handleComponentDidMount.call(this);
      }

      componentDidUpdate() {
        handleComponentDidUpdate.call(this);
      }

      render() {
        const { sortableClassName, sortableStyle, sortableIndex, ...rest } = this.props;
        return (
          <Component {...rest} className={sortableClassName} style={sortableStyle} key={sortableIndex}
                     onMouseDown={::this.handleSortableItemReadyToMove}
                     onTouchStart={::this.handleSortableItemReadyToMove} />
        );
      }
    }
  }

  return {
    getDefaultProps() {
      return _defaultProps;
    },

    handleSortableItemReadyToMove: handleSortableItemReadyToMove,

    componentDidMount: handleComponentDidMount,

    componentDidUpdate: handleComponentDidUpdate,

    renderWithSortable(item) {
      return React.cloneElement(item, {
        className: this.props.sortableClassName + ' ' + item.props.className,
        style: this.props.sortableStyle,
        key: this.props.sortableIndex,
        onMouseDown: this.handleSortableItemReadyToMove,
        onTouchStart: this.handleSortableItemReadyToMove
      });
    }
  }
};
<commit_msg>fix: tweak sort handle feature for IE compatibility<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import { on, position, closest, width, height, isFunction,
        outerWidthWithMargin, outerHeightWithMargin } from './utils';

function handleSortableItemReadyToMove(e) {
  // if sort handle is defined then only handle sort if the target matches the sort handle
  if (this.props.sortHandleClass &&
      e.target.className.indexOf(this.props.sortHandleClass) === -1) {
    return;
  }

  const target = closest((e.target || e.srcElement), '.ui-sortable-item');
  const evt = {
    pageX: (e.pageX || e.clientX || e.touches[0].pageX),
    pageY: (e.pageY || e.clientY || e.touches[0].pageY),
    offset: position(target)
  };

  if (this.props.onSortableItemReadyToMove) {
    this.props.onSortableItemReadyToMove(evt, this.props.sortableIndex);
  }
}

function handleComponentDidMount() {
  const node = ReactDOM.findDOMNode(this);

  on(node, 'selectstart', (e) => {
    if (e.preventDefault) {
      e.preventDefault();
    } else {
      e.returnValue = false;
    }
  });

  if (isFunction(this.props.onSortableItemMount)) {
    this.props.onSortableItemMount(position(node),
                                   width(node),
                                   height(node),
                                   outerWidthWithMargin(node),
                                   outerHeightWithMargin(node),
                                   this.props.sortableIndex);
  }
}

function handleComponentDidUpdate() {
  const node = ReactDOM.findDOMNode(this);

  if (isFunction(this.props.onSortableItemMount)) {
    this.props.onSortableItemMount(position(node),
                                   width(node),
                                   height(node),
                                   outerWidthWithMargin(node),
                                   outerHeightWithMargin(node),
                                   this.props.sortableIndex);
  }
}

const _defaultProps = {
  sortableClassName: '',
  sortableStyle: {},
  onSortableItemMount: () => {},
  onSortableItemReadyToMove: () => {}
};

/**
 * @class A factory for generating either mixin or High Order Component
 *        depending if there is a argument passed in
 *
 * @param {React} Component An optinal argument for creating HOCs, leave it
 *                blank if you'd like old mixin
 */
export default (Component) => {
  if (Component) {
    return class SortableItem extends React.Component {
      static defaultProps = _defaultProps;

      handleSortableItemReadyToMove(e) {
        handleSortableItemReadyToMove.call(this, e);
      }

      componentDidMount() {
        handleComponentDidMount.call(this);
      }

      componentDidUpdate() {
        handleComponentDidUpdate.call(this);
      }

      render() {
        const { sortableClassName, sortableStyle, sortableIndex, ...rest } = this.props;
        return (
          <Component {...rest} className={sortableClassName}
            style={sortableStyle} key={sortableIndex}
            onMouseDown={::this.handleSortableItemReadyToMove}
            onTouchStart={::this.handleSortableItemReadyToMove}
          />
        );
      }
    };
  }

  return {
    getDefaultProps() {
      return _defaultProps;
    },

    handleSortableItemReadyToMove,

    componentDidMount: handleComponentDidMount,

    componentDidUpdate: handleComponentDidUpdate,

    renderWithSortable(item) {
      return React.cloneElement(item, {
        className: `${this.props.sortableClassName} ${item.props.className}`,
        style: this.props.sortableStyle,
        key: this.props.sortableIndex,
        onMouseDown: this.handleSortableItemReadyToMove,
        onTouchStart: this.handleSortableItemReadyToMove
      });
    }
  };
};
<|endoftext|>


<commit_before>/**
 * @file TagField component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, createRef} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import trim from 'lodash/trim';

import EditableField from '../EditableField';
import IconButton from '../IconButton';

import Util from '../_vendors/Util';
import Dom from '../_vendors/Dom';
import CharSize from '../_vendors/CharSize';
import Event from '../_vendors/Event';
import ComponentUtil from '../_vendors/ComponentUtil';

class TagField extends Component {

    static DEFAULT_SEPARATORS = [','];

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.inputSymbol = Symbol('input');
        this.wrapper = createRef();
        this.wrapperEl = null;
        this.inputWrapper = createRef();
        this.input = createRef();
        this.test = createRef();
        this.testEl = null;

        this.state = {
            wrapperWidth: 'auto',
            data: props.data,
            inputValue: props.inputValue,
            inputIndex: props.data.length,
            itemEditing: false,
            editingItemIndex: -1
        };

    }

    getSeparators = (separators = this.props.separators) => {

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

        return DEFAULT_SEPARATORS;

    };

    generateSeparatorReg = (separators = this.props.separators) => {
        const seps = this.getSeparators(separators);
        return new RegExp(`\\s*${seps ? seps.map(sep => `\\${sep}`).join('|') : ''}\\s*`, 'g');
    };

    removeItem = index => {

        const {data} = this.state;

        if (!data || data.length < 1 || !(index in data)) {
            return;
        }

        data.splice(index, 1);

        this.setState({
            data
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(data);
        });

    };

    calInputIndex = e => {

        if (!this.wrapperEl) {
            return -1;
        }

        const offset = Dom.getOffset(this.wrapperEl),
            {left: minX} = offset,
            wrapperWidth = this.wrapperEl.getBoundingClientRect().width,
            maxX = minX + wrapperWidth;

        let x = e.clientX,
            y = e.clientY,
            inputIndex = -1;

        while (x >= minX) {

            const item = document.elementFromPoint(x, y),
                el = Dom.findParentByClassName(item, 'tag-field-item-wrapper');

            if (el) {
                inputIndex = +el.dataset.index + 1;
                break;
            }

            x--;

        }

        if (inputIndex < 0) {
            while (x <= maxX) {

                const item = document.elementFromPoint(x, y),
                    el = Dom.findParentByClassName(item, 'tag-field-item-wrapper');

                if (el) {
                    inputIndex = +el.dataset.index;
                    break;
                }

                x++;

            }
        }

        return inputIndex < 0 ? this.state.data.length : inputIndex;

    };

    handleMouseDown = e => {

        if (this.props.disabled || Dom.findParentByClassName(e.target, 'tag-field-item-wrapper')
            || Dom.hasClass(e.target, 'tag-field-input')) {
            return;
        }

        const callback = (function (e) {

            return () => {

                if (this.state.itemEditing
                    || (this.wrapperEl
                        && Dom.findParentByClassName(e.target, 'tag-field') != this.wrapperEl)) {
                    this.setState({
                        inputIndex: this.state.data.length
                    });
                    return;
                }

                let x = e.clientX,
                    y = e.clientY;

                if (!x || !y || !this.wrapperEl) {
                    return;
                }

                const offset = Dom.getOffset(this.wrapperEl);

                if (!offset) {
                    return;
                }

                const inputIndex = this.calInputIndex(e);

                this.setState({
                    inputIndex
                }, () => {
                    setTimeout(() => {
                        this.input && this.input.current && this.input.current.focus();
                        // wrapperEl.scrollLeft = 0;
                        // wrapperEl.scrollTop = 0;
                    }, 0);
                });

            };

        }.bind(this))(e);

        if (this.state.inputValue) {
            this.insertInputValue(callback);
        } else {
            callback();
        }

    };

    handleInputChange = e => {

        if (this.props.disabled) {
            return;
        }

        const value = trim(e.target.value),
            inputValue = value ? value.replace(/\r?\n/gm, this.getSeparators()[0]) : value;

        this.setState({
            inputValue
        }, () => {

            const {onInputChange} = this.props,
                width = CharSize.calculateStringWidth(inputValue, this.testEl),
                inputWrapperEl = this.inputWrapper && this.inputWrapper.current,
                inputEl = this.input && this.input.current;

            inputWrapperEl && (inputWrapperEl.style.width = `${width + 9}px`);
            inputEl && (inputEl.style.height = Math.max(inputEl.scrollHeight, 24) + 'px');
            onInputChange && onInputChange(inputValue);

        });

    };

    handleInputKeyDown = e => {

        if (this.props.disabled) {
            return;
        }

        if (e.keyCode === 13) {
            this.insertInputValue();
        }

    };

    insertInputValue = callback => {

        if (this.props.disabled) {
            return;
        }

        const {data, inputValue, inputIndex} = this.state;

        if (!inputValue) {
            return;
        }

        const splitedValue = trim(inputValue).split(this.generateSeparatorReg()).filter(item => !!item);

        data.splice(inputIndex, 0, ...splitedValue);

        this.setState({
            data,
            inputValue: '',
            inputIndex: inputIndex + splitedValue.length
        }, () => {

            this.inputWrapper && this.inputWrapper.current && (this.inputWrapper.current.style.width = '9px');

            const {onChange} = this.props;
            onChange && onChange(data);

            callback && callback();

        });

    };

    handleItemChange = (value, index) => {

        if (this.props.disabled) {
            return;
        }

        const {data} = this.state;

        if (value) {
            const splitedValue = value.split(this.generateSeparatorReg()).filter(item => item);
            data.splice(index, 1, ...splitedValue);
        } else {
            data.splice(index, 1);
        }

        this.setState({
            data
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(data);
        });

    };

    handleItemEditStart = editingItemIndex => {

        if (this.props.disabled) {
            return;
        }

        this.setState({
            itemEditing: true,
            editingItemIndex
        });

    };

    handleItemEditEnd = () => {

        if (this.props.disabled) {
            return;
        }

        this.setState({
            itemEditing: false
        });

    };

    componentDidMount() {

        this.wrapperEl = this.wrapper && this.wrapper.current;
        this.testEl = this.test && this.test.current;

        Event.addEvent(document, 'mousedown', this.handleMouseDown);

        this.setState({
            wrapperWidth: this.wrapperEl ? this.wrapperEl.clientWidth : 'auto'
        });

    }

    componentWillUnmount() {
        Event.removeEvent(document, 'mousedown', this.handleMouseDown);
    }

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            data: ComponentUtil.getDerivedState(props, state, 'data'),
            inputValue: ComponentUtil.getDerivedState(props, state, 'inputValue')
        };
    }

    render() {

        const {
                className, style, valueField, displayField, disabled,
                placeholder, isTagAutoWidth, tagRemoveIconCls
            } = this.props,
            {wrapperWidth, data, inputValue, inputIndex, itemEditing, editingItemIndex} = this.state,
            indexData = Util.genIndexArray(data.length);

        indexData.splice(inputIndex, 0, this.inputSymbol);

        return (

                {
                    indexData && indexData.map(index => index === this.inputSymbol ?
                        !disabled ?

                                <textarea ref={this.input}
                                          className="tag-field-input"
                                          style={{width: wrapperWidth}}
                                          autoFocus={true}
                                          value={inputValue}
                                          placeholder={data.length < 1 && placeholder ? placeholder : ''}
                                          onChange={this.handleInputChange}
                                          onKeyDown={this.handleInputKeyDown}/>

                            :
                            null
                        :

                            <EditableField className="tag-field-item-field"
                                           value={Util.getTextByDisplayField(data[index], displayField, valueField)}
                                           disabled={disabled || (itemEditing && index !== editingItemIndex)}
                                           autoWidth={isTagAutoWidth}
                                           onChange={value => this.handleItemChange(value, index)}
                                           onEditStart={() => this.handleItemEditStart(index)}
                                           onEditEnd={this.handleItemEditEnd}>

                                <IconButton className="tag-field-item-field-delete-button"
                                            iconCls={tagRemoveIconCls}
                                            disabled={disabled || (itemEditing && index !== editingItemIndex)}
                                            onClick={() => this.removeItem(index)}/>

                            </EditableField>

                    )
                }

        );

    }
}

TagField.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    data: PropTypes.array,
    inputValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    valueField: PropTypes.string,
    displayField: PropTypes.string,
    separators: PropTypes.array,

    disabled: PropTypes.bool,
    placeholder: PropTypes.string,
    isTagAutoWidth: PropTypes.bool,
    tagRemoveIconCls: PropTypes.string,

    onChange: PropTypes.func,
    onInputChange: PropTypes.func

};

TagField.defaultProps = {

    data: [],
    inputValue: '',

    valueField: 'value',
    displayField: 'text',
    separators: TagField.DEFAULT_SEPARATORS,

    disabled: false,

    placeholder: '',
    isTagAutoWidth: true,
    tagRemoveIconCls: 'fas fa-times'

};

export default TagField;
<commit_msg>update TagField<commit_after>/**
 * @file TagField component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, createRef} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import trim from 'lodash/trim';

import EditableField from '../EditableField';
import IconButton from '../IconButton';

import Util from '../_vendors/Util';
import Dom from '../_vendors/Dom';
import CharSize from '../_vendors/CharSize';
import Event from '../_vendors/Event';
import ComponentUtil from '../_vendors/ComponentUtil';

class TagField extends Component {

    static DEFAULT_SEPARATORS = [','];

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.inputSymbol = Symbol('input');
        this.wrapper = createRef();
        this.wrapperEl = null;
        this.inputWrapper = createRef();
        this.input = createRef();
        this.test = createRef();
        this.testEl = null;

        this.state = {
            wrapperWidth: 'auto',
            data: props.data,
            inputValue: props.inputValue,
            inputIndex: props.data.length,
            itemEditing: false,
            editingItemIndex: -1
        };

    }

    getSeparators = (separators = this.props.separators) => {

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

        return DEFAULT_SEPARATORS;

    };

    generateSeparatorReg = (separators = this.props.separators) => {
        const seps = this.getSeparators(separators);
        return new RegExp(`\\s*${seps ? seps.map(sep => `\\${sep}`).join('|') : ''}\\s*`, 'g');
    };

    removeItem = index => {

        const {data} = this.state;

        if (!data || data.length < 1 || !(index in data)) {
            return;
        }

        data.splice(index, 1);

        this.setState({
            data
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(data);
        });

    };

    calInputIndex = e => {

        if (!this.wrapperEl) {
            return -1;
        }

        const offset = Dom.getOffset(this.wrapperEl),
            {left: minX} = offset,
            wrapperWidth = this.wrapperEl.getBoundingClientRect().width,
            maxX = minX + wrapperWidth;

        let x = e.clientX,
            y = e.clientY,
            inputIndex = -1;

        while (x >= minX) {

            const item = document.elementFromPoint(x, y),
                el = Dom.findParentByClassName(item, 'tag-field-item-wrapper');

            if (el) {
                inputIndex = +el.dataset.index + 1;
                break;
            }

            x--;

        }

        if (inputIndex < 0) {
            while (x <= maxX) {

                const item = document.elementFromPoint(x, y),
                    el = Dom.findParentByClassName(item, 'tag-field-item-wrapper');

                if (el) {
                    inputIndex = +el.dataset.index;
                    break;
                }

                x++;

            }
        }

        return inputIndex < 0 ? this.state.data.length : inputIndex;

    };

    handleMouseDown = e => {

        if (this.props.disabled || Dom.findParentByClassName(e.target, 'tag-field-item-wrapper')
            || Dom.hasClass(e.target, 'tag-field-input')) {
            return;
        }

        const callback = (function (e) {

            return () => {

                if (this.state.itemEditing
                    || (this.wrapperEl
                        && Dom.findParentByClassName(e.target, 'tag-field') != this.wrapperEl)) {
                    this.setState({
                        inputIndex: this.state.data.length
                    });
                    return;
                }

                let x = e.clientX,
                    y = e.clientY;

                if (!x || !y || !this.wrapperEl) {
                    return;
                }

                const offset = Dom.getOffset(this.wrapperEl);

                if (!offset) {
                    return;
                }

                const inputIndex = this.calInputIndex(e);

                this.setState({
                    inputIndex
                }, () => {
                    setTimeout(() => {
                        this.input && this.input.current && this.input.current.focus();
                        // wrapperEl.scrollLeft = 0;
                        // wrapperEl.scrollTop = 0;
                    }, 0);
                });

            };

        }.bind(this))(e);

        if (this.state.inputValue) {
            this.insertInputValue(callback);
        } else {
            callback();
        }

    };

    handleInputChange = e => {

        if (this.props.disabled) {
            return;
        }

        const value = e.target.value,
            inputValue = value ? value.replace(/\r?\n/gm, '') : value;

        this.setState({
            inputValue
        }, () => {

            const {onInputChange} = this.props,
                width = CharSize.calculateStringWidth(inputValue, this.testEl),
                inputWrapperEl = this.inputWrapper && this.inputWrapper.current,
                inputEl = this.input && this.input.current;

            inputWrapperEl && (inputWrapperEl.style.width = `${width + 9}px`);
            inputEl && (inputEl.style.height = Math.max(inputEl.scrollHeight, 24) + 'px');
            onInputChange && onInputChange(inputValue);

        });

    };

    handleInputKeyDown = e => {

        if (this.props.disabled) {
            return;
        }

        if (e.keyCode === 13) {
            this.insertInputValue();
        }

    };

    insertInputValue = callback => {

        if (this.props.disabled) {
            return;
        }

        const {data, inputValue, inputIndex} = this.state;

        if (!inputValue) {
            return;
        }

        const splitedValue = trim(inputValue).split(this.generateSeparatorReg()).filter(item => !!item);

        data.splice(inputIndex, 0, ...splitedValue);

        this.setState({
            data,
            inputValue: '',
            inputIndex: inputIndex + splitedValue.length
        }, () => {

            this.inputWrapper && this.inputWrapper.current && (this.inputWrapper.current.style.width = '9px');

            const {onChange} = this.props;
            onChange && onChange(data);

            callback && callback();

        });

    };

    handleItemChange = (value, index) => {

        if (this.props.disabled) {
            return;
        }

        const {data} = this.state;

        if (value) {
            const splitedValue = value.split(this.generateSeparatorReg()).filter(item => item);
            data.splice(index, 1, ...splitedValue);
        } else {
            data.splice(index, 1);
        }

        this.setState({
            data
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(data);
        });

    };

    handleItemEditStart = editingItemIndex => {

        if (this.props.disabled) {
            return;
        }

        this.setState({
            itemEditing: true,
            editingItemIndex
        });

    };

    handleItemEditEnd = () => {

        if (this.props.disabled) {
            return;
        }

        this.setState({
            itemEditing: false
        });

    };

    componentDidMount() {

        this.wrapperEl = this.wrapper && this.wrapper.current;
        this.testEl = this.test && this.test.current;

        Event.addEvent(document, 'mousedown', this.handleMouseDown);

        this.setState({
            wrapperWidth: this.wrapperEl ? this.wrapperEl.clientWidth : 'auto'
        });

    }

    componentWillUnmount() {
        Event.removeEvent(document, 'mousedown', this.handleMouseDown);
    }

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            data: ComponentUtil.getDerivedState(props, state, 'data'),
            inputValue: ComponentUtil.getDerivedState(props, state, 'inputValue')
        };
    }

    render() {

        const {
                className, style, valueField, displayField, disabled,
                placeholder, isTagAutoWidth, tagRemoveIconCls
            } = this.props,
            {wrapperWidth, data, inputValue, inputIndex, itemEditing, editingItemIndex} = this.state,
            indexData = Util.genIndexArray(data.length);

        indexData.splice(inputIndex, 0, this.inputSymbol);

        return (

                {
                    indexData && indexData.map(index => index === this.inputSymbol ?
                        !disabled ?

                                <textarea ref={this.input}
                                          className="tag-field-input"
                                          style={{width: wrapperWidth}}
                                          autoFocus={true}
                                          value={inputValue}
                                          placeholder={data.length < 1 && placeholder ? placeholder : ''}
                                          onChange={this.handleInputChange}
                                          onKeyDown={this.handleInputKeyDown}/>

                            :
                            null
                        :

                            <EditableField className="tag-field-item-field"
                                           value={Util.getTextByDisplayField(data[index], displayField, valueField)}
                                           disabled={disabled || (itemEditing && index !== editingItemIndex)}
                                           autoWidth={isTagAutoWidth}
                                           onChange={value => this.handleItemChange(value, index)}
                                           onEditStart={() => this.handleItemEditStart(index)}
                                           onEditEnd={this.handleItemEditEnd}>
                                <IconButton className="tag-field-item-field-delete-button"
                                            iconCls={tagRemoveIconCls}
                                            disabled={disabled || (itemEditing && index !== editingItemIndex)}
                                            onClick={() => this.removeItem(index)}/>
                            </EditableField>

                    )
                }

        );

    }
}

TagField.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    data: PropTypes.array,
    inputValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    valueField: PropTypes.string,
    displayField: PropTypes.string,
    separators: PropTypes.array,

    disabled: PropTypes.bool,
    placeholder: PropTypes.string,
    isTagAutoWidth: PropTypes.bool,
    tagRemoveIconCls: PropTypes.string,

    onChange: PropTypes.func,
    onInputChange: PropTypes.func

};

TagField.defaultProps = {

    data: [],
    inputValue: '',

    valueField: 'value',
    displayField: 'text',
    separators: TagField.DEFAULT_SEPARATORS,

    disabled: false,

    placeholder: '',
    isTagAutoWidth: true,
    tagRemoveIconCls: 'fas fa-times'

};

export default TagField;
<|endoftext|>


<commit_before>import isFunction from 'lodash/lang/isFunction';
import isArray from 'lodash/lang/isArray';
import isString from 'lodash/lang/isString';

import Manager from '../index';
import Injector from '../components/injector';
import Config from '../components/config';
import Logger from '../components/logger';
import RouteConfig from '../route/route-config';

import ControllerConverter from './converters/controller-converter';
import FactoryConverter from './converters/factory-converter';
import DirectiveConverter from './converters/directive-converter';
import RouteConverter from './converters/route-converter';

function setupApplication(module) {
  module.run(['$injector', '$rootScope', '$location', function($injector, $rootScope, $location) {
    Injector.setInjector($injector);

    var onRouteChangeError = RouteConfig.getOnRouteChangeError();
    if (onRouteChangeError) {
      $rootScope.$on('$routeChangeError', () => {
        var url = onRouteChangeError();

        if (isString(url)) {
          $location.url(url);
        }
      });
    }

    var onRouteChangeStart = RouteConfig.getOnRouteChangeStart();
    $rootScope.$on('$routeChangeStart', () => {
      Logger.resetColors();

      if (isFunction(onRouteChangeStart)) {
        onRouteChangeStart();
      }
    });
  }]);
}

var local = {
  module: Symbol('angular-module'),
  application: Symbol('application-name')
};

export default class AngularBootstrap {
  constructor(application, deps = []) {
    if (!isString(application)) {
      throw new Error('Wrong module name');
    }

    if (!isArray(deps)) {
      throw new Error('Wrong module dependencies');
    }

    this[local.application] = application;
    this[local.module] = angular.module(application, deps);
  }

  getModule() {
    return this[local.module];
  }

  bootstrap() {
    this.register();

    var module = this.getModule();
    setupApplication(module);
  }

  register() {
    var application = this[local.application];

    var { controllers, factories, directives, routes } = Manager.getModels();

    ControllerConverter.register(controllers, application);
    FactoryConverter.register(factories, application);
    DirectiveConverter.register(directives, application);

    RouteConverter.setup();
    RouteConverter.register(routes, application);
  }
}
<commit_msg>Clear manager's models after registration<commit_after>import isFunction from 'lodash/lang/isFunction';
import isArray from 'lodash/lang/isArray';
import isString from 'lodash/lang/isString';

import Manager from '../index';
import Injector from '../components/injector';
import Config from '../components/config';
import Logger from '../components/logger';
import RouteConfig from '../route/route-config';

import ControllerConverter from './converters/controller-converter';
import FactoryConverter from './converters/factory-converter';
import DirectiveConverter from './converters/directive-converter';
import RouteConverter from './converters/route-converter';

function setupApplication(module) {
  module.run(['$injector', '$rootScope', '$location', function($injector, $rootScope, $location) {
    Injector.setInjector($injector);

    var onRouteChangeError = RouteConfig.getOnRouteChangeError();
    if (onRouteChangeError) {
      $rootScope.$on('$routeChangeError', () => {
        var url = onRouteChangeError();

        if (isString(url)) {
          $location.url(url);
        }
      });
    }

    var onRouteChangeStart = RouteConfig.getOnRouteChangeStart();
    $rootScope.$on('$routeChangeStart', () => {
      Logger.resetColors();

      if (isFunction(onRouteChangeStart)) {
        onRouteChangeStart();
      }
    });
  }]);
}

var local = {
  module: Symbol('angular-module'),
  application: Symbol('application-name')
};

export default class AngularBootstrap {
  constructor(application, deps = []) {
    if (!isString(application)) {
      throw new Error('Wrong module name');
    }

    if (!isArray(deps)) {
      throw new Error('Wrong module dependencies');
    }

    this[local.application] = application;
    this[local.module] = angular.module(application, deps);
  }

  getModule() {
    return this[local.module];
  }

  bootstrap() {
    this.register();

    var module = this.getModule();
    setupApplication(module);
  }

  register() {
    var application = this[local.application];

    var { controllers, factories, directives, routes } = Manager.getModels();

    ControllerConverter.register(controllers, application);
    FactoryConverter.register(factories, application);
    DirectiveConverter.register(directives, application);

    RouteConverter.setup();
    RouteConverter.register(routes, application);

    Manager.clear();
  }
}
<|endoftext|>


<commit_before>import PropTypes from "prop-types";
import React, {Component} from 'react';
import connect from "react-redux/es/connect/connect";
import {Alert, Loader, Page} from 'tabler-react';
import {messageSend, messageListen, messageVersion} from "../actions";
import Communication from "../components/Communication";

class CommunicationContainer extends Component {
  componentDidMount() {
    this.props.communicationVersion();
  }

  sendMessage(topic, message) {
    this.props.messageSend({topic, message});
  }

  listenToChannel(channel) {
    this.props.messageListen(channel);
  }

  render() {
    let isEnabledMessage = '';
    if (this.props.version === undefined || this.props.version === null) {
      isEnabledMessage =
        <Alert bsStyle="secondary "><Loader/> Checking Whisper support, please wait</Alert>;
    } else if (!this.props.version) {
      isEnabledMessage = <Alert type="warning">The node you are using does not support Whisper</Alert>;
    } else if (this.props.version === -1) {
      isEnabledMessage = <Alert type="warning">The node uses an unsupported version of Whisper</Alert>;
    }

    return (
      <Page.Content title="Communication explorer">
        {isEnabledMessage}
        <Communication listenToMessages={(channel) => this.listenToChannel(channel)}
                       sendMessage={(channel, message) => this.sendMessage(channel, message)}
                       channels={this.props.channels}
                       subscriptions={this.props.subscriptions}/>
      </Page.Content>
    );
  }
}

CommunicationContainer.propTypes = {
  messageSend: PropTypes.func,
  messageListen: PropTypes.func,
  communicationVersion: PropTypes.func,
  channels: PropTypes.object,
  subscriptions: PropTypes.array,
  version: PropTypes.number
};

function mapStateToProps(state) {
  return {
    channels: state.messages.channels,
    subscriptions: state.messages.subscriptions,
    version: state.messages.version
  };
}

export default connect(
  mapStateToProps,
  {
    messageSend: messageSend.request,
    messageListen: messageListen.request,
    communicationVersion: messageVersion.request
  }
)(CommunicationContainer);

<commit_msg>fix no rendering on subscriptions<commit_after>import PropTypes from "prop-types";
import React, {Component} from 'react';
import connect from "react-redux/es/connect/connect";
import {Alert, Loader, Page} from 'tabler-react';
import {messageSend, messageListen, messageVersion} from "../actions";
import Communication from "../components/Communication";

class CommunicationContainer extends Component {
  componentDidMount() {
    this.props.communicationVersion();
  }

  sendMessage(topic, message) {
    this.props.messageSend({topic, message});
  }

  listenToChannel(channel) {
    this.props.messageListen(channel);
  }

  render() {
    let isEnabledMessage = '';
    if (this.props.messages.version === undefined || this.props.messages.version === null) {
      isEnabledMessage =
        <Alert bsStyle="secondary "><Loader/> Checking Whisper support, please wait</Alert>;
    } else if (!this.props.messages.version) {
      isEnabledMessage = <Alert type="warning">The node you are using does not support Whisper</Alert>;
    } else if (this.props.messages.version === -1) {
      isEnabledMessage = <Alert type="warning">The node uses an unsupported version of Whisper</Alert>;
    }

    return (
      <Page.Content title="Communication explorer">
        {isEnabledMessage}
        <Communication listenToMessages={(channel) => this.listenToChannel(channel)}
                       sendMessage={(channel, message) => this.sendMessage(channel, message)}
                       channels={this.props.messages.channels}
                       subscriptions={this.props.messages.subscriptions}/>
      </Page.Content>
    );
  }
}

CommunicationContainer.propTypes = {
  messageSend: PropTypes.func,
  messageListen: PropTypes.func,
  communicationVersion: PropTypes.func,
  messages: PropTypes.object
};

function mapStateToProps(state) {
  return {
    messages: state.messages
  };
}

export default connect(
  mapStateToProps,
  {
    messageSend: messageSend.request,
    messageListen: messageListen.request,
    communicationVersion: messageVersion.request
  }
)(CommunicationContainer);

<|endoftext|>


<commit_before>namespace('Sy');

/**
 * Default implementation of the service container interface
 *
 * @package Sy
 * @class
 * @implements {Sy.ServiceContainerInterface}
 *
 * @param {string} name
 */

Sy.ServiceContainer = function (name) {

    this.name = '';
    this.services = {};
    this.definitions = {};
    this.parameters = {};

    this.setName(name);

};

Sy.ServiceContainer.prototype = Object.create(Sy.ServiceContainerInterface.prototype, {

    /**
     * @inheritDoc
     */

    setParameters: {
        value: function (params) {
            this.parameters = params;

            return this;
        }
    },

    /**
     * @inheritDoc
     */

    get: {

        value: function (serviceName) {

            if (this.services[serviceName] === undefined && this.definitions[serviceName]) {

                var opts = this.definitions[serviceName],
                    service;

                if (opts.type === 'creator') {
                    service = this.buildServiceByCreator(serviceName);
                } else if (opts.type === 'prototype') {
                    service = this.buildServiceByDefinition(serviceName);
                }

                this.services[serviceName] = service;
                delete this.definitions[serviceName];

            } else if (this.services[serviceName] === undefined) {

                throw new ReferenceError('Unknown service');

            }

            return this.services[serviceName];

        }

    },

    /**
     * Build a service via its creator function
     *
     * @private
     * @param {string} name
     *
     * @return {Object}
     */

    buildServiceByCreator: {
        value: function (name) {
            return this.definitions[name].fn.apply(this, this.definitions[name].args);
        }
    },

    /**
     * Build a service based on its definition
     *
     * @private
     * @param {string} name
     *
     * @return {Object}
     */

    buildServiceByDefinition: {
        value: function (name) {

            var opts = this.definitions[name],
                service;

            if (opts.arguments) {
                service = new (objectGetter(opts.constructor))(opts.arguments);
            } else {
                service = new (objectGetter(opts.constructor))();
            }

            if (opts.calls instanceof Array) {
                for (var i = 0, l = opts.calls.length; i < l; i++) {
                    var args = opts.calls[i][1];

                    for (var a = 0, al = args.length; a < al; a++) {
                        if (typeof args[a] === 'string' && args[a].substring(0, 1) === '@') {
                            args[a] = this.get(args[a].substr(1));
                        } else if (typeof args[a] === 'string' && new RegExp(/^%.*%$/i).test(args[a])) {
                            args[a] = objectGetter.call(
                                this.parameters,
                                args[a].substring(1, args[a].length - 1)
                            );
                        }
                    }

                    service[opts.calls[i][0]].apply(service, args);
                }
            }

            return service;

        }
    },

    /**
     * @inheritDoc
     */

    set: {

        value: function (serviceName, creator, args) {

            if (serviceName instanceof Object) {
                this.setPrototypes(serviceName);
            } else {
                this.setCreator(serviceName, creator, args);
            }

            return this;

        }

    },

    /**
     * Register a new service creator definition
     *
     * @private
     * @param {string} serviceName
     * @param {funtcion} creator
     * @param {Array} args
     */

    setCreator: {
        value: function (serviceName, creator, args) {

            if (typeof creator !== 'function'){
                throw new TypeError('Invalid creator type');
            }

            if (args && !(args instanceof Array)) {
                throw new TypeError('Invalid args type (must be an array)');
            }

            if (this.has(serviceName)) {
                throw new TypeError('Service name "' + serviceName + '" already used');
            }

            this.definitions[serviceName] = {
                fn: creator,
                args: args,
                type: 'creator'
            };

        }
    },

    /**
     * Register new services prototype definitions
     *
     * @private
     * @param {Object} definitions
     */

    setPrototypes: {
        value: function (definitions) {

            for (var name in definitions) {
                    if (definitions.hasOwnProperty(name)) {

                        if (this.has(name)) {
                            throw new TypeError('Service name "' + name + '" already used');
                        }

                        this.definitions[name] = definitions[name];
                        this.definitions[name].type = 'prototype';
                    }
                }

        }
    },

    /**
     * @inheritDoc
     */

    has: {
        value: function (name) {

            if (this.services[name] || this.definitions[name]) {
                return true;
            }

            return false;

        }
    },

    /**
     * @inheritDoc
     */

    getName: {

        value: function () {

            return this.name;

        }

    },

    /**
     * @inheritDoc
     */

    setName: {
        value: function (name) {

            this.name = name;

            return this;

        }
    }

});<commit_msg>re-introduced the service name pattern to comply with<commit_after>namespace('Sy');

/**
 * Default implementation of the service container interface
 *
 * @package Sy
 * @class
 * @implements {Sy.ServiceContainerInterface}
 *
 * @param {string} name
 */

Sy.ServiceContainer = function (name) {

    this.name = '';
    this.services = {};
    this.definitions = {};
    this.parameters = {};

    this.setName(name);

};

Sy.ServiceContainer.prototype = Object.create(Sy.ServiceContainerInterface.prototype, {

    PATTERN: {
        value: '^([a-z]+::|[a-z]+)+$',
        writable: false,
        configurable: false
    },

    /**
     * @inheritDoc
     */

    setParameters: {
        value: function (params) {
            this.parameters = params;

            return this;
        }
    },

    /**
     * @inheritDoc
     */

    get: {

        value: function (serviceName) {

            if (this.services[serviceName] === undefined && this.definitions[serviceName]) {

                var opts = this.definitions[serviceName],
                    service;

                if (opts.type === 'creator') {
                    service = this.buildServiceByCreator(serviceName);
                } else if (opts.type === 'prototype') {
                    service = this.buildServiceByDefinition(serviceName);
                }

                this.services[serviceName] = service;
                delete this.definitions[serviceName];

            } else if (this.services[serviceName] === undefined) {

                throw new ReferenceError('Unknown service');

            }

            return this.services[serviceName];

        }

    },

    /**
     * Build a service via its creator function
     *
     * @private
     * @param {string} name
     *
     * @return {Object}
     */

    buildServiceByCreator: {
        value: function (name) {
            return this.definitions[name].fn.apply(this, this.definitions[name].args);
        }
    },

    /**
     * Build a service based on its definition
     *
     * @private
     * @param {string} name
     *
     * @return {Object}
     */

    buildServiceByDefinition: {
        value: function (name) {

            var opts = this.definitions[name],
                service;

            if (opts.arguments) {
                service = new (objectGetter(opts.constructor))(opts.arguments);
            } else {
                service = new (objectGetter(opts.constructor))();
            }

            if (opts.calls instanceof Array) {
                for (var i = 0, l = opts.calls.length; i < l; i++) {
                    var args = opts.calls[i][1];

                    for (var a = 0, al = args.length; a < al; a++) {
                        if (typeof args[a] === 'string' && args[a].substring(0, 1) === '@') {
                            args[a] = this.get(args[a].substr(1));
                        } else if (typeof args[a] === 'string' && new RegExp(/^%.*%$/i).test(args[a])) {
                            args[a] = objectGetter.call(
                                this.parameters,
                                args[a].substring(1, args[a].length - 1)
                            );
                        }
                    }

                    service[opts.calls[i][0]].apply(service, args);
                }
            }

            return service;

        }
    },

    /**
     * @inheritDoc
     */

    set: {

        value: function (serviceName, creator, args) {

            if (serviceName instanceof Object) {
                this.setPrototypes(serviceName);
            } else {
                this.setCreator(serviceName, creator, args);
            }

            return this;

        }

    },

    /**
     * Register a new service creator definition
     *
     * @private
     * @param {string} serviceName
     * @param {funtcion} creator
     * @param {Array} args
     */

    setCreator: {
        value: function (serviceName, creator, args) {

            var regex = new RegExp(this.PATTERN, 'gi');

            if (!regex.test(serviceName)) {
                throw new SyntaxError('Service name "' + serviceName + '" does not follow pattern convention');
            }

            if (typeof creator !== 'function'){
                throw new TypeError('Invalid creator type');
            }

            if (args && !(args instanceof Array)) {
                throw new TypeError('Invalid args type (must be an array)');
            }

            if (this.has(serviceName)) {
                throw new TypeError('Service name "' + serviceName + '" already used');
            }

            this.definitions[serviceName] = {
                fn: creator,
                args: args,
                type: 'creator'
            };

        }
    },

    /**
     * Register new services prototype definitions
     *
     * @private
     * @param {Object} definitions
     */

    setPrototypes: {
        value: function (definitions) {

            for (var name in definitions) {
                    if (definitions.hasOwnProperty(name)) {

                        var regex = new RegExp(this.PATTERN, 'gi');

                        if (!regex.test(name)) {
                            throw new SyntaxError('Service name "' + name + '" does not follow pattern convention');
                        }

                        if (this.has(name)) {
                            throw new TypeError('Service name "' + name + '" already used');
                        }

                        this.definitions[name] = definitions[name];
                        this.definitions[name].type = 'prototype';
                    }
                }

        }
    },

    /**
     * @inheritDoc
     */

    has: {
        value: function (name) {

            if (this.services[name] || this.definitions[name]) {
                return true;
            }

            return false;

        }
    },

    /**
     * @inheritDoc
     */

    getName: {

        value: function () {

            return this.name;

        }

    },

    /**
     * @inheritDoc
     */

    setName: {
        value: function (name) {

            this.name = name;

            return this;

        }
    }

});<|endoftext|>