<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import prefixClass from './utils/prefixClass';
import icBEM from './utils/icBEM';
import rowComp from './mixins/rowComp';
import './styles/Checkbox.scss';

import Icon from './Icon';

export const COMPONENT_NAME = prefixClass('checkbox');
const ROOT_BEM = icBEM(COMPONENT_NAME);
export const BEM = {
    root: ROOT_BEM,
    input: ROOT_BEM.element('input'),
    button: ROOT_BEM.element('button'),
    iconWrapper: ROOT_BEM.element('icon-wrapper'),
};

export const CHECKBOX_BUTTON = <Icon type="empty" className={`${BEM.button}`} />;

class Checkbox extends PureComponent {
    static propTypes = {
        /**
         * Use `input` to inject props to the underlying <input>
         */
        input: PropTypes.object, // eslint-disable-line react/forbid-prop-types
        indeterminate: PropTypes.bool,
        overrideButton: PropTypes.element,

        // <input type="checkbox" /> props
        checked: PropTypes.bool,
        defaultChecked: PropTypes.bool,
        disabled: PropTypes.bool,
        onChange: PropTypes.func,
    };

    static defaultProps = {
        input: {},
        indeterminate: false,
        overrideButton: null,

        checked: undefined,
        defaultChecked: undefined,
        disabled: false,
        onChange: undefined
    };

    componentDidMount() {
        this.updateInputIndeterminate();
    }

    componentDidUpdate(prevProps) {
        if (prevProps.indeterminate !== this.props.indeterminate) {
            this.updateInputIndeterminate();
        }
    }

    getInputRef() {
        return this.inputRef;
    }

    updateInputIndeterminate() {
        const inputNode = this.getInputRef();
        inputNode.indeterminate = this.props.indeterminate;
    }

    renderCheckboxInput(inputProps, overrideButton) {
        return (

                <input
                    ref={(ref) => { this.inputRef = ref; }}
                    type="checkbox"
                    className={BEM.input}
                    {...inputProps} />

                {overrideButton || CHECKBOX_BUTTON}

        );
    }

    render() {
        const {
            input,
            indeterminate,
            overrideButton,
            // <input> props
            checked,
            defaultChecked,
            disabled,
            onChange,
            // React props
            className,
            children,
            ...otherProps
        } = this.props;

        const inputProps = {
            checked,
            defaultChecked,
            disabled,
            onChange,
            ...input,
        };

        const rootClassName = classNames(className, COMPONENT_NAME);

        return (

                {this.renderCheckboxInput(inputProps, overrideButton)}
                {children}

        );
    }
}

export default rowComp()(Checkbox);
export { Checkbox as PureCheckbox };
<commit_msg>Fix Checkbox prop table doc<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import prefixClass from './utils/prefixClass';
import icBEM from './utils/icBEM';
import rowComp from './mixins/rowComp';
import './styles/Checkbox.scss';

import Icon from './Icon';

export const COMPONENT_NAME = prefixClass('checkbox');
const ROOT_BEM = icBEM(COMPONENT_NAME);
export const BEM = {
    root: ROOT_BEM,
    input: ROOT_BEM.element('input'),
    button: ROOT_BEM.element('button'),
    iconWrapper: ROOT_BEM.element('icon-wrapper'),
};

export const CHECKBOX_BUTTON = <Icon type="empty" className={`${BEM.button}`} />;

class Checkbox extends PureComponent {
    static propTypes = {
        /**
         * Use this to inject props to the underlying `<input />`
         */
        input: PropTypes.object, // eslint-disable-line react/forbid-prop-types
        indeterminate: PropTypes.bool,
        overrideButton: PropTypes.element,

        // <input type="checkbox" /> props
        checked: PropTypes.bool,
        defaultChecked: PropTypes.bool,
        disabled: PropTypes.bool,
        onChange: PropTypes.func,
    };

    static defaultProps = {
        input: {},
        indeterminate: false,
        overrideButton: null,

        checked: undefined,
        defaultChecked: undefined,
        disabled: false,
        onChange: undefined
    };

    componentDidMount() {
        this.updateInputIndeterminate();
    }

    componentDidUpdate(prevProps) {
        if (prevProps.indeterminate !== this.props.indeterminate) {
            this.updateInputIndeterminate();
        }
    }

    getInputRef() {
        return this.inputRef;
    }

    updateInputIndeterminate() {
        const inputNode = this.getInputRef();
        inputNode.indeterminate = this.props.indeterminate;
    }

    renderCheckboxInput(inputProps, overrideButton) {
        return (

                <input
                    ref={(ref) => { this.inputRef = ref; }}
                    type="checkbox"
                    className={BEM.input}
                    {...inputProps} />

                {overrideButton || CHECKBOX_BUTTON}

        );
    }

    render() {
        const {
            input,
            indeterminate,
            overrideButton,
            // <input> props
            checked,
            defaultChecked,
            disabled,
            onChange,
            // React props
            className,
            children,
            ...otherProps
        } = this.props;

        const inputProps = {
            checked,
            defaultChecked,
            disabled,
            onChange,
            ...input,
        };

        const rootClassName = classNames(className, COMPONENT_NAME);

        return (

                {this.renderCheckboxInput(inputProps, overrideButton)}
                {children}

        );
    }
}

export default rowComp()(Checkbox);
export { Checkbox as PureCheckbox };
<|endoftext|>


<commit_before>define([
  'src/util/api',
  './yamlParser',
  'src/util/ui',
  'src/util/versioning'
], function (API, yamlParser, UI, Versioning) {
  let tipsURL = 'https://docs.cheminfo.org/tips/';
  let pagesURL = 'https://docs.cheminfo.org/pages/';
  let minDelayBetweenTips = 4 * 3600 * 1000;

  async function getViewInfo() {
    if (
      !Versioning.lastLoaded ||
      !Versioning.lastLoaded.view ||
      !Versioning.lastLoaded.view.url
    ) {
      return {};
    }
    let viewURL = Versioning.lastLoaded.view.url;
    let recordURL = viewURL.replace(/\/view.json.*/, '');
    let response = await fetch(recordURL, { credentials: 'include' });
    let info = await response.json();
    info.rev = Number(info._rev.replace(/-.*/, ''));
    return info;
  }

  async function showTips() {
    let info = await getViewInfo();
    if (!info._id) return;

    //  info._id='15c9a2dcd55c963fdedf2c18a1471b03';
    //  info.rev=90;
    // retrieve tips toc
    await fetch(`${tipsURL + info._id}/index.yml`)
      .then(async (response) => {
        let text = await response.text();
        processTipsToc(text, info);
      })
      .catch();
  }

  function processTipsToc(yaml, info) {
    let toc = yamlParser.parse(yaml);
    let userPrefs = JSON.parse(
      window.localStorage.getItem('tipsPreferences') ||
        '{"lastTip":0, "views":{}}'
    );
    if ((Date.now() - userPrefs.lastTip || 0) < minDelayBetweenTips) return;
    if (!userPrefs.views[info._id]) {
      userPrefs.views[info._id] = { lastIndex: -1 };
    }
    let viewPrefs = userPrefs.views[info._id];
    let tips = toc.tips
      .map((a, index) => {
        a.index = a.index === undefined ? index + 1 : a.index;
        return a;
      })
      .sort((a, b) => a.index - b.index)
      .filter((a) => a.index > viewPrefs.lastIndex)
      .filter((a) => info.rev >= (a.minRev || 0))
      .filter((a) => info.rev <= (a.maxRev || Number.MAX_SAFE_INTEGER));
    if (tips.length > 0) {
      viewPrefs.lastIndex = tips[0].index;
      userPrefs.lastTip = Date.now();
      window.localStorage.setItem('tipsPreferences', JSON.stringify(userPrefs));
      UI.dialog(
        `

        `,
        { width: 800, height: 600, title: 'Did you know ?' }
      );
    }
  }

  async function addPageHelp(options = {}) {
    const { iconSize = 'fa-3x' } = options;
    let info = await getViewInfo();
    if (!info._id) return;

    await fetch(`${pagesURL + info._id}/index.html`, { method: 'HEAD' })
      .then(async () => {})
      .catch();

    let target = document.getElementById('modules-grid');
    let div = document.createElement('DIV');
    div.innerHTML = `

      `;
    div.style.zIndex = 99;
    div.style.position = 'fixed';

    div.addEventListener('click', () => {
      UI.dialog(
        `

        `,
        { width: 900, height: 700, title: 'Information about the page' }
      );
    });

    target.prepend(div);

    window.addEventListener(
      'keypress',
      (event) => {
        if (
          event.altKey &&
          event.shiftKey &&
          event.ctrlKey &&
          event.keyCode === 8
        ) {
          UI.dialog(
            `

            `,
            { width: 900, height: 700, title: 'Information about the page' }
          );
        }
      },
      false
    );
  }

  return {
    showTips,
    addPageHelp
  };
});
<commit_msg>mvoe viewInfo in its own file<commit_after>define([
  'src/util/api',
  './yamlParser',
  'src/util/ui',
  './getViewInfo'
], function (API, yamlParser, UI, getViewInfo) {
  let tipsURL = 'https://docs.cheminfo.org/tips/';
  let pagesURL = 'https://docs.cheminfo.org/pages/';
  let minDelayBetweenTips = 4 * 3600 * 1000;

  async function showTips() {
    let info = await getViewInfo();
    if (!info._id) return;

    //  info._id='15c9a2dcd55c963fdedf2c18a1471b03';
    //  info.rev=90;
    // retrieve tips toc
    await fetch(`${tipsURL + info._id}/index.yml`)
      .then(async (response) => {
        let text = await response.text();
        processTipsToc(text, info);
      })
      .catch();
  }

  function processTipsToc(yaml, info) {
    let toc = yamlParser.parse(yaml);
    let userPrefs = JSON.parse(
      window.localStorage.getItem('tipsPreferences') ||
        '{"lastTip":0, "views":{}}'
    );
    if ((Date.now() - userPrefs.lastTip || 0) < minDelayBetweenTips) return;
    if (!userPrefs.views[info._id]) {
      userPrefs.views[info._id] = { lastIndex: -1 };
    }
    let viewPrefs = userPrefs.views[info._id];
    let tips = toc.tips
      .map((a, index) => {
        a.index = a.index === undefined ? index + 1 : a.index;
        return a;
      })
      .sort((a, b) => a.index - b.index)
      .filter((a) => a.index > viewPrefs.lastIndex)
      .filter((a) => info.rev >= (a.minRev || 0))
      .filter((a) => info.rev <= (a.maxRev || Number.MAX_SAFE_INTEGER));
    if (tips.length > 0) {
      viewPrefs.lastIndex = tips[0].index;
      userPrefs.lastTip = Date.now();
      window.localStorage.setItem('tipsPreferences', JSON.stringify(userPrefs));
      UI.dialog(
        `

        `,
        { width: 800, height: 600, title: 'Did you know ?' }
      );
    }
  }

  async function addPageHelp(options = {}) {
    const { iconSize = 'fa-3x' } = options;
    let info = await getViewInfo();
    if (!info._id) return;

    await fetch(`${pagesURL + info._id}/index.html`, { method: 'HEAD' })
      .then(async () => {})
      .catch();

    let target = document.getElementById('modules-grid');
    let div = document.createElement('DIV');
    div.innerHTML = `

      `;
    div.style.zIndex = 99;
    div.style.position = 'fixed';

    div.addEventListener('click', () => {
      UI.dialog(
        `

        `,
        { width: 900, height: 700, title: 'Information about the page' }
      );
    });

    target.prepend(div);

    window.addEventListener(
      'keypress',
      (event) => {
        if (
          event.altKey &&
          event.shiftKey &&
          event.ctrlKey &&
          event.keyCode === 8
        ) {
          UI.dialog(
            `

            `,
            { width: 900, height: 700, title: 'Information about the page' }
          );
        }
      },
      false
    );
  }

  return {
    showTips,
    addPageHelp
  };
});
<|endoftext|>


<commit_before>import React, { useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";

import { combineEventHandlers } from "@hig/utils";

import { AVAILABLE_ANCHOR_POINTS } from "./anchorPoints";
import ContainerTransition from "./behaviors/ContainerTransition";
import FlyoutPresenter from "./presenters/FlyoutPresenter";
import getCoordinates, { DEFAULT_COORDINATES } from "./getCoordinates";
import DelayedHoverBehavior from "./behaviors/DelayedHoverBehavior";
import PanelContainerPresenter from "./presenters/PanelContainerPresenter";
import PanelPresenter from "./presenters/PanelPresenter";

/** @typedef {import("./getCoordinates").Coordinates} Coordinates */

/**
 * @typedef {Object} PanelRendererPayload
 * @property {function(HTMLElement): void} innerRef
 * @property {function(): void} hideFlyout
 * @property {JSX} [content]
 * @property {function(UIEvent): void} [handleScroll]
 * @property {number} [maxHeight]
 */

/**
 * @typedef {Object} State
 * @property {HTMLElement} [actionRef]
 * @property {boolean} open Used to direct the flyout's transition behavior
 * @property {HTMLElement} [panelRef]
 * @property {SVGSVGElement} [pointerRef]
 * @property {HTMLDivElement} [wrapperRef]
 */

const Flyout = props => {
  const [open, setOpen] = useState(props.defaultOpen);
  const actionRef = useRef();
  const panelRef = useRef();
  const pointerRef = useRef();
  const wrapperRef = useRef();

  /**
   * @returns {Coordinates}
   */
  const getCoordinatesMethod = () => {
    const { alterCoordinates, anchorPoint, fallbackAnchorPoints } = props;

    if (
      !actionRef.current ||
      !panelRef.current ||
      !pointerRef.current ||
      typeof window === "undefined"
    ) {
      return DEFAULT_COORDINATES;
    }

    const actionRect = actionRef.current.getBoundingClientRect();
    const panelRect = panelRef.current.getBoundingClientRect();
    const pointerRect = pointerRef.current.getBoundingClientRect();
    const viewportRect = window.document.documentElement.getBoundingClientRect();
    const coordinates = getCoordinates({
      anchorPoint,
      actionRect,
      fallbackAnchorPoints,
      panelRect,
      pointerRect,
      viewportRect
    });

    if (alterCoordinates) {
      const rects = {
        actionRect,
        panelRect,
        pointerRect,
        viewportRect
      };
      return alterCoordinates(coordinates, rects);
    }

    return coordinates;
  };

  /**
   * @param {boolean} open
   */
  const setOpenMethod = openRef => {
    const { onClose, onOpen } = props;
    if (openRef && onOpen) {
      onOpen();
    } else if (!openRef && onClose) {
      onClose();
    }
    setOpen(openRef);
  };

  const isOpenControlled = () => props.open !== undefined;

  const isOpen = () => (isOpenControlled() ? props.open : open);

  const handleChildMouseEnter = () => {
    if (props.openOnHover) {
      setOpenMethod(true);
    }
  };

  const handleChildMouseLeave = () => {
    if (props.openOnHover) {
      setOpenMethod(false);
    }
  };

  /**
   * @param {HTMLElement} actionRef
   */
  const refAction = actionRefParam => {
    actionRef.current = actionRefParam;
  };

  /**
   * @param {SVGSVGElement} pointerRef
   */
  const refPointer = pointerRefParam => {
    pointerRef.current = pointerRefParam;
  };

  /**
   * @param {HTMLElement} panelRef
   */
  const refPanel = panelRefParam => {
    panelRef.current = panelRefParam;
  };

  /**
   * @param {HTMLDivElement} wrapperRef
   */
  const refWrapper = wrapperRefParam => {
    wrapperRef.current = wrapperRefParam;
  };

  const hideFlyout = () => {
    setOpenMethod(false);
  };

  const toggleOpen = () => {
    setOpenMethod(!open);
  };

  const renderContent = () => {
    const { content } = props;

    if (typeof content === "function") {
      return content({ hideFlyout });
    }

    return content;
  };

  /**
   * @param {MouseEvent} event
   */
  const handleBodyClick = event => {
    const { onClickOutside } = props;
    const flyoutClicked =
      event.target === wrapperRef || wrapperRef.current.contains(event.target);

    if (flyoutClicked || !isOpen()) return;
    if (onClickOutside) onClickOutside(event);
    if (!isOpenControlled()) toggleOpen();
  };

  const handleChildClick = () => {
    if (!isOpenControlled()) {
      toggleOpen();
    }
  };

  /**
   * @returns {PanelRendererPayload}
   */
  const createPanelPayload = () => {
    const { maxHeight, onScroll, stylesheet, ...otherProps } = props;
    const { className } = otherProps;

    return {
      hideFlyout,
      maxHeight,
      content: renderContent(),
      handleScroll: onScroll,
      innerRef: refPanel,
      className,
      stylesheet
    };
  };

  const renderPanel = ({ transitionStatus }) => {
    const { panel } = props;

    if (typeof panel === "function") {
      return panel({
        ...createPanelPayload(),
        transitionStatus
      });
    }

    return panel;
  };

  const renderChildren = (onMouseEnter, onMouseLeave) => {
    const { children } = props;
    if (typeof children === "function") {
      return children({ handleClick: handleChildClick });
    }

    if (React.Children.count(children) === 1) {
      return React.cloneElement(children, {
        onClick: combineEventHandlers(handleChildClick, children.props.onClick),
        onMouseEnter,
        onMouseLeave
      });
    }

    return children;
  };

  const renderPresenter = transitionStatus => {
    const { openOnHoverDelay, pointer, stylesheet, ...otherProps } = props;
    const { className } = otherProps;
    const panel = renderPanel({ transitionStatus });
    const {
      anchorPoint,
      containerPosition,
      pointerPosition
    } = getCoordinatesMethod();
    return (
      <DelayedHoverBehavior
        onMouseEnter={handleChildMouseEnter}
        onMouseLeave={handleChildMouseLeave}
        openOnHoverDelay={openOnHoverDelay}
      >
        {({ onMouseEnter, onMouseLeave }) => (
          <FlyoutPresenter
            anchorPoint={anchorPoint}
            className={className}
            stylesheet={stylesheet}
            containerPosition={containerPosition}
            panel={panel}
            pointer={pointer}
            pointerPosition={pointerPosition}
            refAction={refAction}
            refPointer={refPointer}
            refWrapper={refWrapper}
            transitionStatus={transitionStatus}
          >
            {renderChildren(onMouseEnter, onMouseLeave)}
          </FlyoutPresenter>
        )}
      </DelayedHoverBehavior>
    );
  };

  useEffect(() => {
    window.document.body.addEventListener("click", handleBodyClick);
    return () => {
      window.document.body.removeEventListener("click", handleBodyClick);
    };
  }, []);

  return (
    <ContainerTransition open={isOpen()}>{renderPresenter}</ContainerTransition>
  );
};

Flyout.displayName = "Flyout";

Flyout.propTypes = {
  /** Manipulate flyout coordinates before rendering */
  alterCoordinates: PropTypes.func,
  /** Where the flyout will be anchored relative to target */
  anchorPoint: PropTypes.oneOf(AVAILABLE_ANCHOR_POINTS),
  /** Target component to open the flyout. Can be either a node or a render function */
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
  /** Content for the flyout. Can be either a node or a render function */
  content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
  /** Default uncontrolled open state */
  defaultOpen: PropTypes.bool,
  /**
   * When the flyout overflows the viewport, it'll attempt to
   * use the given anchor points in order to keep the flyout
   * within the viewport.
   */
  fallbackAnchorPoints: PropTypes.arrayOf(
    PropTypes.oneOf(AVAILABLE_ANCHOR_POINTS)
  ),
  /** Renders a custom flyout panel. Can be either a node or a render function */
  panel: PropTypes.func,
  /** A custom pointer */
  pointer: PropTypes.node,
  /** Max height of the flyout content, in pixels */
  maxHeight: PropTypes.number,
  /** Function called when the flyout is open, and a click event occurs outside the flyout */
  onClickOutside: PropTypes.func,
  /** Function called when the flyout closes */
  onClose: PropTypes.func,
  /** Function called when the flyout opens */
  onOpen: PropTypes.func,
  /** Function called when the flyout panel is scrolled */
  onScroll: PropTypes.func,
  /** When provided, it overrides the flyout's open state */
  open: PropTypes.bool,
  /** Whether flyout should open when the target is hovered over */
  openOnHover: PropTypes.bool,
  /**
   * If openOnHover is true, this prop will determine the delay
   * from when mouseEnter begins until the flyout visually opens
   */
  openOnHoverDelay: PropTypes.number,
  /** Function to modify the component's styles */
  stylesheet: PropTypes.func
};

Flyout.defaultProps = {
  anchorPoint: DEFAULT_COORDINATES.anchorPoint,
  defaultOpen: false,
  fallbackAnchorPoints: AVAILABLE_ANCHOR_POINTS,
  openOnHover: false,
  openOnHoverDelay: 500,
  /**
   * @param {PanelRendererPayload} payload
   */
  // eslint-disable-next-line react/prop-types
  panel({ innerRef, content, handleScroll, maxHeight, className, stylesheet }) {
    return (
      <PanelContainerPresenter
        innerRef={innerRef}
        maxHeight={maxHeight}
        className={className}
        stylesheet={stylesheet}
      >
        <PanelPresenter
          onScroll={handleScroll}
          className={className}
          stylesheet={stylesheet}
        >
          {content}
        </PanelPresenter>
      </PanelContainerPresenter>
    );
  }
};

export default Flyout;
<commit_msg>fix: improve useEffect manage state<commit_after>import React, { useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";

import { combineEventHandlers } from "@hig/utils";

import { AVAILABLE_ANCHOR_POINTS } from "./anchorPoints";
import ContainerTransition from "./behaviors/ContainerTransition";
import FlyoutPresenter from "./presenters/FlyoutPresenter";
import getCoordinates, { DEFAULT_COORDINATES } from "./getCoordinates";
import DelayedHoverBehavior from "./behaviors/DelayedHoverBehavior";
import PanelContainerPresenter from "./presenters/PanelContainerPresenter";
import PanelPresenter from "./presenters/PanelPresenter";

/** @typedef {import("./getCoordinates").Coordinates} Coordinates */

/**
 * @typedef {Object} PanelRendererPayload
 * @property {function(HTMLElement): void} innerRef
 * @property {function(): void} hideFlyout
 * @property {JSX} [content]
 * @property {function(UIEvent): void} [handleScroll]
 * @property {number} [maxHeight]
 */

/**
 * @typedef {Object} State
 * @property {HTMLElement} [actionRef]
 * @property {boolean} open Used to direct the flyout's transition behavior
 * @property {HTMLElement} [panelRef]
 * @property {SVGSVGElement} [pointerRef]
 * @property {HTMLDivElement} [wrapperRef]
 */

const Flyout = props => {
  const [open, setOpen] = useState(props.defaultOpen);
  const actionRef = useRef();
  const panelRef = useRef();
  const pointerRef = useRef();
  const wrapperRef = useRef();

  /**
   * @returns {Coordinates}
   */
  const getCoordinatesMethod = () => {
    const { alterCoordinates, anchorPoint, fallbackAnchorPoints } = props;

    if (
      !actionRef.current ||
      !panelRef.current ||
      !pointerRef.current ||
      typeof window === "undefined"
    ) {
      return DEFAULT_COORDINATES;
    }

    const actionRect = actionRef.current.getBoundingClientRect();
    const panelRect = panelRef.current.getBoundingClientRect();
    const pointerRect = pointerRef.current.getBoundingClientRect();
    const viewportRect = window.document.documentElement.getBoundingClientRect();
    const coordinates = getCoordinates({
      anchorPoint,
      actionRect,
      fallbackAnchorPoints,
      panelRect,
      pointerRect,
      viewportRect
    });

    if (alterCoordinates) {
      const rects = {
        actionRect,
        panelRect,
        pointerRect,
        viewportRect
      };
      return alterCoordinates(coordinates, rects);
    }

    return coordinates;
  };

  /**
   * @param {boolean} open
   */
  const setOpenMethod = openRef => {
    const { onClose, onOpen } = props;
    if (openRef && onOpen) {
      onOpen();
    } else if (!openRef && onClose) {
      onClose();
    }
    setOpen(openRef);
  };

  const isOpenControlled = () => props.open !== undefined;

  const isOpen = () => (isOpenControlled() ? props.open : open);

  const handleChildMouseEnter = () => {
    if (props.openOnHover) {
      setOpenMethod(true);
    }
  };

  const handleChildMouseLeave = () => {
    if (props.openOnHover) {
      setOpenMethod(false);
    }
  };

  /**
   * @param {HTMLElement} actionRef
   */
  const refAction = actionRefParam => {
    actionRef.current = actionRefParam;
  };

  /**
   * @param {SVGSVGElement} pointerRef
   */
  const refPointer = pointerRefParam => {
    pointerRef.current = pointerRefParam;
  };

  /**
   * @param {HTMLElement} panelRef
   */
  const refPanel = panelRefParam => {
    panelRef.current = panelRefParam;
  };

  /**
   * @param {HTMLDivElement} wrapperRef
   */
  const refWrapper = wrapperRefParam => {
    wrapperRef.current = wrapperRefParam;
  };

  const hideFlyout = () => {
    setOpenMethod(false);
  };

  const toggleOpen = () => {
    setOpenMethod(!open);
  };

  const renderContent = () => {
    const { content } = props;

    if (typeof content === "function") {
      return content({ hideFlyout });
    }

    return content;
  };

  /**
   * @param {MouseEvent} event
   */
  const handleBodyClick = event => {
    const { onClickOutside } = props;
    const flyoutClicked =
      event.target === wrapperRef || wrapperRef.current.contains(event.target);

    if (flyoutClicked || !isOpen()) return;
    if (onClickOutside) onClickOutside(event);
    if (!isOpenControlled()) toggleOpen();
  };

  const handleChildClick = () => {
    if (!isOpenControlled()) {
      toggleOpen();
    }
  };

  /**
   * @returns {PanelRendererPayload}
   */
  const createPanelPayload = () => {
    const { maxHeight, onScroll, stylesheet, ...otherProps } = props;
    const { className } = otherProps;

    return {
      hideFlyout,
      maxHeight,
      content: renderContent(),
      handleScroll: onScroll,
      innerRef: refPanel,
      className,
      stylesheet
    };
  };

  const renderPanel = ({ transitionStatus }) => {
    const { panel } = props;

    if (typeof panel === "function") {
      return panel({
        ...createPanelPayload(),
        transitionStatus
      });
    }

    return panel;
  };

  const renderChildren = (onMouseEnter, onMouseLeave) => {
    const { children } = props;
    if (typeof children === "function") {
      return children({ handleClick: handleChildClick });
    }

    if (React.Children.count(children) === 1) {
      return React.cloneElement(children, {
        onClick: combineEventHandlers(handleChildClick, children.props.onClick),
        onMouseEnter,
        onMouseLeave
      });
    }

    return children;
  };

  const renderPresenter = transitionStatus => {
    const { openOnHoverDelay, pointer, stylesheet, ...otherProps } = props;
    const { className } = otherProps;
    const panel = renderPanel({ transitionStatus });
    const {
      anchorPoint,
      containerPosition,
      pointerPosition
    } = getCoordinatesMethod();
    return (
      <DelayedHoverBehavior
        onMouseEnter={handleChildMouseEnter}
        onMouseLeave={handleChildMouseLeave}
        openOnHoverDelay={openOnHoverDelay}
      >
        {({ onMouseEnter, onMouseLeave }) => (
          <FlyoutPresenter
            anchorPoint={anchorPoint}
            className={className}
            stylesheet={stylesheet}
            containerPosition={containerPosition}
            panel={panel}
            pointer={pointer}
            pointerPosition={pointerPosition}
            refAction={refAction}
            refPointer={refPointer}
            refWrapper={refWrapper}
            transitionStatus={transitionStatus}
          >
            {renderChildren(onMouseEnter, onMouseLeave)}
          </FlyoutPresenter>
        )}
      </DelayedHoverBehavior>
    );
  };

  useEffect(() => {
    window.document.body.addEventListener("click", handleBodyClick);
    return () => {
      window.document.body.removeEventListener("click", handleBodyClick);
    };
  });

  return (
    <ContainerTransition open={isOpen()}>{renderPresenter}</ContainerTransition>
  );
};

Flyout.displayName = "Flyout";

Flyout.propTypes = {
  /** Manipulate flyout coordinates before rendering */
  alterCoordinates: PropTypes.func,
  /** Where the flyout will be anchored relative to target */
  anchorPoint: PropTypes.oneOf(AVAILABLE_ANCHOR_POINTS),
  /** Target component to open the flyout. Can be either a node or a render function */
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
  /** Content for the flyout. Can be either a node or a render function */
  content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
  /** Default uncontrolled open state */
  defaultOpen: PropTypes.bool,
  /**
   * When the flyout overflows the viewport, it'll attempt to
   * use the given anchor points in order to keep the flyout
   * within the viewport.
   */
  fallbackAnchorPoints: PropTypes.arrayOf(
    PropTypes.oneOf(AVAILABLE_ANCHOR_POINTS)
  ),
  /** Renders a custom flyout panel. Can be either a node or a render function */
  panel: PropTypes.func,
  /** A custom pointer */
  pointer: PropTypes.node,
  /** Max height of the flyout content, in pixels */
  maxHeight: PropTypes.number,
  /** Function called when the flyout is open, and a click event occurs outside the flyout */
  onClickOutside: PropTypes.func,
  /** Function called when the flyout closes */
  onClose: PropTypes.func,
  /** Function called when the flyout opens */
  onOpen: PropTypes.func,
  /** Function called when the flyout panel is scrolled */
  onScroll: PropTypes.func,
  /** When provided, it overrides the flyout's open state */
  open: PropTypes.bool,
  /** Whether flyout should open when the target is hovered over */
  openOnHover: PropTypes.bool,
  /**
   * If openOnHover is true, this prop will determine the delay
   * from when mouseEnter begins until the flyout visually opens
   */
  openOnHoverDelay: PropTypes.number,
  /** Function to modify the component's styles */
  stylesheet: PropTypes.func
};

Flyout.defaultProps = {
  anchorPoint: DEFAULT_COORDINATES.anchorPoint,
  defaultOpen: false,
  fallbackAnchorPoints: AVAILABLE_ANCHOR_POINTS,
  openOnHover: false,
  openOnHoverDelay: 500,
  /**
   * @param {PanelRendererPayload} payload
   */
  // eslint-disable-next-line react/prop-types
  panel({ innerRef, content, handleScroll, maxHeight, className, stylesheet }) {
    return (
      <PanelContainerPresenter
        innerRef={innerRef}
        maxHeight={maxHeight}
        className={className}
        stylesheet={stylesheet}
      >
        <PanelPresenter
          onScroll={handleScroll}
          className={className}
          stylesheet={stylesheet}
        >
          {content}
        </PanelPresenter>
      </PanelContainerPresenter>
    );
  }
};

export default Flyout;
<|endoftext|>


<commit_before>var DL_Util = DL_Util || {};

// predefined noop so we can make efficient self destroying functions and such
DL_Util.noop = function (){}

/******************
GAME-SPECIFIC UTILITIES
******************/

// intersect check with square bounding box
// currently designed to work with pixi.js display objects
DL_Util.boxIntersect = function (a, b) {
    return (a.x < b.x + b.width && a.x + a.width > b.x &&
        a.y < b.y + b.height && a.y + a.height > b.y);
}

// based on observed behavior, it looks like this takes radius instead of width. May be dependent on anchor point
DL_Util.circleIntersect = function (x1, y1, r1, x2, y2, r2) {
    var xd = x1 - x2;
    var yd = y1 - y2;
    var rt = r2 + r1;
    return (xd * xd + yd * yd <= rt * rt);
}

// function to deal with number sequences in JSON spritesheets. Flash exports png sequences with 4 digit suffixes, so use this to determine which one to use when looping through spritesheet frames. Useful in PIXI.js when loading movieclip textures.
DL_Util.numberSuffix = function (i) {
    var num;
    if (i < 9) {
        num = "000" + (i + 1);
    } else if (i < 99) {
        num = "00" + (i + 1);
    } else if (i < 999) {
        num = "0" + (i + 1);
    } else {
        num = i + 1;
    }
    return num;
}

/******************
PLATFORM / CAPABILITY DETECTION
******************/

// CURRENTLY PREFERRED METHOD FOR TESTING FOR TOUCH CAPABILITIES
DL_Util.isTouchDevice = function () {
   var el = document.createElement('div');
   el.setAttribute('ongesturestart', 'return;');
   if(typeof el.ongesturestart === "function"){
      return true;
   }else {
      return false;
   }
}

// User Agent Sniffing method of mobile detection - not optimal
DL_Util.mobilecheck = function() {
    var check = false;
    (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check=true})(navigator.userAgent||navigator.vendor||window.opera);
    return check;
};

// testing touch capability detection - don't typically use this one for some reason :/
DL_Util.isMobile = function () {
  try{ document.createEvent("TouchEvent"); return true; }
  catch(e){ return false; }
}

/******************
CONVERSIONS
******************/

DL_Util.rgbToHex = function (r, g, b) {
    return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}

// converting color types
DL_Util.hexToRgb = function (hex) {
    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
    var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    hex = hex.replace(shorthandRegex, function(r, g, b) {
        return r + r + g + g + b + b;
    });

    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}

// degrees to radians
DL_Util.d2r = function (d) {
    return d * (Math.PI/180);
}

// round input to nearest quarter
DL_Util.roundNearQtr = function (number) {
    return (Math.round(number * 4) / 4).toFixed(2);
};

/******************
RANDOMIZATION
******************/

DL_Util.randFromArray = function (array) {
    return array[Math.floor(Math.random() * array.length)];
}

DL_Util.randFromObject = function (obj) {
    var result;
    var count = 0;
    for (var prop in obj) {
        if (Math.random() < 1/++count) {
           result = prop;
        }
    }
    return result;
}

/*
 * Returns a random integer between min (inclusive) and max (inclusive)
 * Using Math.round() will give you a non-uniform distribution!
 */
DL_Util.getRandomInt = function (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

DL_Util.randPosNeg = function () {
    var value = Math.random() >= 0.5 ? 1 : -1;
    return value;
}

/*
 * Add a shuffle function to Array object prototype
 * Usage :
 *  var tmpArray = ["a", "b", "c", "d", "e"];
 *  tmpArray.shuffle();
 */
DL_Util.shuffle = function (array){
    var i = array.length, j, temp;
    if ( i === 0 ) {
        return;
    }
    while ( --i ) {
        j = Math.floor( Math.random() * ( i + 1 ) );
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

/******************
PARSING
******************/

/*  get variable from query string
    so running getQueryVariable('dest') on
    www.mysite.com/default.aspx?dest=aboutus.aspx
    would return
    "aboutus.aspx"
*/
DL_Util.getQueryVariable = function (variable) {
    var query = window.location.search.substring(1);
    var vars = query.split('&');
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        if (decodeURIComponent(pair[0]) == variable) {
            return decodeURIComponent(pair[1]);
        }
    }
    console.log('Query variable %s not found', variable);
}

/*
    Get file type from extension. Removes query strings that come after the file request as well. Does NOT deal with invalid inputs at the moment, so should probably validate that input equals output
*/
DL_Util.getFileType = function (url) {
    var fileType = url.split('?').shift().split('.').pop().toLowerCase();
    return fileType;
}

/******************
VALIDATION
******************/

// check if value is an integer
DL_Util.isInt = function (value) {
    return !isNaN(value) &&
            parseInt(Number(value)) == value &&
            !isNaN(parseInt(value, 10));
}

/******************
TEXT AND LAYOUT
******************/

// takes a jquery selector and inserts a non-breaking space character between the last two words. Useful for headlines. Commented out while unused, so as not to cause errors due to the jQuery dependency

/*
DL_Util.preventWidows = function (selector) {
    selector.each(function() {
        var wordArray = $(this).text().split(" ");
        if (wordArray.length > 1) {
            wordArray[wordArray.length-2] += " " + wordArray[wordArray.length-1];
            wordArray.pop();
            $(this).html(wordArray.join(" "));
        }
    });
}
*/

/* UNTESTED!!
    In theory sets the source of the first img element to the one you want to preload. Probably not the best way of doing things, might require there to be an img element in the dom that is hidden test before using in production!!
*/
/*
DL_Util.preloadImages = function (array) {
    $(array).each(function(){
        $("<img />")[0].src = this;
    });
}
*/

/******************
POLYFILLS, NOT NAMESPACED
******************/

/*
polyfill for String.prototype.includes method in ecmascript 6 spec.
This is not well supported in all browsers yet.
*/
if (!String.prototype.includes) {
  String.prototype.includes = function() {'use strict';
    return String.prototype.indexOf.apply(this, arguments) !== -1;
  };
}

// In case we forget to take out console statements. IE becomes very unhappy when we forget. Let's not make IE unhappy
//console.log("==================== console not polyfilled! ");
if (typeof(console) === 'undefined') {
    var console = {};
    var consolearray = [];
    console.log = console.groupCollapsed = console.error = console.info = console.debug = console.warn = console.trace = console.dir = console.dirxml = console.group = console.groupEnd = console.time = console.timeEnd = console.assert = console.profile = function(msg) {consolearray.push(msg)};
};<commit_msg>Added Math.sign polyfill<commit_after>var DL_Util = DL_Util || {};

// predefined noop so we can make efficient self destroying functions and such
DL_Util.noop = function (){}

/******************
GAME-SPECIFIC UTILITIES
******************/

// intersect check with square bounding box
// currently designed to work with pixi.js display objects
DL_Util.boxIntersect = function (a, b) {
    return (a.x < b.x + b.width && a.x + a.width > b.x &&
        a.y < b.y + b.height && a.y + a.height > b.y);
}

// based on observed behavior, it looks like this takes radius instead of width. May be dependent on anchor point
DL_Util.circleIntersect = function (x1, y1, r1, x2, y2, r2) {
    var xd = x1 - x2;
    var yd = y1 - y2;
    var rt = r2 + r1;
    return (xd * xd + yd * yd <= rt * rt);
}

// function to deal with number sequences in JSON spritesheets. Flash exports png sequences with 4 digit suffixes, so use this to determine which one to use when looping through spritesheet frames. Useful in PIXI.js when loading movieclip textures.
DL_Util.numberSuffix = function (i) {
    var num;
    if (i < 9) {
        num = "000" + (i + 1);
    } else if (i < 99) {
        num = "00" + (i + 1);
    } else if (i < 999) {
        num = "0" + (i + 1);
    } else {
        num = i + 1;
    }
    return num;
}

/******************
PLATFORM / CAPABILITY DETECTION
******************/

// CURRENTLY PREFERRED METHOD FOR TESTING FOR TOUCH CAPABILITIES
DL_Util.isTouchDevice = function () {
   var el = document.createElement('div');
   el.setAttribute('ongesturestart', 'return;');
   if(typeof el.ongesturestart === "function"){
      return true;
   }else {
      return false;
   }
}

// User Agent Sniffing method of mobile detection - not optimal
DL_Util.mobilecheck = function() {
    var check = false;
    (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check=true})(navigator.userAgent||navigator.vendor||window.opera);
    return check;
};

// testing touch capability detection - don't typically use this one for some reason :/
DL_Util.isMobile = function () {
  try{ document.createEvent("TouchEvent"); return true; }
  catch(e){ return false; }
}

/******************
CONVERSIONS
******************/

DL_Util.rgbToHex = function (r, g, b) {
    return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}

// converting color types
DL_Util.hexToRgb = function (hex) {
    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
    var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    hex = hex.replace(shorthandRegex, function(r, g, b) {
        return r + r + g + g + b + b;
    });

    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}

// degrees to radians
DL_Util.d2r = function (d) {
    return d * (Math.PI/180);
}

// round input to nearest quarter
DL_Util.roundNearQtr = function (number) {
    return (Math.round(number * 4) / 4).toFixed(2);
};

/******************
RANDOMIZATION
******************/

DL_Util.randFromArray = function (array) {
    return array[Math.floor(Math.random() * array.length)];
}

DL_Util.randFromObject = function (obj) {
    var result;
    var count = 0;
    for (var prop in obj) {
        if (Math.random() < 1/++count) {
           result = prop;
        }
    }
    return result;
}

/*
 * Returns a random integer between min (inclusive) and max (inclusive)
 * Using Math.round() will give you a non-uniform distribution!
 */
DL_Util.getRandomInt = function (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

DL_Util.randPosNeg = function () {
    var value = Math.random() >= 0.5 ? 1 : -1;
    return value;
}

/*
 * Add a shuffle function to Array object prototype
 * Usage :
 *  var tmpArray = ["a", "b", "c", "d", "e"];
 *  tmpArray.shuffle();
 */
DL_Util.shuffle = function (array){
    var i = array.length, j, temp;
    if ( i === 0 ) {
        return;
    }
    while ( --i ) {
        j = Math.floor( Math.random() * ( i + 1 ) );
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

/******************
PARSING
******************/

/*  get variable from query string
    so running getQueryVariable('dest') on
    www.mysite.com/default.aspx?dest=aboutus.aspx
    would return
    "aboutus.aspx"
*/
DL_Util.getQueryVariable = function (variable) {
    var query = window.location.search.substring(1);
    var vars = query.split('&');
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        if (decodeURIComponent(pair[0]) == variable) {
            return decodeURIComponent(pair[1]);
        }
    }
    console.log('Query variable %s not found', variable);
}

/*
    Get file type from extension. Removes query strings that come after the file request as well. Does NOT deal with invalid inputs at the moment, so should probably validate that input equals output
*/
DL_Util.getFileType = function (url) {
    var fileType = url.split('?').shift().split('.').pop().toLowerCase();
    return fileType;
}

/******************
VALIDATION
******************/

// check if value is an integer
DL_Util.isInt = function (value) {
    return !isNaN(value) &&
            parseInt(Number(value)) == value &&
            !isNaN(parseInt(value, 10));
}

/******************
TEXT AND LAYOUT
******************/

// takes a jquery selector and inserts a non-breaking space character between the last two words. Useful for headlines. Commented out while unused, so as not to cause errors due to the jQuery dependency

/*
DL_Util.preventWidows = function (selector) {
    selector.each(function() {
        var wordArray = $(this).text().split(" ");
        if (wordArray.length > 1) {
            wordArray[wordArray.length-2] += " " + wordArray[wordArray.length-1];
            wordArray.pop();
            $(this).html(wordArray.join(" "));
        }
    });
}
*/

/* UNTESTED!!
    In theory sets the source of the first img element to the one you want to preload. Probably not the best way of doing things, might require there to be an img element in the dom that is hidden test before using in production!!
*/
/*
DL_Util.preloadImages = function (array) {
    $(array).each(function(){
        $("<img />")[0].src = this;
    });
}
*/

/******************
POLYFILLS, NOT NAMESPACED
******************/

/*
polyfill for String.prototype.includes method in ecmascript 6 spec.
This is not well supported in all browsers yet.
*/
if (!String.prototype.includes) {
  String.prototype.includes = function() {'use strict';
    return String.prototype.indexOf.apply(this, arguments) !== -1;
  };
}

// In case we forget to take out console statements. IE becomes very unhappy when we forget. Let's not make IE unhappy
//console.log("==================== console not polyfilled! ");
if (typeof(console) === 'undefined') {
    var console = {};
    var consolearray = [];
    console.log = console.groupCollapsed = console.error = console.info = console.debug = console.warn = console.trace = console.dir = console.dirxml = console.group = console.groupEnd = console.time = console.timeEnd = console.assert = console.profile = function(msg) {consolearray.push(msg)};
};

/*
Polyfill for Math.sign method in ecmascript 6 spec.
This is not yet supported in IE or Safari
*/
Math.sign = Math.sign || function(x) {
  x = +x; // convert to a number
  if (x === 0 || isNaN(x)) {
    return x;
  }
  return x > 0 ? 1 : -1;
}<|endoftext|>


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

import NavBar from './NavBar';
import Scene from './Scene';

import { getNavigationDelegate, replaceInstanceEventedProps } from './utils';

import {
  View,
  StyleSheet,
  Navigator,
  Platform,
  BackAndroid,
} from 'react-native';

const VALID_EVENTED_PROPS = [
  'onPress',
  'onValueChange',
  'onChange',
  'onSelection',
  'onChangeText',
  'onBlur',
  'onFocus',
  'onSelectionChange',
  'onSubmitEditing',
];

export default class YANavigator extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      shouldHandleAndroidBack: props.shouldHandleAndroidBack,
    }
  }

  componentDidMount() {
    const navigatorMethods = [
      'getCurrentRoutes',
      'jumpBack',
      'jumpForward',
      'jumpTo',
      'push',
      'pop',
      'replace',
      'replaceAtIndex',
      'replacePrevious',
      'replacePreviousAndPop',
      'resetTo',
      'immediatelyResetRouteStack',
      'popToRoute',
      'popToTop',
    ];

    navigatorMethods.forEach((method) => {
      this[method] = this.refs.navigator[method];
    })

    this.refs.navigator.setShouldHandleAndroidBack = this.setShouldHandleAndroidBack.bind(this);

    if (Platform.OS === 'android') {
      this._backPressSub = BackAndroid.addEventListener('hardwareBackPress', () => {
        if (!this.state.shouldHandleAndroidBack) return false;

        const { navigator } = this.refs;
        const navState = navigator.state;
        const presentedComponent =
          navState.routeStack[navState.presentedIndex].component;
        const navigationDelegate = getNavigationDelegate(presentedComponent);

        if (navigationDelegate && navigationDelegate.onAndroidBackPress) {

          navigationDelegate.onAndroidBackPress(navigator);

          return true;
        } else if (navState.routeStack.length > 1) {
          navigator.pop();

          return true;
        }

       return false;
      });
    }
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      shouldHandleAndroidBack: nextProps.shouldHandleAndroidBack,
    })
  }

  componentWillUnmount() {
    if (Platform.OS === 'android') {
      this._backPressSub.remove();
      this._backPressSub = null;
    }
  }

  setShouldHandleAndroidBack(should) {
    this.setState({
      shouldHandleAndroidBack: should,
    });
  }

  _renderScene = (route, navigator) => {
    let Component;

    const { eachSceneProps } = this.props;

    if (typeof route.component === 'object') {
      Component = React.cloneElement(route.component, {
        navigator,
        ...route.props,
        ...eachSceneProps,
      });
    } else if (typeof route.component === 'function') {
      Component = (
        <route.component
          navigator={navigator}
          {...route.props}
          {...eachSceneProps}
          />
        )
    }

    return Component;
  };

  _configureScene = (route) => {
    const navigationDelegate = getNavigationDelegate(route.component);

    return (navigationDelegate && navigationDelegate.sceneConfig) ||
            this.props.defaultSceneConfig;
  };

  _renderNavigationBar(
    navBarStyle,
    isHiddenOnInit,
    navBarBackBtn,
    navBarUnderlay,
  ) {

    return (
      <NavBar
        isHiddenOnInit={isHiddenOnInit}
        style={navBarStyle}
        backIcon={navBarBackBtn.icon}
        underlay={navBarUnderlay}
        routeMapper={{
          navBarBackgroundColor: (route) => {
            let navBarBackgroundColor = '';

            const navigationDelegate = getNavigationDelegate(route.component);

            if (navigationDelegate &&
                navigationDelegate.navBarBackgroundColor) {

                navBarBackgroundColor =
                  navigationDelegate.navBarBackgroundColor;
            }

            return navBarBackgroundColor;
          },

          LeftPart: (route, navigator, index, state) => {
            let LeftPart = null;

            const navigationDelegate = getNavigationDelegate(route.component);

            if (navigationDelegate &&
                navigationDelegate.renderNavBarLeftPart) {

              navigationDelegate._events = navigationDelegate._events || [];

              LeftPart = navigationDelegate.renderNavBarLeftPart(route.props || {});

              if (!LeftPart) return null;

              if (typeof LeftPart === 'object' && React.isValidElement(LeftPart)) {
                const children =
                  React.Children.toArray(LeftPart.props.children).map((child) => {
                    const {reactElement, events} =
                      replaceInstanceEventedProps(
                        child,
                        VALID_EVENTED_PROPS,
                        navigationDelegate._events,
                        route,
                        navigator.navigationContext
                      )

                    navigationDelegate._events.concat(events.filter((event) => {
                      return navigationDelegate._events.indexOf(event) < 0;
                    }));

                    return reactElement
                  })

                const {reactElement, events} =
                  replaceInstanceEventedProps(
                    LeftPart,
                    VALID_EVENTED_PROPS,
                    navigationDelegate._events,
                    route,
                    navigator.navigationContext
                  )

                navigationDelegate._events.concat(events.filter((event) => {
                  return navigationDelegate._events.indexOf(event) < 0;
                }));

                LeftPart = reactElement;

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_leftPart`;

                LeftPart = React.cloneElement(LeftPart, {ref}, children)
              } else if (typeof LeftPart === 'function') {
                const props = {};

                LeftPart.propTypes && VALID_EVENTED_PROPS.forEach((validProp) => {
                  if (LeftPart.propTypes[validProp]) {
                    const event = `onNavBarLeftPart${validProp.replace(/^on/, '')}`

                    if (navigationDelegate._events.indexOf(event) < 0) {
                      navigationDelegate._events.push(event)
                    }

                    props[validProp] = (e) => navigator.navigationContext
                      .emit(event, {route, e});
                  }
                })

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_leftPart`;

                LeftPart = <LeftPart ref={ref} {...props} />
              }
            } else {
              if (index > 0) {
                // tell navBar to render back button
                const prevRoute = state.routeStack[index - 1];
                const previousComponent = prevRoute.component;
                const previousNavigationDelegate = getNavigationDelegate(previousComponent);

                let backBtnText = '';

                if (previousNavigationDelegate &&
                    typeof previousNavigationDelegate.backBtnText === 'string') {

                  backBtnText = previousNavigationDelegate.backBtnText;
                }

                const navigationDelegate = state.routeStack[index] &&
                  getNavigationDelegate(state.routeStack[index].component);

                const backBtnConfig = {
                  isBackBtn: true,
                  text: backBtnText,
                  onPress: navigator.pop,
                  textStyle: navBarBackBtn.textStyle,
                }

                if (navigationDelegate) {
                  if (navigationDelegate.navBarBackBtnColor) {
                    backBtnConfig.textStyle.color =
                      navigationDelegate.navBarBackBtnColor;
                  }

                  if (navigationDelegate.overrideBackBtnPress) {
                    navigationDelegate._events = navigationDelegate._events || [];

                    const event = 'onNavBarBackBtnPress'

                    if (navigationDelegate._events.indexOf(event) < 0) {
                      navigationDelegate._events.push(event)
                    }

                    backBtnConfig.onPress = () => navigator.navigationContext
                      .emit(event, {route});
                  }
                }

                return backBtnConfig;
              }
            }

            return LeftPart;
          },

          RightPart: (route, navigator) => {
            let RightPart = null;

            const navigationDelegate = getNavigationDelegate(route.component);

            if (navigationDelegate &&
                navigationDelegate.renderNavBarRightPart) {

              navigationDelegate._events = navigationDelegate._events || [];

              RightPart = navigationDelegate.renderNavBarRightPart(route.props || {});

              if (!RightPart) return null;

              if (typeof RightPart === 'object' && React.isValidElement(RightPart)) {
                const children =
                  React.Children.toArray(RightPart.props.children).map((child) => {
                    const {reactElement, events} =
                      replaceInstanceEventedProps(
                        child,
                        VALID_EVENTED_PROPS,
                        navigationDelegate._events,
                        route,
                        navigator.navigationContext
                      )

                    navigationDelegate._events.concat(events.filter((event) => {
                      return navigationDelegate._events.indexOf(event) < 0;
                    }));

                    return reactElement
                  })

                const {reactElement, events} =
                  replaceInstanceEventedProps(
                    RightPart,
                    VALID_EVENTED_PROPS,
                    navigationDelegate._events,
                    route,
                    navigator.navigationContext
                  )

                navigationDelegate._events.concat(events.filter((event) => {
                  return navigationDelegate._events.indexOf(event) < 0;
                }));

                RightPart = reactElement;

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_rightPart`;

                RightPart = React.cloneElement(RightPart, {ref}, children)
              } else if (typeof RightPart === 'function') {
                const props = {};

                RightPart.propTypes && VALID_EVENTED_PROPS.forEach((validProp) => {
                  if (RightPart.propTypes[validProp]) {
                    const event = `onNavBarRightPart${validProp.replace(/^on/, '')}`

                    if (navigationDelegate._events.indexOf(event) < 0) {
                      navigationDelegate._events.push(event)
                    }

                    props[validProp] = (e) => navigator.navigationContext
                      .emit(event, {route, e});
                  }
                })

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_rightPart`;

                RightPart = <RightPart ref={ref} {...props} />
              }
            }

            return RightPart;
          },

          Title: (route, navigator) => {
            let Title = null;

            const navigationDelegate = getNavigationDelegate(route.component);

            if (navigationDelegate &&
                navigationDelegate.renderTitle) {

              navigationDelegate._events = navigationDelegate._events || [];

              Title = navigationDelegate.renderTitle(route.props || {});

              if (!Title) return null;

              if (typeof Title === 'object' && React.isValidElement(Title)) {
                const children =
                  React.Children.toArray(Title.props.children).map((child) => {
                    const {reactElement, events} =
                      replaceInstanceEventedProps(
                        child,
                        VALID_EVENTED_PROPS,
                        navigationDelegate._events,
                        route,
                        navigator.navigationContext
                      )

                    navigationDelegate._events.concat(events.filter((event) => {
                      return navigationDelegate._events.indexOf(event) < 0;
                    }));

                    return reactElement
                  })

                const {reactElement, events} =
                  replaceInstanceEventedProps(
                    Title,
                    VALID_EVENTED_PROPS,
                    navigationDelegate._events,
                    route,
                    navigator.navigationContext
                  )

                navigationDelegate._events.concat(events.filter((event) => {
                  return navigationDelegate._events.indexOf(event) < 0;
                }));

                Title = reactElement;

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_title`;

                Title = React.cloneElement(Title, {ref}, children)
              } else if (typeof Title === 'function') {
                const props = {};

                Title.propTypes && VALID_EVENTED_PROPS.forEach((validProp) => {
                  if (Title.propTypes[validProp]) {
                    const event = `onNavBarTitle${validProp.replace(/^on/, '')}`

                    if (navigationDelegate._events.indexOf(event) < 0) {
                      navigationDelegate._events.push(event)
                    }

                    props[validProp] = (e) => navigator.navigationContext
                      .emit(event, {route, e});
                  }
                })

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_title`;

                Title = <Title ref={ref} {...props} />
              }
            }

            return Title;
          },
        }} />
    )
  }

  _onWillFocus = (route) => {
    const { navigator } = this.refs;
    const component = route.component;
    const state = navigator && navigator.state;

    if (state) {
      setTimeout(() => {
        const index = state.presentedIndex;

        for (let i = state.routeStack.length - 1; i > index; i--) {
          if (state.routeStack[i] &&
              state.routeStack[i].props &&
              state.routeStack[i].props.onSceneWillBlur) {

            state.routeStack[i].props.onSceneWillBlur(
              route.__navigatorRouteID !== undefined
            );
          }
        }
      }, 100);
    }

    const navBar = navigator && navigator._navBar;
    const navigationDelegate = getNavigationDelegate(component);

    if (navBar && navigationDelegate) {
      navigationDelegate.navBarIsHidden ?
        navBar.hide() :
        navBar.show();
    }
  };

  render() {
    const {
      initialRoute,
      initialRouteStack,
      defaultSceneConfig,
      navBarStyle,
      style,
      sceneStyle,
      navBarBackBtn,
      navBarUnderlay,
      useNavigationBar,
    } = this.props;

    const navigationDelegate = getNavigationDelegate(
      (initialRoute && initialRoute.component) ||
      (initialRouteStack && initialRouteStack.length &&
        initialRouteStack[initialRouteStack.length - 1].component)
    );

    return (
      <Navigator
        ref={'navigator'}
        initialRoute={initialRoute}
        initialRouteStack={initialRouteStack}
        renderScene={this._renderScene}
        configureScene={this._configureScene}
        defaultSceneConfig={defaultSceneConfig}
        navigationBar={useNavigationBar ? this._renderNavigationBar(
          navBarStyle,
          navigationDelegate ?
            navigationDelegate.navBarIsHidden :
            false,
          navBarBackBtn,
          navBarUnderlay,
        ) : null}
        sceneStyle={sceneStyle}
        onWillFocus={this._onWillFocus}
        style={[
          styles.navigator,
          style,
        ]}
      />
    )
  }

  static propTypes = {
    initialRoute: Navigator.propTypes.initialRoute,
    initialRouteStack: Navigator.propTypes.initialRouteStack,
    defaultSceneConfig: PropTypes.object,
    useNavigationBar: PropTypes.bool,
    style: View.propTypes.style,
    navBarStyle: View.propTypes.style,
    navBarUnderlay: PropTypes.object,
    navBarBackBtn: PropTypes.shape({
      icon: PropTypes.object,
      textStyle: PropTypes.object,
    }),
    sceneStyle: View.propTypes.style,
    eachSceneProps: PropTypes.object,
    shouldHandleAndroidBack: PropTypes.bool,
  };

  static defaultProps = {
    defaultSceneConfig: Platform.OS === 'android' ?
      Navigator.SceneConfigs.FadeAndroid :
      Navigator.SceneConfigs.PushFromRight,
    useNavigationBar: true,
    shouldHandleAndroidBack: true,
    navBarBackBtn: {
      textStyle: {},
    },
  };

  static navBarHeight = Scene.navBarHeight;
}

const styles = StyleSheet.create({
  navigator: {
    flex: 1,
    backgroundColor: '#fff',
  },
})

YANavigator.Scene = Scene;
<commit_msg>Add ability to listen back button press event<commit_after>import React, { PropTypes } from 'react';

import NavBar from './NavBar';
import Scene from './Scene';

import { getNavigationDelegate, replaceInstanceEventedProps } from './utils';

import {
  View,
  StyleSheet,
  Navigator,
  Platform,
  BackAndroid,
} from 'react-native';

const VALID_EVENTED_PROPS = [
  'onPress',
  'onValueChange',
  'onChange',
  'onSelection',
  'onChangeText',
  'onBlur',
  'onFocus',
  'onSelectionChange',
  'onSubmitEditing',
];

export default class YANavigator extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      shouldHandleAndroidBack: props.shouldHandleAndroidBack,
    }
  }

  componentDidMount() {
    const navigatorMethods = [
      'getCurrentRoutes',
      'jumpBack',
      'jumpForward',
      'jumpTo',
      'push',
      'pop',
      'replace',
      'replaceAtIndex',
      'replacePrevious',
      'replacePreviousAndPop',
      'resetTo',
      'immediatelyResetRouteStack',
      'popToRoute',
      'popToTop',
    ];

    navigatorMethods.forEach((method) => {
      this[method] = this.refs.navigator[method];
    })

    this.refs.navigator.setShouldHandleAndroidBack = this.setShouldHandleAndroidBack.bind(this);

    if (Platform.OS === 'android') {
      this._backPressSub = BackAndroid.addEventListener('hardwareBackPress', () => {
        if (!this.state.shouldHandleAndroidBack) return false;

        const { navigator } = this.refs;
        const navState = navigator.state;
        const presentedComponent =
          navState.routeStack[navState.presentedIndex].component;
        const navigationDelegate = getNavigationDelegate(presentedComponent);

        if (navigationDelegate && navigationDelegate.onAndroidBackPress) {

          navigationDelegate.onAndroidBackPress(navigator);

          return true;
        } else if (navState.routeStack.length > 1) {
          navigator.pop();

          return true;
        }

       return false;
      });
    }
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      shouldHandleAndroidBack: nextProps.shouldHandleAndroidBack,
    })
  }

  componentWillUnmount() {
    if (Platform.OS === 'android') {
      this._backPressSub.remove();
      this._backPressSub = null;
    }
  }

  setShouldHandleAndroidBack(should) {
    this.setState({
      shouldHandleAndroidBack: should,
    });
  }

  _renderScene = (route, navigator) => {
    let Component;

    const { eachSceneProps } = this.props;

    if (typeof route.component === 'object') {
      Component = React.cloneElement(route.component, {
        navigator,
        ...route.props,
        ...eachSceneProps,
      });
    } else if (typeof route.component === 'function') {
      Component = (
        <route.component
          navigator={navigator}
          {...route.props}
          {...eachSceneProps}
          />
        )
    }

    return Component;
  };

  _configureScene = (route) => {
    const navigationDelegate = getNavigationDelegate(route.component);

    return (navigationDelegate && navigationDelegate.sceneConfig) ||
            this.props.defaultSceneConfig;
  };

  _renderNavigationBar(
    navBarStyle,
    isHiddenOnInit,
    navBarBackBtn,
    navBarUnderlay,
  ) {

    return (
      <NavBar
        isHiddenOnInit={isHiddenOnInit}
        style={navBarStyle}
        backIcon={navBarBackBtn.icon}
        underlay={navBarUnderlay}
        routeMapper={{
          navBarBackgroundColor: (route) => {
            let navBarBackgroundColor = '';

            const navigationDelegate = getNavigationDelegate(route.component);

            if (navigationDelegate &&
                navigationDelegate.navBarBackgroundColor) {

                navBarBackgroundColor =
                  navigationDelegate.navBarBackgroundColor;
            }

            return navBarBackgroundColor;
          },

          LeftPart: (route, navigator, index, state) => {
            let LeftPart = null;

            const navigationDelegate = getNavigationDelegate(route.component);

            if (navigationDelegate &&
                navigationDelegate.renderNavBarLeftPart) {

              navigationDelegate._events = navigationDelegate._events || [];

              LeftPart = navigationDelegate.renderNavBarLeftPart(route.props || {});

              if (!LeftPart) return null;

              if (typeof LeftPart === 'object' && React.isValidElement(LeftPart)) {
                const children =
                  React.Children.toArray(LeftPart.props.children).map((child) => {
                    const {reactElement, events} =
                      replaceInstanceEventedProps(
                        child,
                        VALID_EVENTED_PROPS,
                        navigationDelegate._events,
                        route,
                        navigator.navigationContext
                      )

                    navigationDelegate._events.concat(events.filter((event) => {
                      return navigationDelegate._events.indexOf(event) < 0;
                    }));

                    return reactElement
                  })

                const {reactElement, events} =
                  replaceInstanceEventedProps(
                    LeftPart,
                    VALID_EVENTED_PROPS,
                    navigationDelegate._events,
                    route,
                    navigator.navigationContext
                  )

                navigationDelegate._events.concat(events.filter((event) => {
                  return navigationDelegate._events.indexOf(event) < 0;
                }));

                LeftPart = reactElement;

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_leftPart`;

                LeftPart = React.cloneElement(LeftPart, {ref}, children)
              } else if (typeof LeftPart === 'function') {
                const props = {};

                LeftPart.propTypes && VALID_EVENTED_PROPS.forEach((validProp) => {
                  if (LeftPart.propTypes[validProp]) {
                    const event = `onNavBarLeftPart${validProp.replace(/^on/, '')}`

                    if (navigationDelegate._events.indexOf(event) < 0) {
                      navigationDelegate._events.push(event)
                    }

                    props[validProp] = (e) => navigator.navigationContext
                      .emit(event, {route, e});
                  }
                })

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_leftPart`;

                LeftPart = <LeftPart ref={ref} {...props} />
              }
            } else {
              if (index > 0) {
                // tell navBar to render back button
                const prevRoute = state.routeStack[index - 1];
                const previousComponent = prevRoute.component;
                const previousNavigationDelegate = getNavigationDelegate(previousComponent);

                let backBtnText = '';

                if (previousNavigationDelegate &&
                    typeof previousNavigationDelegate.backBtnText === 'string') {

                  backBtnText = previousNavigationDelegate.backBtnText;
                }

                const navigationDelegate = state.routeStack[index] &&
                  getNavigationDelegate(state.routeStack[index].component);

                const backBtnConfig = {
                  isBackBtn: true,
                  text: backBtnText,
                  onPress: () => {
                    navigator.pop();
                    navigator.navigationContext.emit('onNavBarBackBtnPress', {route});
                  },
                  textStyle: navBarBackBtn.textStyle,
                }

                if (navigationDelegate) {
                  if (navigationDelegate.navBarBackBtnColor) {
                    backBtnConfig.textStyle.color =
                      navigationDelegate.navBarBackBtnColor;
                  }

                  if (navigationDelegate.overrideBackBtnPress) {
                    navigationDelegate._events = navigationDelegate._events || [];

                    const event = 'onNavBarBackBtnPress'

                    if (navigationDelegate._events.indexOf(event) < 0) {
                      navigationDelegate._events.push(event)
                    }

                    backBtnConfig.onPress = () => navigator.navigationContext
                      .emit(event, {route});
                  }
                }

                return backBtnConfig;
              }
            }

            return LeftPart;
          },

          RightPart: (route, navigator) => {
            let RightPart = null;

            const navigationDelegate = getNavigationDelegate(route.component);

            if (navigationDelegate &&
                navigationDelegate.renderNavBarRightPart) {

              navigationDelegate._events = navigationDelegate._events || [];

              RightPart = navigationDelegate.renderNavBarRightPart(route.props || {});

              if (!RightPart) return null;

              if (typeof RightPart === 'object' && React.isValidElement(RightPart)) {
                const children =
                  React.Children.toArray(RightPart.props.children).map((child) => {
                    const {reactElement, events} =
                      replaceInstanceEventedProps(
                        child,
                        VALID_EVENTED_PROPS,
                        navigationDelegate._events,
                        route,
                        navigator.navigationContext
                      )

                    navigationDelegate._events.concat(events.filter((event) => {
                      return navigationDelegate._events.indexOf(event) < 0;
                    }));

                    return reactElement
                  })

                const {reactElement, events} =
                  replaceInstanceEventedProps(
                    RightPart,
                    VALID_EVENTED_PROPS,
                    navigationDelegate._events,
                    route,
                    navigator.navigationContext
                  )

                navigationDelegate._events.concat(events.filter((event) => {
                  return navigationDelegate._events.indexOf(event) < 0;
                }));

                RightPart = reactElement;

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_rightPart`;

                RightPart = React.cloneElement(RightPart, {ref}, children)
              } else if (typeof RightPart === 'function') {
                const props = {};

                RightPart.propTypes && VALID_EVENTED_PROPS.forEach((validProp) => {
                  if (RightPart.propTypes[validProp]) {
                    const event = `onNavBarRightPart${validProp.replace(/^on/, '')}`

                    if (navigationDelegate._events.indexOf(event) < 0) {
                      navigationDelegate._events.push(event)
                    }

                    props[validProp] = (e) => navigator.navigationContext
                      .emit(event, {route, e});
                  }
                })

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_rightPart`;

                RightPart = <RightPart ref={ref} {...props} />
              }
            }

            return RightPart;
          },

          Title: (route, navigator) => {
            let Title = null;

            const navigationDelegate = getNavigationDelegate(route.component);

            if (navigationDelegate &&
                navigationDelegate.renderTitle) {

              navigationDelegate._events = navigationDelegate._events || [];

              Title = navigationDelegate.renderTitle(route.props || {});

              if (!Title) return null;

              if (typeof Title === 'object' && React.isValidElement(Title)) {
                const children =
                  React.Children.toArray(Title.props.children).map((child) => {
                    const {reactElement, events} =
                      replaceInstanceEventedProps(
                        child,
                        VALID_EVENTED_PROPS,
                        navigationDelegate._events,
                        route,
                        navigator.navigationContext
                      )

                    navigationDelegate._events.concat(events.filter((event) => {
                      return navigationDelegate._events.indexOf(event) < 0;
                    }));

                    return reactElement
                  })

                const {reactElement, events} =
                  replaceInstanceEventedProps(
                    Title,
                    VALID_EVENTED_PROPS,
                    navigationDelegate._events,
                    route,
                    navigator.navigationContext
                  )

                navigationDelegate._events.concat(events.filter((event) => {
                  return navigationDelegate._events.indexOf(event) < 0;
                }));

                Title = reactElement;

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_title`;

                Title = React.cloneElement(Title, {ref}, children)
              } else if (typeof Title === 'function') {
                const props = {};

                Title.propTypes && VALID_EVENTED_PROPS.forEach((validProp) => {
                  if (Title.propTypes[validProp]) {
                    const event = `onNavBarTitle${validProp.replace(/^on/, '')}`

                    if (navigationDelegate._events.indexOf(event) < 0) {
                      navigationDelegate._events.push(event)
                    }

                    props[validProp] = (e) => navigator.navigationContext
                      .emit(event, {route, e});
                  }
                })

                const ref = `${navigationDelegate.id ||
                  `${navigator.state.presentedIndex + 1}_scene`}_title`;

                Title = <Title ref={ref} {...props} />
              }
            }

            return Title;
          },
        }} />
    )
  }

  _onWillFocus = (route) => {
    const { navigator } = this.refs;
    const component = route.component;
    const state = navigator && navigator.state;

    if (state) {
      setTimeout(() => {
        const index = state.presentedIndex;

        for (let i = state.routeStack.length - 1; i > index; i--) {
          if (state.routeStack[i] &&
              state.routeStack[i].props &&
              state.routeStack[i].props.onSceneWillBlur) {

            state.routeStack[i].props.onSceneWillBlur(
              route.__navigatorRouteID !== undefined
            );
          }
        }
      }, 100);
    }

    const navBar = navigator && navigator._navBar;
    const navigationDelegate = getNavigationDelegate(component);

    if (navBar && navigationDelegate) {
      navigationDelegate.navBarIsHidden ?
        navBar.hide() :
        navBar.show();
    }
  };

  render() {
    const {
      initialRoute,
      initialRouteStack,
      defaultSceneConfig,
      navBarStyle,
      style,
      sceneStyle,
      navBarBackBtn,
      navBarUnderlay,
      useNavigationBar,
    } = this.props;

    const navigationDelegate = getNavigationDelegate(
      (initialRoute && initialRoute.component) ||
      (initialRouteStack && initialRouteStack.length &&
        initialRouteStack[initialRouteStack.length - 1].component)
    );

    return (
      <Navigator
        ref={'navigator'}
        initialRoute={initialRoute}
        initialRouteStack={initialRouteStack}
        renderScene={this._renderScene}
        configureScene={this._configureScene}
        defaultSceneConfig={defaultSceneConfig}
        navigationBar={useNavigationBar ? this._renderNavigationBar(
          navBarStyle,
          navigationDelegate ?
            navigationDelegate.navBarIsHidden :
            false,
          navBarBackBtn,
          navBarUnderlay,
        ) : null}
        sceneStyle={sceneStyle}
        onWillFocus={this._onWillFocus}
        style={[
          styles.navigator,
          style,
        ]}
      />
    )
  }

  static propTypes = {
    initialRoute: Navigator.propTypes.initialRoute,
    initialRouteStack: Navigator.propTypes.initialRouteStack,
    defaultSceneConfig: PropTypes.object,
    useNavigationBar: PropTypes.bool,
    style: View.propTypes.style,
    navBarStyle: View.propTypes.style,
    navBarUnderlay: PropTypes.object,
    navBarBackBtn: PropTypes.shape({
      icon: PropTypes.object,
      textStyle: PropTypes.object,
    }),
    sceneStyle: View.propTypes.style,
    eachSceneProps: PropTypes.object,
    shouldHandleAndroidBack: PropTypes.bool,
  };

  static defaultProps = {
    defaultSceneConfig: Platform.OS === 'android' ?
      Navigator.SceneConfigs.FadeAndroid :
      Navigator.SceneConfigs.PushFromRight,
    useNavigationBar: true,
    shouldHandleAndroidBack: true,
    navBarBackBtn: {
      textStyle: {},
    },
  };

  static navBarHeight = Scene.navBarHeight;
}

const styles = StyleSheet.create({
  navigator: {
    flex: 1,
    backgroundColor: '#fff',
  },
})

YANavigator.Scene = Scene;
<|endoftext|>


<commit_before>var async = require('asyncawait/async');
var await = require('asyncawait/await');
var User = require('../models/User');
var Get = require('../utils/Get');

var dump = i => {
  var o = {};
  for (k in i) {
    if (i[k].test)
      o[k] = '' + i[k];
    else if (Array.isArray(i[k]))
      o[k] = i[k];
    else if (typeof i[k] == 'object')
      o[k] = dump(i[k]);
    else
      o[k] = i[k];
  }
  return o;
};

var check = (key, value, rule) => {
  if (Array.isArray(rule)) {
    if (rule.length > 0) {
      if (rule[0].substr) {
        // enum
        if (rule.indexOf(value) == -1)
          throw [-4, "value '" + value + "' of parameter '" + key + "' is not in ['" + rule.join("', '") + "']"];
      } else if (rule.length == 1 && Array.isArray(rule[0])) {
        // array of enum
        if (!Array.isArray(value))
          throw [-5, "type of parameter '" + key + "' must be array"];
        for (var i in value)
          if (rule[0].indexOf(value[i]) == -1)
            throw [-6, "value '" + value[i] + "' of parameter '" + key + "[" + i + "]' is not in ['" + rule[0].join("', '") + "']"];
      } else if (rule.length == 2) {
        // range
        if (value < rule[0] || value > rule[1])
          throw [-3, "value " + value + " of parameter '" + key + "' is not in the range of (" + rule[0] + ", " + rule[1] + ")"];
      }
    } else {
      // array
      if (!Array.isArray(value))
        throw [-5, "type of parameter '" + key + "' must be array"];
    }
  } else if (rule.test) {
    // regex
    if (!rule.test(value))
      throw [-7, "value '" + value + "' of parameter '" + key + "' does not match pattern " + rule];
  } else if (rule == 'int') {
    // int
    if (!/-?[0-9]+/.test(value) || isNaN(parseInt(value)))
      throw [-8, "value '" + value + "' of parameter '" + key + "' is not an integer"];
  } else if (rule == 'date') {
    // date
    //TODO
  } else if (rule == '') {
    // any
  }
};

var callback = (rules, func) =>
  async((req, res, next) => {
    res.rtn = (json) => {
      if (req.query.callback)
        res.jsonp(json);
      else
        res.json(json);
    };

    res.ok = (data) =>
      res.rtn({code: 0, message: 'ok', data: data});

    res.err = (code, message, error) =>
      res.rtn({code: code, message: message, error: error});

    try {
      req.checked = {};

      for (var key in rules) {
        // optional
        if (key == '_') {
          for (var _key in rules['_']) {
            if (_key in req.query) {
              var value = req.query[_key];
              check(_key, value, rules['_'][_key]);
              req.checked[_key] = value;
            }
          }
          continue;
        }

        if (rules[key] == 'file') {
          if (!req.files[key])
            throw [-10, "missing file '" + key + "'"];
          continue;
        }

        var value = req.query[key];
        if (typeof value === 'undefined')
          throw [-2, "missing parameter '" + key + "'"];

        if (rules[key] == 'user') {
          // get user from id
          req.user = await(User(value));
          if (!req.user) throw [-1, 'please login'];
        }
        else {
          check(key, value, rules[key]);
          req.checked[key] = value;
        }
      }

      func(req, res, next);
    } catch(e) {
      console.log(e);
      if (Array.isArray(e))
        return res.err(e[0], e[1]);
      res.err(e.code? e.code: -99, e.message? e.message: 'unknown error', e);
    }
  });

var api = base => {
  api = require('express').Router();
  api._get = api.get;
  api._post = api.post;

  var help = {};
  var unit = {};

  api.get = (route, rules, func) => {
    if (typeof rules == 'function') {
      func = rules;
      rules = {};
    }
    help[base + route] = {get: rules};
    api._get(route, callback(rules, func));
    return api;
  };

  api.post = (route, rules, func) => {
    if (typeof rules == 'function') {
      func = rules;
      rules = {};
    }
    help[base + route] = {post: rules};
    api._post(route, require('connect-multiparty')(), callback(rules, func));
    return api;
  };

  api.help = (route) => {
    api._get(route, (req, res) => res.json(dump(help)));
    return api;
  };

  api.unit = (route, qs) => {
    unit[base + route] = qs;
    return api;
  };

  api.test = (route, uri) => {
    var jobs = {};
    for (var path in unit)
      jobs[base + path] = Get.json(uri + base + path, unit[path]);
    api._get(route, async((req, res) =>
      res.json(await(jobs))));
    return api;
  };

  return api;
};

module.exports = api;
<commit_msg>simplify implementation of test<commit_after>var async = require('asyncawait/async');
var await = require('asyncawait/await');
var User = require('../models/User');
var Get = require('../utils/Get');

var dump = i => {
  var o = {};
  for (k in i) {
    if (i[k].test)
      o[k] = '' + i[k];
    else if (Array.isArray(i[k]))
      o[k] = i[k];
    else if (typeof i[k] == 'object')
      o[k] = dump(i[k]);
    else
      o[k] = i[k];
  }
  return o;
};

var check = (key, value, rule) => {
  if (Array.isArray(rule)) {
    if (rule.length > 0) {
      if (rule[0].substr) {
        // enum
        if (rule.indexOf(value) == -1)
          throw [-4, "value '" + value + "' of parameter '" + key + "' is not in ['" + rule.join("', '") + "']"];
      } else if (rule.length == 1 && Array.isArray(rule[0])) {
        // array of enum
        if (!Array.isArray(value))
          throw [-5, "type of parameter '" + key + "' must be array"];
        for (var i in value)
          if (rule[0].indexOf(value[i]) == -1)
            throw [-6, "value '" + value[i] + "' of parameter '" + key + "[" + i + "]' is not in ['" + rule[0].join("', '") + "']"];
      } else if (rule.length == 2) {
        // range
        if (value < rule[0] || value > rule[1])
          throw [-3, "value " + value + " of parameter '" + key + "' is not in the range of (" + rule[0] + ", " + rule[1] + ")"];
      }
    } else {
      // array
      if (!Array.isArray(value))
        throw [-5, "type of parameter '" + key + "' must be array"];
    }
  } else if (rule.test) {
    // regex
    if (!rule.test(value))
      throw [-7, "value '" + value + "' of parameter '" + key + "' does not match pattern " + rule];
  } else if (rule == 'int') {
    // int
    if (!/-?[0-9]+/.test(value) || isNaN(parseInt(value)))
      throw [-8, "value '" + value + "' of parameter '" + key + "' is not an integer"];
  } else if (rule == 'date') {
    // date
    //TODO
  } else if (rule == '') {
    // any
  }
};

var callback = (rules, func) =>
  async((req, res, next) => {
    res.rtn = (json) => {
      if (req.query.callback)
        res.jsonp(json);
      else
        res.json(json);
    };

    res.ok = (data) =>
      res.rtn({code: 0, message: 'ok', data: data});

    res.err = (code, message, error) =>
      res.rtn({code: code, message: message, error: error});

    try {
      req.checked = {};

      for (var key in rules) {
        // optional
        if (key == '_') {
          for (var _key in rules['_']) {
            if (_key in req.query) {
              var value = req.query[_key];
              check(_key, value, rules['_'][_key]);
              req.checked[_key] = value;
            }
          }
          continue;
        }

        if (rules[key] == 'file') {
          if (!req.files[key])
            throw [-10, "missing file '" + key + "'"];
          continue;
        }

        var value = req.query[key];
        if (typeof value === 'undefined')
          throw [-2, "missing parameter '" + key + "'"];

        if (rules[key] == 'user') {
          // get user from id
          req.user = await(User(value));
          if (!req.user) throw [-1, 'please login'];
        }
        else {
          check(key, value, rules[key]);
          req.checked[key] = value;
        }
      }

      func(req, res, next);
    } catch(e) {
      console.log(e);
      if (Array.isArray(e))
        return res.err(e[0], e[1]);
      res.err(e.code? e.code: -99, e.message? e.message: 'unknown error', e);
    }
  });

var api = base => {
  api = require('express').Router();
  api._get = api.get;
  api._post = api.post;

  var help = {};
  var unit = {};

  api.get = (route, rules, func) => {
    if (typeof rules == 'function') {
      func = rules;
      rules = {};
    }
    help[base + route] = {get: rules};
    api._get(route, callback(rules, func));
    return api;
  };

  api.post = (route, rules, func) => {
    if (typeof rules == 'function') {
      func = rules;
      rules = {};
    }
    help[base + route] = {post: rules};
    api._post(route, require('connect-multiparty')(), callback(rules, func));
    return api;
  };

  api.help = (route) => {
    api._get(route, (req, res) => res.json(dump(help)));
    return api;
  };

  api.unit = (route, qs) => {
    unit[base + route] = qs;
    return api;
  };

  api.test = (route, uri) => {
    var jobs = {};
    for (var path in unit)
      jobs[base + path] = Get.json(uri + base + path, unit[path]);
    api._get(route, async((req, res) => res.json(await(jobs))));
    return api;
  };

  return api;
};

module.exports = api;
<|endoftext|>


<commit_before>(function (root, factory) {
    if (typeof exports === 'object') {
        // Node.
        module.exports = factory.call(root);
    } else if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(function(){ return factory.call(root) });
    } else {
        // Browser globals (root is window)
        root.Physics = factory.call(root);
    }
}(this, function () {

'use strict';

var window = this;
var document = window.document;

/** related to: Physics.world
 * Physics
 *
 * The top-level namespace. All of PhysicsJS is contained in
 * the `Physics` namespace.
 *
 * It may (and should) be invoked as a function to create a world instance. For all intensive purposes, [[Physics]] and [[Physics.world]] are the same thing.
 *
 * See [[new Physics.world]] for config options and function signature.
 *
 * Example:
 *
 * ```javascript
 * Physics( cfg, function( world ) {
 *     // use world
 * }); // -> world
 * ```
 **/
var Physics = function Physics(){

    return Physics.world.apply(Physics, arguments);
};

/**
 * Physics.util
 *
 * Namespace for utility functions.
 **/
Physics.util = {};

/**
 * == Special ==
 *
 * This section contains miscellaneous functionality.
 **/
<commit_msg>change init root to window<commit_after>(function (root, factory) {
    if (typeof exports === 'object') {
        // Node.
        module.exports = factory.call(root);
    } else if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(function(){ return factory.call(root) });
    } else {
        // Browser globals (root is window)
        root.Physics = factory.call(root);
    }
}(window, function () {

'use strict';

var window = this;
var document = window.document;

/** related to: Physics.world
 * Physics
 *
 * The top-level namespace. All of PhysicsJS is contained in
 * the `Physics` namespace.
 *
 * It may (and should) be invoked as a function to create a world instance. For all intensive purposes, [[Physics]] and [[Physics.world]] are the same thing.
 *
 * See [[new Physics.world]] for config options and function signature.
 *
 * Example:
 *
 * ```javascript
 * Physics( cfg, function( world ) {
 *     // use world
 * }); // -> world
 * ```
 **/
var Physics = function Physics(){

    return Physics.world.apply(Physics, arguments);
};

/**
 * Physics.util
 *
 * Namespace for utility functions.
 **/
Physics.util = {};

/**
 * == Special ==
 *
 * This section contains miscellaneous functionality.
 **/
<|endoftext|>


<commit_before>var flipAhead = function(wnd) {
  var self = {},
      gestureStartCoords = [],
      pageLinks = {
        prev: document.head.querySelector("link[rel='previous']") && document.head.querySelector("link[rel='previous']").href,
        next: document.head.querySelector("link[rel='next']") && document.head.querySelector("link[rel='next']").href
      };

  var handleGesture = function(touches) {
    var gesture = null;

    for(var i=0; i<touches.length;i++) {
      if(touches[i].clientX < gestureStartCoords[i]) {
        gesture = "next";
        break;
      } else if(touches[i].clientX > gestureStartCoords[i]) {
        gesture = "prev";
        break;
      }
    }

    if(gesture && pageLinks[gesture]) {
      wnd.location.href = pageLinks[gesture];
    }
  };

  self.handle = function(event) {

    if(event.type != "touchstart" && event.type != "touchend") return;

    if(event.type == "touchstart") {
      for(var i=0;i<event.touches.length;i++) {
        gestureStartCoords.push(event.touches[i].clientX);
      }
    } else if(event.type == "touchend") {
      handleGesture(event.changedTouches);
    }

    event.preventDefault(true);
    return false;
  }

  return self;
}

window.FlipAhead = flipAhead(window);<commit_msg>Fixed link rel;for previous, which is now prev.<commit_after>var flipAhead = function(wnd) {
  var self = {},
      gestureStartCoords = [],
      pageLinks = {
        prev: document.head.querySelector("link[rel='prev']") && document.head.querySelector("link[rel='previous']").href,
        next: document.head.querySelector("link[rel='next']") && document.head.querySelector("link[rel='next']").href
      };

  var handleGesture = function(touches) {
    var gesture = null;

    for(var i=0; i<touches.length;i++) {
      if(touches[i].clientX < gestureStartCoords[i]) {
        gesture = "next";
        break;
      } else if(touches[i].clientX > gestureStartCoords[i]) {
        gesture = "prev";
        break;
      }
    }

    if(gesture && pageLinks[gesture]) {
      wnd.location.href = pageLinks[gesture];
    }
  };

  self.handle = function(event) {

    if(event.type != "touchstart" && event.type != "touchend") return;

    if(event.type == "touchstart") {
      for(var i=0;i<event.touches.length;i++) {
        gestureStartCoords.push(event.touches[i].clientX);
      }
    } else if(event.type == "touchend") {
      handleGesture(event.changedTouches);
    }

    event.preventDefault(true);
    return false;
  }

  return self;
}

window.FlipAhead = flipAhead(window);
<|endoftext|>