<commit_before>import React from 'react';
import { Link } from '@curi/react';

import BaseGuide from './base/BaseGuide';
import {
  PrismBlock,
  InlineJS as IJS,
  InlineComponent as Cmp
} from '../components/PrismBlocks';
import { Note } from '../components/Messages';
import { Section } from '../components/Sections';

export default ({ name }) => (
  <BaseGuide>

{name}

    <Note>This guide assumes that you are already familiar with React.</Note>

      Curi provides a number of React components through the{' '}
      <Link to="Package" params={{ package: 'react' }}>
        <IJS>@curi/react</IJS>
      </Link>{' '}
      package. To get started, there are only two components that you need to be
      aware of: <Cmp>CuriBase</Cmp> and <Cmp>Link</Cmp>.

      <PrismBlock lang="bash">{`npm install @curi/react`}</PrismBlock>

        The{' '}
        <Link
          to="Package"
          params={{ package: 'react' }}
          details={{ hash: 'CuriBase' }}
        >
          CuriBase
        </Link>{' '}
        component is responsible for placing values on React's{' '}
        <IJS>context</IJS> so that child components can access them. To do this,
        you pass it a Curi router, a <IJS>response</IJS> object, and an{' '}
        <IJS>action</IJS> string. You should re-render this whenever the a new
        response is emitted, so it makes sense to render this inside of a
        response handler (the function passed to <IJS>router.respond</IJS>).

        The CuriBase also takes a <IJS>render</IJS> prop. That is a function
        that returns the React elements that make up your application. The
        render function will receive three arguments: <IJS>response</IJS>,{' '}
        <IJS>action</IJS>, and <IJS>router</IJS>. Response is a response object,
        action is an action string, and router is your router. The response
        object is the most important of these there. The others may occasionally
        be useful or you may never use them.

      <PrismBlock lang="jsx">
        {`import { CuriBase } from '@curi/react';

const router = curi(history, routes);

function render(response, router) {
  // return a React element (or null)
}

router.respond((response, action) => {
  ReactDOM.render((
    <CuriBase
      router={router}
      response={response}
      action={action}
      render={render}
    />
  ), holder);
});`}
      </PrismBlock>

        You can define your render function inline, but typically it is
        easier to define the function in its own module and import it wherever
        you are rendering your CuriBase. Inlining would also mean that the
        function gets recreated every time that the CuriBase is re-rendered by
        its parent, which is not ideal.

        The primary property of the response object that you will find useful is{' '}
        <IJS>body</IJS>. The body property is the value set by calling{' '}
        <IJS>set.body()</IJS> in the route's <IJS>match.response</IJS> function.
        Since this is a React guide, this value should be a React component that
        will render the contents of the page for a specific route. For example,
        a <Cmp>Home</Cmp> component might render the contents of your homepage
        while an <Cmp>About</Cmp> component might render a page describing your
        application.

      <PrismBlock lang="javascript">
        {`import Home from './components/Home';
import About from './components/About';

const routes = [
  {
    name: 'Home',
    path: '',
    match: {
      response: ({ set }) => {
        set.body(Home);
      }
    }
  },
  {
    name: 'About',
    path: 'about',
    match: {
      response: ({ set }) => {
        set.body(About);
      }
    }
  }
]`}
      </PrismBlock>

        When the body property of a response is <IJS>undefined</IJS>, that means
        that none of your routes matched the new location and you should render
        some sort of 404/page not found component. You may also want to add a
        catch all route (one whose path is <IJS>(.*)</IJS>) so that you can
        expect to always have a <IJS>body</IJS> property on the response.

      <PrismBlock lang="javascript">
        {`function render(response) {
  const { body:Body } = response;
  return ;
}`}
      </PrismBlock>
      <Note>
        We rename "body" to "Body" so that the JSX is{' '}

          transformed properly
        .
      </Note>

        You can review all of the response properties in the{' '}
        <Link
          to="Guide"
          params={{ slug: 'responses' }}
          details={{ hash: 'properties' }}
        >
          Rendering with Responses
        </Link>{' '}
        guide. The other ones that you will most likely be interested in are
        <IJS>params</IJS> and <IJS>data</IJS>.

      <PrismBlock lang="javascript">
        {`function render(response) {
  const { body:Body, params, data } = response;
  return ;
}`}
      </PrismBlock>

      <Note>
        This does not cover all of the Link props. Please check out the{' '}
        <Link
          to="Package"
          params={{ package: 'react' }}
          details={{ hash: 'CuriBase' }}
        >
          CuriBase
        </Link>{' '}
        API docs to learn more about the other props.
      </Note>

      <PrismBlock lang="bash">{`npm install @curi/react`}</PrismBlock>

        A single page application isn't very useful if you cannot navigate
        between locations. The{' '}
        <Link
          to="Package"
          params={{ package: 'react' }}
          details={{ hash: 'Link' }}
        >
          Link
        </Link>{' '}
        component provides you with an easy way to do this by rendering anchor (<Cmp
        >
          a
        </Cmp>) elements.

      <PrismBlock lang="jsx">
        {`import { Link } from '@curi/react'
<Link to='Home'>Home</Link>`}
      </PrismBlock>

        The most important prop of the Link is <IJS>to</IJS>. This value should
        be the name of the route that you want to navigate to. For instance,
        take about following route:

      <PrismBlock lang="javascript">
        {`{
  name: 'About',
  path: 'about',
  ...
}
`}
      </PrismBlock>

        If you were to render a Link whose to property is "About", then it would
        render an anchor whose <IJS>href</IJS> attribute is <IJS>/about</IJS>.
        The great thing about this is that you don't have to know the URI of the
        route that you want to navigate to, only its name. Curi (using the
        built-in <IJS>pathname</IJS> add-on), handles creating URI pathnames for
        you.

      <PrismBlock lang="jsx">
        {`<Link to='About'>About</Link>
// About`}
      </PrismBlock>

        That works well enough for simple paths, but what about paths that
        include params? For that, you need to pass an object using the{' '}
        <IJS>params</IJS> property. This object's keys should be the same as the
        route's expected params. The params object should also include the
        params for any parent routes.

      <PrismBlock lang="jsx">
        {`{
  name: 'Album',
  path: 'a/:albumID',
  ...,
  children: [
    {
      name: 'Song',
      path: ':songID',
      ...
    }
  ]
}

<Link to='Song' params={{ albumID: 2390, songID: 7 }}>Some Song on Some Album</Link>
// Some Song on Some Album`}
      </PrismBlock>

        If you want to attach additional location information to a Link, you can
        do so using the <IJS>details</IJS> prop. This is an object that has any
        other location properties that you want to link to. These would be{' '}
        <IJS>query</IJS>, <IJS>hash</IJS>, and <IJS>state</IJS>.

      <PrismBlock lang="jsx">
        {`<Link
  to='Song'
  params={{ albumID: 2390, songID: 8 }}
  details={{ query: { time: 17 } }}
>
  Some Song on Some Album
</Link>
// Some Song on Some Album`}
      </PrismBlock>

        The <IJS>children</IJS> prop has been used, but not mentioned. The
        contents of the children prop will simply be passed as the children of
        the anchor, the same as if you were to use a regular anchor. Using a
        regular anchor would not work here, though. You could hand code the URI
        to link to, which is half of what the Link does, but clicking the anchor
        would cause a full page reload. The Link uses calls the{' '}
        <IJS>navigate</IJS> method from your Hickory history object to allow for
        in-app navigation without reloading the page.

      <PrismBlock lang="jsx">
        {`// if you render this, clicking it will reload the page
About

// while rendering this will not cause a reload
<Link to='About'>About</Link>
`}
      </PrismBlock>

        Both of the above elements render the same thing to the page, an anchor
        element, but the "magic" is the Link's usage of your history object.

      <Note>
        This does not cover all of the Link props. Please check out the{' '}
        <Link
          to="Package"
          params={{ package: 'react' }}
          details={{ hash: 'Link' }}
        >
          Link
        </Link>{' '}
        API docs to learn more about the other props.
      </Note>

  </BaseGuide>
);
<commit_msg>(docs) Improve React Guide [ci skip]<commit_after>import React from "react";
import { Link } from "@curi/react";

import BaseGuide from "./base/BaseGuide";
import {
  PrismBlock,
  InlineJS as IJS,
  InlineComponent as Cmp
} from "../components/PrismBlocks";
import { Note } from "../components/Messages";
import { Section, Subsection } from "../components/Sections";

export default ({ name }) => (
  <BaseGuide>

{name}

    <PrismBlock lang="bash">{`npm install @curi/react`}</PrismBlock>
    <Note>This guide assumes that you are already familiar with React.</Note>

      Curi provides a number of React components through the{" "}
      <Link to="Package" params={{ package: "react" }}>
        <IJS>@curi/react</IJS>
      </Link>{" "}
      package. To get started, there are only two components that you need to be
      aware of: <Cmp>CuriBase</Cmp> and <Cmp>Link</Cmp>.

    <Section title={<Cmp>CuriBase</Cmp>} id="CuriBase">

        In order for other components to access router-related variables, we
        need to make them available through React's <IJS>context</IJS>. The{" "}
        <Link
          to="Package"
          params={{ package: "react" }}
          details={{ hash: "CuriBase" }}
        >
          <Cmp>CuriBase</Cmp>
        </Link>{" "}
        component is responsible for doing this. You pass it a <IJS>router</IJS>,
        a <IJS>response</IJS> object, and an <IJS>action</IJS> string and other
        components in your application will be able to easily access them.

      <PrismBlock lang="jsx">
        {`import { CuriBase } from '@curi/react';

<CuriBase
  router={router}
  response={response}
  action={action}
  render={render}
/>`}
      </PrismBlock>

      <Subsection title="The render prop" id="render-prop">

          The <Cmp>CuriBase</Cmp> also expects a <IJS>render</IJS> prop, which
          is a function that will be called every time the <Cmp>CuriBase</Cmp>{" "}
          is re-rendered. The <IJS>render</IJS> function should return the React
          elements that make up your application.

          The render function receives three arguments to help you render your
          application: <IJS>response</IJS>, <IJS>action</IJS>, and{" "}
          <IJS>router</IJS>. The <IJS>response</IJS> object is the most
          important of these, while the others may occasionally be useful or you
          may never use them.

        <Note>
          The <Cmp>CuriBase</Cmp> does not automatically update
          when new responses are emitted. There are a couple of strategies for
          re-rendering new responses that will be covered later.
        </Note>

          In our routes, we can attach a <IJS>body</IJS> property to response
          objects, which we can then use in a <IJS>render</IJS> function.

        <PrismBlock lang="javascript">
          {`import Home from './components/Home';

const routes = [
  {
    name: 'Home',
    path: '',
    match: {
      response: ({ set }) => {
        set.body(Home);
      }
    }
  },
  ...
]`}
        </PrismBlock>
        <PrismBlock lang="javascript">
          {`function render(response) {
  const { body:Body } = response;
  return ;
}`}
        </PrismBlock>
        <Note>
          We rename "body" to "Body" so that the JSX is{" "}

            transformed properly
          .
        </Note>

          When no routes match, the body property of a response is{" "}
          <IJS>undefined</IJS>. To deal with this, you should either render some
          sort of 404/page not found component or add a catch all route (a route
          whose path is <IJS>"(.*)"</IJS>) so that you can expect to always have
          a <IJS>body</IJS> property on the response.

          Finally, you can pass any props that you want to the <Cmp>Body</Cmp>{" "}
          component. Generally speaking, it is useful to pass the entire{" "}
          <IJS>response</IJS> object. You can review all of the response
          properties in the{" "}
          <Link
            to="Guide"
            params={{ slug: "responses" }}
            details={{ hash: "properties" }}
          >
            Rendering with Responses
          </Link>{" "}
          guide. The other ones that you will most likely be interested in are
          <IJS>params</IJS> and <IJS>data</IJS>.

        <PrismBlock lang="javascript">
          {`function render(response) {
  const { body:Body, params, data } = response;
  return ;
}`}
        </PrismBlock>
      </Subsection>

      <Subsection title="Re-rendering" id="re-rendering">

          As noted above, the <Cmp>CuriBase</Cmp> does not automatically update
          when a new response is emitted. Here are a few strategies for
          re-rendering your application when a new response is emitted.

        <Subsection tag="h4" title="Use the router" id="use-the-router">

            The easiest solution is to just use <IJS>router.respond</IJS> to
            re-call <IJS>ReactDOM.render</IJS>.

          <PrismBlock lang="jsx">
            {`router.respond((response, action) => {
  ReactDOM.render((
    <CuriBase
      router={router}
      response={response}
      action={action}
      render={render}
    />
  ), holder);
});`}
          </PrismBlock>
        </Subsection>
        <Subsection tag="h4" title={<Cmp>Curious</Cmp>} id="use-curious">

            If you prefer not to call <IJS>ReactDOM.render</IJS> multiple times,
            you can use the <Cmp>Curious</Cmp> component to listen for new
            responses and re-render when the are emitted.

          <PrismBlock lang="jsx">
            {`import { CuriBase, Curious } from '@curi/react';

// the props passed to <Curious>'s render function
// is an object with router, response, and action properties
<Curious
  router={router}
  responsive={true}
  render={props => <CuriBase {...props} render={render} />}
/>`}
          </PrismBlock>
        </Subsection>
        <Subsection tag="h4" title="Use the stores" id="use-stores">

            If you are using either a Redux or MobX store and are connecting
            them to your router (using the respective <IJS>@curi/redux</IJS> and{" "}
            <IJS>@curi/mobx</IJS> packages), then you can just use the
            higher-order components from those stores to inject the props into
            the <Cmp>CuriBase</Cmp>.

          <PrismBlock lang="jsx">
            {`import { CuriBase } from '@curi/react';

// redux
import { connect } from 'react-redux';
const CuriReduxBase = connect(
  ({ curi }) => ({
    router: curi.router,
    response: curi.response,
    action: curi.action
  })
)(CuriBase);
// usage with <Provider> from react-redux
<Provider store={store}>
  <CuriReduxBase render={render} />
</Provider>

// mobx
const CuriMobXBase = inject(
  ({ curi }) => ({
    router: curi.router,
    response: curi.response,
    action: curi.action
  })
)(observer(CuriBase));
// usage with <Provider> from mobx-react
<Provider curi={curiStore}>
  <CuriMobXBase render={render} />
</Provider>`}
          </PrismBlock>
        </Subsection>
      </Subsection>

    <Section title={<Cmp>Link</Cmp>} id="link">
      <PrismBlock lang="bash">{`npm install @curi/react`}</PrismBlock>

        A single page application isn't very useful if you cannot navigate
        between locations. The{" "}
        <Link
          to="Package"
          params={{ package: "react" }}
          details={{ hash: "Link" }}
        >
          <Cmp>Link</Cmp>
        </Link>{" "}
        component provides you with an easy way to do this by rendering anchor (<Cmp
        >
          a
        </Cmp>) elements.

      <PrismBlock lang="jsx">
        {`import { Link } from '@curi/react'
<Link to='Home'>Home</Link>`}
      </PrismBlock>

        The most important prop of the <Cmp>Link</Cmp> is <IJS>to</IJS>. This
        value should be the name of the route that you want to navigate to. For
        instance, take about following route:

      <PrismBlock lang="javascript">
        {`{
  name: 'About',
  path: 'about',
  ...
}
`}
      </PrismBlock>

        If you were to render a Link whose to property is <IJS>"About"</IJS>,
        then it would render an anchor whose <IJS>href</IJS> attribute is{" "}
        <IJS>/about</IJS>. The great thing about this is that you don't have to
        know the URI of the route that you want to navigate to, only its name
        because Curi handles creating URI pathnames for you.

      <PrismBlock lang="jsx">
        {`<Link to='About'>About</Link>
// About`}
      </PrismBlock>

        That works well enough for simple paths, but what about paths that
        include params? For that, you need to pass an object using the{" "}
        <IJS>params</IJS> property. This object's keys should be the same as the
        route's expected params. The params object should also include the
        params for any parent routes.

      <PrismBlock lang="jsx">
        {`{
  name: 'Album',
  path: 'a/:albumID',
  ...,
  children: [
    {
      name: 'Song',
      path: ':songID',
      ...
    }
  ]
}

<Link to='Song' params={{ albumID: 2390, songID: 7 }}>
  Some Song on Some Album
</Link>
// Some Song on Some Album`}
      </PrismBlock>

        If you want to attach additional location information to a Link, you can
        do so using the <IJS>details</IJS> prop. This is an object that has any
        other location properties that you want to link to. These would be{" "}
        <IJS>query</IJS>, <IJS>hash</IJS>, and <IJS>state</IJS>.

      <PrismBlock lang="jsx">
        {`<Link
  to='Song'
  params={{ albumID: 2390, songID: 8 }}
  details={{ query: { time: 17 } }}
>
  Some Song on Some Album
</Link>
// Some Song on Some Album`}
      </PrismBlock>

        The <IJS>children</IJS> prop will simply be passed as the children of
        the anchor, the same as if you were to use a regular anchor.

      <Subsection title="Why not an anchor?" id="why-link">

          We use a <Cmp>Link</Cmp> because a regular anchor would not work here.
          Clicking the anchor would cause a full page reload. The{" "}
          <Cmp>Link</Cmp> uses calls the <IJS>navigate</IJS> method from your
          Hickory history object to allow for in-app navigation without
          reloading the page.

        <PrismBlock lang="jsx">
          {`// if you render this, clicking it will reload the page
About

// while rendering this will not cause a reload
<Link to='About'>About</Link>
`}
        </PrismBlock>

          Both of the above elements render the same thing to the page, an
          anchor element, but the "magic" is the Link's usage of your history
          object.

      </Subsection>

      <Note>
        This does not cover all of the Link props. Please check out the{" "}
        <Link
          to="Package"
          params={{ package: "react" }}
          details={{ hash: "Link" }}
        >
          Link
        </Link>{" "}
        API docs to learn more about the other props.
      </Note>

      The above guide should cover what you need to know to get started with
      using Curi and React. However, there are also more React components that
      you might find useful. You can read more about them in the{" "}
      <Link to="Package" params={{ package: "react" }}>
        <IJS>@curi/react</IJS>
      </Link>{" "}
      documentation.

  </BaseGuide>
);
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ViewPagerAndroid
 * @flow
 */
'use strict';

var React = require('React');
var PropTypes = require('prop-types');
var ReactNative = require('ReactNative');
var UIManager = require('UIManager');
var ViewPropTypes = require('ViewPropTypes');

var dismissKeyboard = require('dismissKeyboard');
var requireNativeComponent = require('requireNativeComponent');

var VIEWPAGER_REF = 'viewPager';

type Event = Object;

export type ViewPagerScrollState = $Enum<{
  idle: string,
  dragging: string,
  settling: string,
}>;

/**
 * Container that allows to flip left and right between child views. Each
 * child view of the `ViewPagerAndroid` will be treated as a separate page
 * and will be stretched to fill the `ViewPagerAndroid`.
 *
 * It is important all children are `<View>`s and not composite components.
 * You can set style properties like `padding` or `backgroundColor` for each
 * child.
 *
 * Example:
 *
 * ```
 * render: function() {
 *   return (
 *     <ViewPagerAndroid
 *       style={styles.viewPager}
 *       initialPage={0}>
 *       <View style={styles.pageStyle}>
 *         <Text>First page</Text>
 *       </View>
 *       <View style={styles.pageStyle}>
 *         <Text>Second page</Text>
 *       </View>
 *     </ViewPagerAndroid>
 *   );
 * }
 *
 * ...
 *
 * var styles = {
 *   ...
 *   pageStyle: {
 *     alignItems: 'center',
 *     padding: 20,
 *   }
 * }
 * ```
 */
class ViewPagerAndroid extends React.Component {
  props: {
    initialPage?: number,
    onPageScroll?: Function,
    onPageScrollStateChanged?: Function,
    onPageSelected?: Function,
    pageMargin?: number,
    keyboardDismissMode?: 'none' | 'on-drag',
    scrollEnabled?: boolean,
  };

  static propTypes = {
    ...ViewPropTypes,
    /**
     * Index of initial page that should be selected. Use `setPage` method to
     * update the page, and `onPageSelected` to monitor page changes
     */
    initialPage: ReactPropTypes.number,

    /**
     * Executed when transitioning between pages (ether because of animation for
     * the requested page change or when user is swiping/dragging between pages)
     * The `event.nativeEvent` object for this callback will carry following data:
     *  - position - index of first page from the left that is currently visible
     *  - offset - value from range [0,1) describing stage between page transitions.
     *    Value x means that (1 - x) fraction of the page at "position" index is
     *    visible, and x fraction of the next page is visible.
     */
    onPageScroll: ReactPropTypes.func,

    /**
     * Function called when the page scrolling state has changed.
     * The page scrolling state can be in 3 states:
     * - idle, meaning there is no interaction with the page scroller happening at the time
     * - dragging, meaning there is currently an interaction with the page scroller
     * - settling, meaning that there was an interaction with the page scroller, and the
     *   page scroller is now finishing it's closing or opening animation
     */
    onPageScrollStateChanged: ReactPropTypes.func,

    /**
     * This callback will be called once ViewPager finish navigating to selected page
     * (when user swipes between pages). The `event.nativeEvent` object passed to this
     * callback will have following fields:
     *  - position - index of page that has been selected
     */
    onPageSelected: ReactPropTypes.func,

    /**
     * Blank space to show between pages. This is only visible while scrolling, pages are still
     * edge-to-edge.
     */
    pageMargin: ReactPropTypes.number,

    /**
     * Determines whether the keyboard gets dismissed in response to a drag.
     *   - 'none' (the default), drags do not dismiss the keyboard.
     *   - 'on-drag', the keyboard is dismissed when a drag begins.
     */
    keyboardDismissMode: ReactPropTypes.oneOf([
      'none', // default
      'on-drag',
    ]),

    /**
    * When false, the content does not scroll.
    * The default value is true.
    */
    scrollEnabled: ReactPropTypes.bool,
  };

  componentDidMount() {
    if (this.props.initialPage != null) {
      this.setPageWithoutAnimation(this.props.initialPage);
    }
  }

  getInnerViewNode = (): ReactComponent => {
    return this.refs[VIEWPAGER_REF].getInnerViewNode();
  };

  _childrenWithOverridenStyle = (): Array => {
    // Override styles so that each page will fill the parent. Native component
    // will handle positioning of elements, so it's not important to offset
    // them correctly.
    return React.Children.map(this.props.children, function(child) {
      if (!child) {
        return null;
      }
      var newProps = {
        ...child.props,
        style: [child.props.style, {
          position: 'absolute',
          left: 0,
          top: 0,
          right: 0,
          bottom: 0,
          width: undefined,
          height: undefined,
        }],
        collapsable: false,
      };
      if (child.type &&
          child.type.displayName &&
          (child.type.displayName !== 'RCTView') &&
          (child.type.displayName !== 'View')) {
        console.warn('Each ViewPager child must be a <View>. Was ' + child.type.displayName);
      }
      return React.createElement(child.type, newProps);
    });
  };

  _onPageScroll = (e: Event) => {
    if (this.props.onPageScroll) {
      this.props.onPageScroll(e);
    }
    if (this.props.keyboardDismissMode === 'on-drag') {
      dismissKeyboard();
    }
  };

  _onPageScrollStateChanged = (e: Event) => {
    if (this.props.onPageScrollStateChanged) {
      this.props.onPageScrollStateChanged(e.nativeEvent.pageScrollState);
    }
  };

  _onPageSelected = (e: Event) => {
    if (this.props.onPageSelected) {
      this.props.onPageSelected(e);
    }
  };

  /**
   * A helper function to scroll to a specific page in the ViewPager.
   * The transition between pages will be animated.
   */
  setPage = (selectedPage: number) => {
    UIManager.dispatchViewManagerCommand(
      ReactNative.findNodeHandle(this),
      UIManager.AndroidViewPager.Commands.setPage,
      [selectedPage],
    );
  };

  /**
   * A helper function to scroll to a specific page in the ViewPager.
   * The transition between pages will *not* be animated.
   */
  setPageWithoutAnimation = (selectedPage: number) => {
    UIManager.dispatchViewManagerCommand(
      ReactNative.findNodeHandle(this),
      UIManager.AndroidViewPager.Commands.setPageWithoutAnimation,
      [selectedPage],
    );
  };

  render() {
    return (
      <NativeAndroidViewPager
        {...this.props}
        ref={VIEWPAGER_REF}
        style={this.props.style}
        onPageScroll={this._onPageScroll}
        onPageScrollStateChanged={this._onPageScrollStateChanged}
        onPageSelected={this._onPageSelected}
        children={this._childrenWithOverridenStyle()}
      />
    );
  }
}

var NativeAndroidViewPager = requireNativeComponent('AndroidViewPager', ViewPagerAndroid);

module.exports = ViewPagerAndroid;
<commit_msg>Fixed bad ReactPropTypes reference in ViewPagerAndroid<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ViewPagerAndroid
 * @flow
 */
'use strict';

var React = require('React');
var PropTypes = require('prop-types');
var ReactNative = require('ReactNative');
var UIManager = require('UIManager');
var ViewPropTypes = require('ViewPropTypes');

var dismissKeyboard = require('dismissKeyboard');
var requireNativeComponent = require('requireNativeComponent');

var VIEWPAGER_REF = 'viewPager';

type Event = Object;

export type ViewPagerScrollState = $Enum<{
  idle: string,
  dragging: string,
  settling: string,
}>;

/**
 * Container that allows to flip left and right between child views. Each
 * child view of the `ViewPagerAndroid` will be treated as a separate page
 * and will be stretched to fill the `ViewPagerAndroid`.
 *
 * It is important all children are `<View>`s and not composite components.
 * You can set style properties like `padding` or `backgroundColor` for each
 * child.
 *
 * Example:
 *
 * ```
 * render: function() {
 *   return (
 *     <ViewPagerAndroid
 *       style={styles.viewPager}
 *       initialPage={0}>
 *       <View style={styles.pageStyle}>
 *         <Text>First page</Text>
 *       </View>
 *       <View style={styles.pageStyle}>
 *         <Text>Second page</Text>
 *       </View>
 *     </ViewPagerAndroid>
 *   );
 * }
 *
 * ...
 *
 * var styles = {
 *   ...
 *   pageStyle: {
 *     alignItems: 'center',
 *     padding: 20,
 *   }
 * }
 * ```
 */
class ViewPagerAndroid extends React.Component {
  props: {
    initialPage?: number,
    onPageScroll?: Function,
    onPageScrollStateChanged?: Function,
    onPageSelected?: Function,
    pageMargin?: number,
    keyboardDismissMode?: 'none' | 'on-drag',
    scrollEnabled?: boolean,
  };

  static propTypes = {
    ...ViewPropTypes,
    /**
     * Index of initial page that should be selected. Use `setPage` method to
     * update the page, and `onPageSelected` to monitor page changes
     */
    initialPage: PropTypes.number,

    /**
     * Executed when transitioning between pages (ether because of animation for
     * the requested page change or when user is swiping/dragging between pages)
     * The `event.nativeEvent` object for this callback will carry following data:
     *  - position - index of first page from the left that is currently visible
     *  - offset - value from range [0,1) describing stage between page transitions.
     *    Value x means that (1 - x) fraction of the page at "position" index is
     *    visible, and x fraction of the next page is visible.
     */
    onPageScroll: PropTypes.func,

    /**
     * Function called when the page scrolling state has changed.
     * The page scrolling state can be in 3 states:
     * - idle, meaning there is no interaction with the page scroller happening at the time
     * - dragging, meaning there is currently an interaction with the page scroller
     * - settling, meaning that there was an interaction with the page scroller, and the
     *   page scroller is now finishing it's closing or opening animation
     */
    onPageScrollStateChanged: PropTypes.func,

    /**
     * This callback will be called once ViewPager finish navigating to selected page
     * (when user swipes between pages). The `event.nativeEvent` object passed to this
     * callback will have following fields:
     *  - position - index of page that has been selected
     */
    onPageSelected: PropTypes.func,

    /**
     * Blank space to show between pages. This is only visible while scrolling, pages are still
     * edge-to-edge.
     */
    pageMargin: PropTypes.number,

    /**
     * Determines whether the keyboard gets dismissed in response to a drag.
     *   - 'none' (the default), drags do not dismiss the keyboard.
     *   - 'on-drag', the keyboard is dismissed when a drag begins.
     */
    keyboardDismissMode: PropTypes.oneOf([
      'none', // default
      'on-drag',
    ]),

    /**
    * When false, the content does not scroll.
    * The default value is true.
    */
    scrollEnabled: PropTypes.bool,
  };

  componentDidMount() {
    if (this.props.initialPage != null) {
      this.setPageWithoutAnimation(this.props.initialPage);
    }
  }

  getInnerViewNode = (): ReactComponent => {
    return this.refs[VIEWPAGER_REF].getInnerViewNode();
  };

  _childrenWithOverridenStyle = (): Array => {
    // Override styles so that each page will fill the parent. Native component
    // will handle positioning of elements, so it's not important to offset
    // them correctly.
    return React.Children.map(this.props.children, function(child) {
      if (!child) {
        return null;
      }
      var newProps = {
        ...child.props,
        style: [child.props.style, {
          position: 'absolute',
          left: 0,
          top: 0,
          right: 0,
          bottom: 0,
          width: undefined,
          height: undefined,
        }],
        collapsable: false,
      };
      if (child.type &&
          child.type.displayName &&
          (child.type.displayName !== 'RCTView') &&
          (child.type.displayName !== 'View')) {
        console.warn('Each ViewPager child must be a <View>. Was ' + child.type.displayName);
      }
      return React.createElement(child.type, newProps);
    });
  };

  _onPageScroll = (e: Event) => {
    if (this.props.onPageScroll) {
      this.props.onPageScroll(e);
    }
    if (this.props.keyboardDismissMode === 'on-drag') {
      dismissKeyboard();
    }
  };

  _onPageScrollStateChanged = (e: Event) => {
    if (this.props.onPageScrollStateChanged) {
      this.props.onPageScrollStateChanged(e.nativeEvent.pageScrollState);
    }
  };

  _onPageSelected = (e: Event) => {
    if (this.props.onPageSelected) {
      this.props.onPageSelected(e);
    }
  };

  /**
   * A helper function to scroll to a specific page in the ViewPager.
   * The transition between pages will be animated.
   */
  setPage = (selectedPage: number) => {
    UIManager.dispatchViewManagerCommand(
      ReactNative.findNodeHandle(this),
      UIManager.AndroidViewPager.Commands.setPage,
      [selectedPage],
    );
  };

  /**
   * A helper function to scroll to a specific page in the ViewPager.
   * The transition between pages will *not* be animated.
   */
  setPageWithoutAnimation = (selectedPage: number) => {
    UIManager.dispatchViewManagerCommand(
      ReactNative.findNodeHandle(this),
      UIManager.AndroidViewPager.Commands.setPageWithoutAnimation,
      [selectedPage],
    );
  };

  render() {
    return (
      <NativeAndroidViewPager
        {...this.props}
        ref={VIEWPAGER_REF}
        style={this.props.style}
        onPageScroll={this._onPageScroll}
        onPageScrollStateChanged={this._onPageScrollStateChanged}
        onPageSelected={this._onPageSelected}
        children={this._childrenWithOverridenStyle()}
      />
    );
  }
}

var NativeAndroidViewPager = requireNativeComponent('AndroidViewPager', ViewPagerAndroid);

module.exports = ViewPagerAndroid;
<|endoftext|>


<commit_before>var inherits = require('util').inherits,
    Task = require('./Task'),
    gulp = require('gulp'),
    _ = require('lodash');

var ListingTask = function (buildHelper) {
    Task.call(this, buildHelper);
};

inherits(ListingTask, Task);

ListingTask.prototype.command = "tasks";
ListingTask.prototype.availableToModule = false;
ListingTask.prototype.action = function () {
    console.log();
    _.forEach(gulp.tree(), _.ary(console.log, 1));
    console.log();
};

module.exports = ListingTask;<commit_msg>Enable listing task to modules<commit_after>var inherits = require('util').inherits,
    Task = require('./Task'),
    gulp = require('gulp'),
    _ = require('lodash');

var ListingTask = function (buildHelper) {
    Task.call(this, buildHelper);
};

inherits(ListingTask, Task);

ListingTask.prototype.command = "tasks";
ListingTask.prototype.action = function () {
    console.log();
    _.forEach(gulp.tree(), _.ary(console.log, 1));
    console.log();
};

module.exports = ListingTask;<|endoftext|>


<commit_before>
var serialized_user_data;

function sound_play(x){
	var sound = document.createElement("AUDIO");
	sound.setAttribute("src","sounds/"+x+".mp3");
	sound.autoplay = true;
	document.body.appendChild(sound);
}

function display(x){
	if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
		xmlhttp = new XMLHttpRequest();
	} else { // code for IE6, IE5
		xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
	}
	xmlhttp.onload = document.getElementById("game").innerHTML += "
Loading...
";
	xmlhttp.onreadystatechange = function () {
		if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
			document.getElementById("game").innerHTML = xmlhttp.responseText;
		}
	}
	xmlhttp.open("POST", "engine.php?command="+x, true);
	xmlhttp.send();
}

function post_data(url, data, where_to_show){
	var post = $.post(url, data);
  	post.done(function(received_data) {
    	$("#"+where_to_show).html(received_data);
 	});
}

function login(){
	serialized_user_data = $("#login_form").serialize();
 	post_data('game/login.php', serialized_user_data, 'game');
}

function register(){
	serialized_user_data = $("#register_form").serialize();
 	post_data('game/register.php', serialized_user_data, 'game');
}

function logout(){
	post_data('game/logout.php', null, 'game');
}

function show_attack_gui(monster_id,type){
	post_data('game/attack_gui.php', 'monster_id='+monster_id+'&type='+type, 'game');
}

function shop_show_items(which){
	post_data('game/shop.php', 'which_items_to_show='+which, 'game');
}

function item_action(item_id,action){
	post_data('game/item_functions.php', 'item_index='+item_id+'&action='+action, 'game');
}

function open_chest(item_id){
	post_data('game/open_chest.php', 'item_index='+item_id, 'game');
}

function communicat(type, text){
	var data = "type="+type+"&text="+text;
	post_data('game/communicat.php', data, 'game');
}

function show_page(page_number){
	post_data('game/changepage.php', 'page='+page_number, 'game');
}

window.setInterval(function(){
  var seconds_left = parseInt($("#energy_seconds_left").text());
  if(seconds_left<1){
	  $("#energy_seconds_left").html(0);
  }else{
	seconds_left--;
	$("#energy_seconds_left").html(seconds_left);
  }
}, 1000);

function attack_animation(){
	var user_hp = parseInt($("#actual_user_hp").html());
	if(user_hp === NaN){
		user_hp = 1;
	}
	var max_user_hp = parseInt($("#user_hp").html());

	var user_hp = (user_hp/max_user_hp)*100;

	var opponent_hp = $("#opponent_hp").val();
	console.log(user_hp+" lol "+user_hp+" lol "+max_user_hp);
	$("#progress").css({"width":user_hp});
}

<commit_msg>Bugfixes<commit_after>
var serialized_user_data;

function sound_play(x){
	var sound = document.createElement("AUDIO");
	sound.setAttribute("src","sounds/"+x+".mp3");
	sound.autoplay = true;
	document.body.appendChild(sound);
}

function display(x){
	if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
		xmlhttp = new XMLHttpRequest();
	} else { // code for IE6, IE5
		xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
	}
	xmlhttp.onload = document.getElementById("game").innerHTML += "
Loading...
";
	xmlhttp.onreadystatechange = function () {
		if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
			document.getElementById("game").innerHTML = xmlhttp.responseText;
		}
	}
	xmlhttp.open("POST", "engine.php?command="+x, true);
	xmlhttp.send();
}

function post_data(url, data, where_to_show){
	var post = $.post(url, data);
  	post.done(function(received_data) {
    	$("#"+where_to_show).html(received_data);
 	});
}

function login(){
	serialized_user_data = $("#login_form").serialize();
 	post_data('game/login.php', serialized_user_data, 'game');
}

function register(){
	serialized_user_data = $("#register_form").serialize();
 	post_data('game/register.php', serialized_user_data, 'game');
}

function logout(){
	post_data('game/logout.php', null, 'game');
}

function show_attack_gui(monster_id,type){
	post_data('game/attack_gui.php', 'monster_id='+monster_id+'&type='+type, 'game');
}

function shop_show_items(which){
	post_data('game/shop.php', 'which_items_to_show='+which, 'game');
}

function item_action(item_id,action){
	post_data('game/item_functions.php', 'item_index='+item_id+'&action='+action, 'game');
}

function open_chest(item_id){
	post_data('game/open_chest.php', 'item_index='+item_id, 'game');
}

function communicat(type, text){
	var data = "type="+type+"&text="+text;
	post_data('game/communicat.php', data, 'game');
}

function show_page(page_number){
	post_data('game/changepage.php', 'page='+page_number, 'game');
}

window.setInterval(function(){
  var seconds_left = parseInt($("#energy_seconds_left").text());
  if(seconds_left<1){
	  $("#energy_seconds_left").html(0);
  }else{
	seconds_left--;
	$("#energy_seconds_left").html(seconds_left);
  }
}, 1000);

function attack_animation(){
	var user_hp = parseInt($("#actual_user_hp").html());
	if(user_hp === NaN){
		user_hp = 1;
	}
	var max_user_hp = parseInt($("#user_hp").html());

	var user_hp = (user_hp/max_user_hp)*100;

	var opponent_hp = $("#opponent_hp").val();
	console.log(user_hp+" lol "+user_hp+" lol "+max_user_hp);
	$("#progress").css({"width":user_hp+"%"});
}

<|endoftext|>


<commit_before>(function(angular, $, _) {
  angular.module('crmMosaico').directive('crmMosaicoBlockMailing', function($q, crmMetadata, crmUiHelp) {
    return crmMailingSimpleDirective('crmMosaicoBlockMailing', '~/crmMosaico/BlockMailing.html');
  });
})(angular, CRM.$, CRM._);
<commit_msg>CIVICRM-886: Update injected parameters for crmMosaicoBlockMailing<commit_after>(function(angular, $, _) {
  angular.module('crmMosaico').directive('crmMosaicoBlockMailing', function(crmMailingSimpleDirective) {
    return crmMailingSimpleDirective('crmMosaicoBlockMailing', '~/crmMosaico/BlockMailing.html');
  });
})(angular, CRM.$, CRM._);
<|endoftext|>


<commit_before>var L = require('leaflet');
var LayersCollection = require('../../../../src/geo/map/layers');
var CartoDBLayer = require('../../../../src/geo/map/cartodb-layer');
var CartoDBLayerGroup = require('../../../../src/geo/cartodb-layer-group');
var LeafletCartoDBWebGLLayerGroupView = require('../../../../src/geo/leaflet/leaflet-cartodb-webgl-layer-group-view');
var MockFactory = require('../../../helpers/mockFactory');

describe('src/geo/leaflet/leaflet-cartodb-webgl-layer-group-view.js', function () {
  beforeEach(function () {
    var container = document.createElement('div');
    container.id = 'map';
    document.body.appendChild(container);

    this.leafletMap = L.map('map');
    this.layersCollection = new LayersCollection();
    this.layerGroupModel = new CartoDBLayerGroup({}, {
      layersCollection: this.layersCollection
    });

    this.layerGroupModel.set('urls', { tiles: 'http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7ba36d71b887dd2:0/{layerIndexes}/{z}/{x}/{y}.{format}', subdomains: ['a', 'b', 'c'] });

    var engineMock = MockFactory.createEngine();

    this.layer1 = new CartoDBLayer({ id: 'layer1', meta: {cartocss: '#layer {polygon-fill: black;}'} }, { engine: engineMock });
    this.layer2 = new CartoDBLayer({ id: 'layer2', meta: {cartocss: '#layer {polygon-fill: black;}'} }, { engine: engineMock });

    this.layersCollection.reset([ this.layer1, this.layer2 ]);

    this.view = new LeafletCartoDBWebGLLayerGroupView(this.layerGroupModel, this.leafletMap); // eslint-disable-line
    this.view.tangram = {addLayer: function () {}, addDataSource: function () {}, layer: {setSelectionEvents: function () {}}};

    this.view.initConfig(this.layerGroupModel);
  });

  afterEach(function () {
    document.getElementById('map').remove();
  });

  it('should set the styles options when meta changes', function () {
    spyOn(this.view.tangram, 'addLayer');
    var self = this;

    self.layer1.set('meta', {cartocss: '#layer {polygon-fill: red;}'});
    expect(this.view.tangram.addLayer).toHaveBeenCalled();
  });

  it('should set the visibility options when visibility changes', function () {
    spyOn(this.view.tangram, 'addLayer');
    var self = this;

    self.layer1.set('visible', false);
    expect(this.view.tangram.addLayer).toHaveBeenCalled();
  });

  it('should set a new tile template URL when urls change', function () {
    spyOn(this.view.tangram, 'addDataSource');

    this.layerGroupModel.set('urls', { tiles: 'http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7bas36d71b887dd2:0/{layerIndexes}/{z}/{x}/{y}.{format}', subdomains: ['a', 'b', 'c'] });

    expect(this.view.tangram.addDataSource).toHaveBeenCalledWith('http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7bas36d71b887dd2:0/mapnik/{z}/{x}/{y}.mvt', ['a', 'b', 'c']);
  });

  it('should set a new tile with token when URL changes', function () {
    this.layerGroupModel.set('authToken', 'hahskdfasd');

    spyOn(this.view.tangram, 'addDataSource');

    this.layerGroupModel.set('urls', { tiles: 'http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7ba36d71sd87dd2:0/{layerIndexes}/{z}/{x}/{y}.{format}', subdomains: ['a', 'b', 'c'] });

    expect(this.view.tangram.addDataSource).toHaveBeenCalledWith('http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7ba36d71sd87dd2:0/mapnik/{z}/{x}/{y}.mvt?auth_token=hahskdfasd', ['a', 'b', 'c']);
  });
});
<commit_msg>Add test for _addErrorsEvents<commit_after>var L = require('leaflet');
var LayersCollection = require('../../../../src/geo/map/layers');
var CartoDBLayer = require('../../../../src/geo/map/cartodb-layer');
var CartoDBLayerGroup = require('../../../../src/geo/cartodb-layer-group');
var LeafletCartoDBWebGLLayerGroupView = require('../../../../src/geo/leaflet/leaflet-cartodb-webgl-layer-group-view');
var MockFactory = require('../../../helpers/mockFactory');

describe('src/geo/leaflet/leaflet-cartodb-webgl-layer-group-view.js', function () {
  beforeEach(function () {
    var container = document.createElement('div');
    container.id = 'map';
    document.body.appendChild(container);

    this.leafletMap = L.map('map');
    this.layersCollection = new LayersCollection();
    this.layerGroupModel = new CartoDBLayerGroup({}, {
      layersCollection: this.layersCollection
    });

    this.layerGroupModel.set('urls', { tiles: 'http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7ba36d71b887dd2:0/{layerIndexes}/{z}/{x}/{y}.{format}', subdomains: ['a', 'b', 'c'] });

    var engineMock = MockFactory.createEngine();

    this.layer1 = new CartoDBLayer({ id: 'layer1', meta: {cartocss: '#layer {polygon-fill: black;}'} }, { engine: engineMock });
    this.layer2 = new CartoDBLayer({ id: 'layer2', meta: {cartocss: '#layer {polygon-fill: black;}'} }, { engine: engineMock });

    this.layersCollection.reset([ this.layer1, this.layer2 ]);

    this.view = new LeafletCartoDBWebGLLayerGroupView(this.layerGroupModel, this.leafletMap); // eslint-disable-line
    this.view.tangram = {
      addLayer: function () {},
      addDataSource: function () {},
      layer: {
        setSelectionEvents: function () {}
      },
      scene: {
        subscribe: function (opts) {
          this.tileError = opts.tileError;
        }.bind(this)
      }
    };

    spyOn(this.view.tangram.scene, 'subscribe').and.callThrough();

    this.view.initConfig(this.layerGroupModel);
  });

  afterEach(function () {
    document.getElementById('map').remove();
  });

  it('should set the styles options when meta changes', function () {
    spyOn(this.view.tangram, 'addLayer');
    var self = this;

    self.layer1.set('meta', {cartocss: '#layer {polygon-fill: red;}'});
    expect(this.view.tangram.addLayer).toHaveBeenCalled();
  });

  it('should set the visibility options when visibility changes', function () {
    spyOn(this.view.tangram, 'addLayer');
    var self = this;

    self.layer1.set('visible', false);
    expect(this.view.tangram.addLayer).toHaveBeenCalled();
  });

  it('should set a new tile template URL when urls change', function () {
    spyOn(this.view.tangram, 'addDataSource');

    this.layerGroupModel.set('urls', { tiles: 'http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7bas36d71b887dd2:0/{layerIndexes}/{z}/{x}/{y}.{format}', subdomains: ['a', 'b', 'c'] });

    expect(this.view.tangram.addDataSource).toHaveBeenCalledWith('http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7bas36d71b887dd2:0/mapnik/{z}/{x}/{y}.mvt', ['a', 'b', 'c']);
  });

  it('should set a new tile with token when URL changes', function () {
    this.layerGroupModel.set('authToken', 'hahskdfasd');

    spyOn(this.view.tangram, 'addDataSource');

    this.layerGroupModel.set('urls', { tiles: 'http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7ba36d71sd87dd2:0/{layerIndexes}/{z}/{x}/{y}.{format}', subdomains: ['a', 'b', 'c'] });

    expect(this.view.tangram.addDataSource).toHaveBeenCalledWith('http://0.ashbu.cartocdn.com/documentation/api/v1/map/90e64f1b9145961af7ba36d71sd87dd2:0/mapnik/{z}/{x}/{y}.mvt?auth_token=hahskdfasd', ['a', 'b', 'c']);
  });

  describe('._addErrorsEvents', function () {
    it('should subscribe tileError to the scene', function () {
      var subscribe = this.view.tangram.scene.subscribe;
      expect(subscribe).toHaveBeenCalled();
      expect(subscribe.calls.mostRecent().args[0].tileError).not.toBeUndefined();
    });

    it('should add a limit error to layerGroupModel if status is 429', function () {
      spyOn(this.layerGroupModel, 'addError');
      this.view._addErrorsEvents();
      this.tileError({ statusCode: 429 });

      expect(this.layerGroupModel.addError).toHaveBeenCalledWith({
        type: 'limit'
      });
    });
  });
});
<|endoftext|>


<commit_before>var async = require('async');

var sortObjectByProperty = require('../../lib/array/helpmate-array-utils.js').sortObjectByProperty;

var eachOfLimitInOrder = function (items, concurrency, cb, complete) {
    var pendingOutputs = [];
    var outputDoneUptoIndex = -1;
    var flushOutputs = function () {
        pendingOutputs = pendingOutputs.sort(sortObjectByProperty('index'));
        var pendingOutput = pendingOutputs[0];

        if (pendingOutput) {
            if (pendingOutput.index === outputDoneUptoIndex + 1) {
                pendingOutputs.shift();
                pendingOutput.orderedOutput();
                pendingOutput._cb(pendingOutput.err);
                outputDoneUptoIndex++;
                flushOutputs();
            }
        }
    };

    async.eachOfLimit(items, concurrency, function (item, key, _cb) {
        cb(item, key, function (err, orderedOutput) {
            pendingOutputs.push({index: key, err: err, orderedOutput: orderedOutput, _cb: _cb});
            flushOutputs();
        });
    }, function (err) {
        complete(err);
    });
};

module.exports = eachOfLimitInOrder;
<commit_msg>Parameter "complete" function should be optional in async.eachOfLimitInOrder<commit_after>var async = require('async');

var sortObjectByProperty = require('../../lib/array/helpmate-array-utils.js').sortObjectByProperty;

var eachOfLimitInOrder = function (items, concurrency, cb, complete) {
    var pendingOutputs = [];
    var outputDoneUptoIndex = -1;
    var flushOutputs = function () {
        pendingOutputs = pendingOutputs.sort(sortObjectByProperty('index'));
        var pendingOutput = pendingOutputs[0];

        if (pendingOutput) {
            if (pendingOutput.index === outputDoneUptoIndex + 1) {
                pendingOutputs.shift();
                pendingOutput.orderedOutput();
                pendingOutput._cb(pendingOutput.err);
                outputDoneUptoIndex++;
                flushOutputs();
            }
        }
    };

    async.eachOfLimit(items, concurrency, function (item, key, _cb) {
        cb(item, key, function (err, orderedOutput) {
            pendingOutputs.push({index: key, err: err, orderedOutput: orderedOutput, _cb: _cb});
            flushOutputs();
        });
    }, function (err) {
        complete && complete(err);
    });
};

module.exports = eachOfLimitInOrder;
<|endoftext|>


<commit_before>// Root path to assets.
var ROOT = './';

// Path to build compiled assets to.
var DEST = './build/';

// Build
var paths = {};

/**
 * Clean
 *
 * Folders to remove before doing a full production build.
 */
paths.clean = DEST;

/**
 * Styles
 *
 * Source SASS files, output to `DEST` folder
 */
var CSS = ROOT + 'css/';
var SASS = ROOT + 'sass/';

paths.styles = {
  src:   SASS + 'main.scss',
  dest:  DEST + 'css',
  watch: SASS + '**/*.scss',
};

paths.cssToSass = {
  src:   CSS + '**/*.css',
  dest: SASS + 'css/',
};

/**
 * Scripts
 *
 * Source JavaScript files, output minified to `DEST` folder
 */
var JS = ROOT + 'js/';
var JS_EX = '!' + JS;

paths.scripts = {
  src: [
    JS + '**/*.js',
  ],
  dest: DEST + 'js/',
};

/**
 * JS Hint
 *
 * Lint our source JavaScript files and Gulp files.
 * Exclude files to not be linted with the `JS_EX` path.
 */
paths.jshint = {
  src: [
    JS + '**/*.js',
    JS_EX + 'vendor/**/*.js',
  ],
  gulp: [
    './gulpfile.js',
    './gulp/**/*.js',
  ]
};

/**
 * JavaScript Bundles
 *
 * Add more properties to the `paths.bundles` object with
 * arrays of file names. Each group will be built into a
 * bundle file named: `{property name}.bundle.js`
 * e.g. `main.bundle.js`
 */
paths.bundles = {
  'main': [
//    JS + 'vendor/initr.js',
//    JS + 'initr.config.js',
  ]
};

/**
 * Exclude main bundle files from being minified
 *
 * They are loaded in a minified bundle, no need to build them otherwise.
 */
var exMainBundles = paths.bundles.main.map(function( src ) {
  return src.replace(JS, JS_EX);
});

paths.scripts.src = paths.scripts.src.concat(exMainBundles);

/**
 * Copy
 *
 * Copy JavaScript files from one place (usually `bower_components`)
 * into the `js/vendor` folder.
 */
paths.copy = {
  'copy-fonts': {
    src:  ROOT + 'fonts/**/*',
    dest: DEST + 'fonts'
  }
};

/**
 * Images
 *
 * Compress images and output to `build/images` folder.
 */
paths.images = {
  src:  ROOT + 'img/**/*',
  dest: DEST + 'img'
};

/**
 * File Size
 *
 * Show size of files gzipped and not.
 */
paths.fileSize = [
  paths.styles.dest  + '*',
  paths.scripts.dest + 'main.bundle.js'
];

/**
 * Docs
 *
 * Build JavaScript documentation with JSDocs.
 */
paths.docs = {
  dest : 'docs',
  js: [
    JS + '**/*.js',
    JS_EX + 'vendor/**/*.js',
    'README.md'
  ]
};
paths.docs.js = paths.docs.js.concat(paths.jshint.gulp);

module.exports = paths;
<commit_msg>Fixes issue with gulp watch not registering new files<commit_after>// Path to build compiled assets to.
var DEST = './build/';

// Build
var paths = {};

/**
 * Clean
 *
 * Folders to remove before doing a full production build.
 */
paths.clean = DEST;

/**
 * Styles
 *
 * Source SASS files, output to `DEST` folder
 */
var CSS = 'css/';
var SASS = 'sass/';

paths.styles = {
  src:   SASS + 'main.scss',
  dest:  DEST + 'css',
  watch: SASS + '**/*.scss',
};

paths.cssToSass = {
  src:   CSS + '**/*.css',
  dest: SASS + 'css/',
};

/**
 * Scripts
 *
 * Source JavaScript files, output minified to `DEST` folder
 */
var JS = 'js/';
var JS_EX = '!' + JS;

paths.scripts = {
  src: [
    JS + '**/*.js',
  ],
  dest: DEST + 'js/',
};

/**
 * JS Hint
 *
 * Lint our source JavaScript files and Gulp files.
 * Exclude files to not be linted with the `JS_EX` path.
 */
paths.jshint = {
  src: [
    JS + '**/*.js',
    JS_EX + 'vendor/**/*.js',
  ],
  gulp: [
    './gulpfile.js',
    './gulp/**/*.js',
  ]
};

/**
 * JavaScript Bundles
 *
 * Add more properties to the `paths.bundles` object with
 * arrays of file names. Each group will be built into a
 * bundle file named: `{property name}.bundle.js`
 * e.g. `main.bundle.js`
 */
paths.bundles = {
  'main': [
//    JS + 'vendor/initr.js',
//    JS + 'initr.config.js',
  ]
};

/**
 * Exclude main bundle files from being minified
 *
 * They are loaded in a minified bundle, no need to build them otherwise.
 */
var exMainBundles = paths.bundles.main.map(function( src ) {
  return src.replace(JS, JS_EX);
});

paths.scripts.src = paths.scripts.src.concat(exMainBundles);

/**
 * Copy
 *
 * Copy JavaScript files from one place (usually `bower_components`)
 * into the `js/vendor` folder.
 */
paths.copy = {
  'copy-fonts': {
    src:  'fonts/**/*',
    dest: DEST + 'fonts'
  }
};

/**
 * Images
 *
 * Compress images and output to `build/images` folder.
 */
paths.images = {
  src:  'img/**/*',
  dest: DEST + 'img'
};

/**
 * File Size
 *
 * Show size of files gzipped and not.
 */
paths.fileSize = [
  paths.styles.dest  + '*',
  paths.scripts.dest + 'main.bundle.js'
];

/**
 * Docs
 *
 * Build JavaScript documentation with JSDocs.
 */
paths.docs = {
  dest : 'docs',
  js: [
    JS + '**/*.js',
    JS_EX + 'vendor/**/*.js',
    'README.md'
  ]
};
paths.docs.js = paths.docs.js.concat(paths.jshint.gulp);

module.exports = paths;
<|endoftext|>


<commit_before>const express = require('express')
const qs = require('querystring')
const urlParser = require('url')
const companion = require('../companion')
const helmet = require('helmet')
const morgan = require('morgan')
const bodyParser = require('body-parser')
const redis = require('../server/redis')
const merge = require('lodash.merge')
// @ts-ignore
const promBundle = require('express-prom-bundle')
const session = require('express-session')
const addRequestId = require('express-request-id')()
const helper = require('./helper')
// @ts-ignore
const { version } = require('../../package.json')

const app = express()

// for server metrics tracking.
const metricsMiddleware = promBundle({ includeMethod: true })
const promClient = metricsMiddleware.promClient
const collectDefaultMetrics = promClient.collectDefaultMetrics
const promInterval = collectDefaultMetrics({ register: promClient.register, timeout: 5000 })

// Add version as a prometheus gauge
const versionGauge = new promClient.Gauge({ name: 'companion_version', help: 'npm version as an integer' })
// @ts-ignore
const numberVersion = version.replace(/\D/g, '') * 1
versionGauge.set(numberVersion)

if (app.get('env') !== 'test') {
  clearInterval(promInterval)
}

app.use(addRequestId)
// log server requests.
app.use(morgan('combined'))
morgan.token('url', (req, res) => {
  const query = Object.assign({}, req.query)
  let hasQuery = false;
  ['access_token', 'uppyAuthToken'].forEach((key) => {
    if (req.query && req.query[key]) {
      // replace logged access token with xxxx character
      query[key] = 'x'.repeat(req.query[key].length)
      hasQuery = true
    }
  })

  return hasQuery ? `${req.path}?${qs.stringify(query)}` : req.originalUrl || req.url
})

morgan.token('referrer', (req, res) => {
  const ref = req.headers.referer || req.headers.referrer
  if (typeof ref === 'string') {
    // @todo drop the use of url.parse
    // when support for node 6 is dropped
    // eslint-disable-next-line
    const parsed = urlParser.URL ? new urlParser.URL(ref) : urlParser.parse(ref)
    const query = qs.parse(parsed.search.replace('?', ''));
    ['uppyAuthToken', 'access_token'].forEach(key => {
      if (query[key]) {
        query[key] = 'x'.repeat(query[key].length)
      }
    })

    const hasQuery = parsed.search
    const newURL = `${parsed.href.split('?')[0]}?${qs.stringify(query)}`
    return hasQuery ? newURL : parsed.href
  }
})

// make app metrics available at '/metrics'.
app.use(metricsMiddleware)

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

// Use helmet to secure Express headers
app.use(helmet.frameguard())
app.use(helmet.xssFilter())
app.use(helmet.noSniff())
app.use(helmet.ieNoOpen())
app.disable('x-powered-by')

const companionOptions = helper.getCompanionOptions()
const sessionOptions = {
  secret: companionOptions.secret,
  resave: true,
  saveUninitialized: true
}

if (companionOptions.redisUrl) {
  const RedisStore = require('connect-redis')(session)
  const redisClient = redis.client(
    merge({ url: companionOptions.redisUrl }, companionOptions.redisOptions)
  )
  sessionOptions.store = new RedisStore({ client: redisClient })
}

if (process.env.COMPANION_COOKIE_DOMAIN) {
  sessionOptions.cookie = {
    domain: process.env.COMPANION_COOKIE_DOMAIN,
    maxAge: 24 * 60 * 60 * 1000 // 1 day
  }
}

app.use(session(sessionOptions))

app.use((req, res, next) => {
  const protocol = process.env.COMPANION_PROTOCOL || 'http'

  // if endpoint urls are specified, then we only allow those endpoints
  // otherwise, we allow any client url to access companion.
  // here we also enforce that only the protocol allowed by companion is used.
  if (process.env.COMPANION_CLIENT_ORIGINS) {
    const whitelist = process.env.COMPANION_CLIENT_ORIGINS
      .split(',')
      .map((url) => helper.hasProtocol(url) ? url : `${protocol}://${url}`)

    // @ts-ignore
    if (req.headers.origin && whitelist.indexOf(req.headers.origin) > -1) {
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
      // only allow credentials when origin is whitelisted
      res.setHeader('Access-Control-Allow-Credentials', 'true')
    }
  } else {
    res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
  }

  res.setHeader(
    'Access-Control-Allow-Methods',
    'GET, POST, OPTIONS, PUT, PATCH, DELETE'
  )
  res.setHeader(
    'Access-Control-Allow-Headers',
    'Authorization, Origin, Content-Type, Accept'
  )
  next()
})

// Routes
app.get('/', (req, res) => {
  res.setHeader('Content-Type', 'text/plain')
  res.send(helper.buildHelpfulStartupMessage(companionOptions))
})

// initialize companion
helper.validateConfig(companionOptions)
if (process.env.COMPANION_PATH) {
  app.use(process.env.COMPANION_PATH, companion.app(companionOptions))
} else {
  app.use(companion.app(companionOptions))
}

// WARNING: This route is added in order to validate your app with OneDrive.
// Only set COMPANION_ONEDRIVE_DOMAIN_VALIDATION if you are sure that you are setting the
// correct value for COMPANION_ONEDRIVE_KEY (i.e application ID). If there's a slightest possiblilty
// that you might have mixed the values for COMPANION_ONEDRIVE_KEY and COMPANION_ONEDRIVE_SECRET,
// please do not set a value for COMPANION_ONEDRIVE_DOMAIN_VALIDATION
if (process.env.COMPANION_ONEDRIVE_DOMAIN_VALIDATION === 'true' && process.env.COMPANION_ONEDRIVE_KEY) {
  app.get('/.well-known/microsoft-identity-association.json', (req, res) => {
    res.json(
      {
        associatedApplications: [
          {
            applicationId: process.env.COMPANION_ONEDRIVE_KEY
          }
        ]
      })
  })
}

app.use((req, res, next) => {
  return res.status(404).json({ message: 'Not Found' })
})

if (app.get('env') === 'production') {
  // @ts-ignore
  app.use((err, req, res, next) => {
    console.error('\x1b[31m', req.id, err, '\x1b[0m')
    res.status(err.status || 500).json({ message: 'Something went wrong', requestId: req.id })
  })
} else {
  // @ts-ignore
  app.use((err, req, res, next) => {
    console.error('\x1b[31m', req.id, err, '\x1b[0m')
    res.status(err.status || 500).json({ message: err.message, error: err, requestId: req.id })
  })
}

module.exports = { app, companionOptions }
<commit_msg>companion: set required content type for oneDrive domain verification<commit_after>const express = require('express')
const qs = require('querystring')
const urlParser = require('url')
const companion = require('../companion')
const helmet = require('helmet')
const morgan = require('morgan')
const bodyParser = require('body-parser')
const redis = require('../server/redis')
const merge = require('lodash.merge')
// @ts-ignore
const promBundle = require('express-prom-bundle')
const session = require('express-session')
const addRequestId = require('express-request-id')()
const helper = require('./helper')
// @ts-ignore
const { version } = require('../../package.json')

const app = express()

// for server metrics tracking.
const metricsMiddleware = promBundle({ includeMethod: true })
const promClient = metricsMiddleware.promClient
const collectDefaultMetrics = promClient.collectDefaultMetrics
const promInterval = collectDefaultMetrics({ register: promClient.register, timeout: 5000 })

// Add version as a prometheus gauge
const versionGauge = new promClient.Gauge({ name: 'companion_version', help: 'npm version as an integer' })
// @ts-ignore
const numberVersion = version.replace(/\D/g, '') * 1
versionGauge.set(numberVersion)

if (app.get('env') !== 'test') {
  clearInterval(promInterval)
}

app.use(addRequestId)
// log server requests.
app.use(morgan('combined'))
morgan.token('url', (req, res) => {
  const query = Object.assign({}, req.query)
  let hasQuery = false;
  ['access_token', 'uppyAuthToken'].forEach((key) => {
    if (req.query && req.query[key]) {
      // replace logged access token with xxxx character
      query[key] = 'x'.repeat(req.query[key].length)
      hasQuery = true
    }
  })

  return hasQuery ? `${req.path}?${qs.stringify(query)}` : req.originalUrl || req.url
})

morgan.token('referrer', (req, res) => {
  const ref = req.headers.referer || req.headers.referrer
  if (typeof ref === 'string') {
    // @todo drop the use of url.parse
    // when support for node 6 is dropped
    // eslint-disable-next-line
    const parsed = urlParser.URL ? new urlParser.URL(ref) : urlParser.parse(ref)
    const query = qs.parse(parsed.search.replace('?', ''));
    ['uppyAuthToken', 'access_token'].forEach(key => {
      if (query[key]) {
        query[key] = 'x'.repeat(query[key].length)
      }
    })

    const hasQuery = parsed.search
    const newURL = `${parsed.href.split('?')[0]}?${qs.stringify(query)}`
    return hasQuery ? newURL : parsed.href
  }
})

// make app metrics available at '/metrics'.
app.use(metricsMiddleware)

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

// Use helmet to secure Express headers
app.use(helmet.frameguard())
app.use(helmet.xssFilter())
app.use(helmet.noSniff())
app.use(helmet.ieNoOpen())
app.disable('x-powered-by')

const companionOptions = helper.getCompanionOptions()
const sessionOptions = {
  secret: companionOptions.secret,
  resave: true,
  saveUninitialized: true
}

if (companionOptions.redisUrl) {
  const RedisStore = require('connect-redis')(session)
  const redisClient = redis.client(
    merge({ url: companionOptions.redisUrl }, companionOptions.redisOptions)
  )
  sessionOptions.store = new RedisStore({ client: redisClient })
}

if (process.env.COMPANION_COOKIE_DOMAIN) {
  sessionOptions.cookie = {
    domain: process.env.COMPANION_COOKIE_DOMAIN,
    maxAge: 24 * 60 * 60 * 1000 // 1 day
  }
}

app.use(session(sessionOptions))

app.use((req, res, next) => {
  const protocol = process.env.COMPANION_PROTOCOL || 'http'

  // if endpoint urls are specified, then we only allow those endpoints
  // otherwise, we allow any client url to access companion.
  // here we also enforce that only the protocol allowed by companion is used.
  if (process.env.COMPANION_CLIENT_ORIGINS) {
    const whitelist = process.env.COMPANION_CLIENT_ORIGINS
      .split(',')
      .map((url) => helper.hasProtocol(url) ? url : `${protocol}://${url}`)

    // @ts-ignore
    if (req.headers.origin && whitelist.indexOf(req.headers.origin) > -1) {
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
      // only allow credentials when origin is whitelisted
      res.setHeader('Access-Control-Allow-Credentials', 'true')
    }
  } else {
    res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
  }

  res.setHeader(
    'Access-Control-Allow-Methods',
    'GET, POST, OPTIONS, PUT, PATCH, DELETE'
  )
  res.setHeader(
    'Access-Control-Allow-Headers',
    'Authorization, Origin, Content-Type, Accept'
  )
  next()
})

// Routes
app.get('/', (req, res) => {
  res.setHeader('Content-Type', 'text/plain')
  res.send(helper.buildHelpfulStartupMessage(companionOptions))
})

// initialize companion
helper.validateConfig(companionOptions)
if (process.env.COMPANION_PATH) {
  app.use(process.env.COMPANION_PATH, companion.app(companionOptions))
} else {
  app.use(companion.app(companionOptions))
}

// WARNING: This route is added in order to validate your app with OneDrive.
// Only set COMPANION_ONEDRIVE_DOMAIN_VALIDATION if you are sure that you are setting the
// correct value for COMPANION_ONEDRIVE_KEY (i.e application ID). If there's a slightest possiblilty
// that you might have mixed the values for COMPANION_ONEDRIVE_KEY and COMPANION_ONEDRIVE_SECRET,
// please do not set a value for COMPANION_ONEDRIVE_DOMAIN_VALIDATION
if (process.env.COMPANION_ONEDRIVE_DOMAIN_VALIDATION === 'true' && process.env.COMPANION_ONEDRIVE_KEY) {
  app.get('/.well-known/microsoft-identity-association.json', (req, res) => {
    // use writeHead to prevent 'charset' from being appended
    // https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-configure-publisher-domain#to-select-a-verified-domain
    res.writeHead(200, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify(
      {
        associatedApplications: [
          {
            applicationId: process.env.COMPANION_ONEDRIVE_KEY
          }
        ]
      })
    )
  })
}

app.use((req, res, next) => {
  return res.status(404).json({ message: 'Not Found' })
})

if (app.get('env') === 'production') {
  // @ts-ignore
  app.use((err, req, res, next) => {
    console.error('\x1b[31m', req.id, err, '\x1b[0m')
    res.status(err.status || 500).json({ message: 'Something went wrong', requestId: req.id })
  })
} else {
  // @ts-ignore
  app.use((err, req, res, next) => {
    console.error('\x1b[31m', req.id, err, '\x1b[0m')
    res.status(err.status || 500).json({ message: err.message, error: err, requestId: req.id })
  })
}

module.exports = { app, companionOptions }
<|endoftext|>


<commit_before>/**
 * @file profile command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license GPL-3.0
 */

const specialIDs = xrequire('./assets/specialIDs.json');

exports.exec = async (Bastion, message, args) => {
  let user;
  if (message.mentions.users.size) {
    user = message.mentions.users.first();
  }
  else if (args.id) {
    user = await Bastion.utils.fetchMember(message.guild, args.id);
    if (user) {
      user = user.user;
    }
  }
  if (!user) {
    user = message.author;
  }

  let guildMemberModel = await Bastion.database.models.guildMember.findOne({
    attributes: Object.keys(Bastion.database.models.guildMember.attributes).concat([
      [ Bastion.database.literal(`(SELECT COUNT(*) FROM guildMembers AS member WHERE member.guildID = ${message.guild.id} AND member.experiencePoints * 1 > guildMember.experiencePoints * 1)`), 'rank' ]
    ]),
    where: {
      userID: user.id,
      guildID: message.guild.id
    }
  });

  let userModel = await Bastion.database.models.user.findOne({
    attributes: [ 'avatar', 'info', 'birthDate', 'color', 'location' ],
    where: {
      userID: user.id
    }
  });

  if (!guildMemberModel) {
    return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'profileNotCreated', `<@${user.id}>`), message.channel);
  }

  let info;
  if (userModel && userModel.dataValues.info) {
    info = await Bastion.utils.decompressString(userModel.dataValues.info);
  }
  else {
    info = `No info has been set. ${user.id === message.author.id ? 'Set your info using `setInfo` command.' : ''}`;
  }

  let rank = parseInt(guildMemberModel.dataValues.rank) + 1;
  let totalExp = Bastion.methods.getRequiredExpForLevel(parseInt(guildMemberModel.dataValues.level, 10) + 1) - 1;

  let profileData = [
    {
      name: 'Bastion Currency',
      value: guildMemberModel.dataValues.bastionCurrencies,
      inline: true
    },
    {
      name: 'Rank',
      value: rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : rank,
      inline: true
    },
    {
      name: 'Experience Points',
      value: `**${guildMemberModel.dataValues.experiencePoints}** / ${totalExp}`,
      inline: true
    },
    {
      name: 'Level',
      value: guildMemberModel.dataValues.level,
      inline: true
    },
    {
      name: 'Progress',
      value: Bastion.methods.generateProgressBar(guildMemberModel.dataValues.experiencePoints / totalExp * 100, 35)
    }
  ];

  if (userModel && userModel.dataValues.birthDate) {
    profileData.push({
      name: 'Birthday',
      value: new Date(userModel.dataValues.birthDate).toDateString().split(' ').splice(1, 2).join(' '),
      inline: true
    });
  }
  if (userModel && userModel.dataValues.location) {
    profileData.push({
      name: 'Location',
      value: userModel.dataValues.location,
      inline: true
    });
  }

  await message.channel.send({
    embed: {
      color: userModel.dataValues.color ? userModel.dataValues.color : Bastion.colors.BLUE,
      author: {
        name: user.tag,
        icon_url: await getUserIcon(user)
      },
      description: info,
      fields: profileData,
      thumbnail: {
        url: userModel && userModel.dataValues.avatar ? userModel.dataValues.avatar : user.displayAvatarURL
      },
      footer: {
        text: `${guildMemberModel.dataValues.reputations} Reputation${parseInt(guildMemberModel.dataValues.reputations) === 1 ? '' : 's'}`
      }
    }
  });
};

exports.config = {
  aliases: [],
  enabled: true,
  argsDefinitions: [
    { name: 'id', type: String, defaultOption: true }
  ]
};

exports.help = {
  name: 'profile',
  description: 'Shows Bastion user profile of a specified user of your Discord server.',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'profile [@USER_MENTION | USER_ID]',
  example: [ 'profle', 'profile @Bastion#0001', 'profile 167433345337713651' ]
};

/**
 * Returns the provided user's staff icon
 * @function getUserIcon
 * @param {User} user The user for which we need to get the icon
 * @returns {String} The url of the user's staff icon
 */
async function getUserIcon(user) {
  try {
    const bastionGuildID = specialIDs.bastionGuild;
    const bastionGuild = user.client.guilds.get(bastionGuildID);
    if (!bastionGuild) return;
    const bastionGuildMember = await user.client.utils.fetchMember(bastionGuild, user.id);
    if (!bastionGuildMember) return;

    const devRoleID = specialIDs.developerRole;
    const contributorsRoleID = specialIDs.contributorsRole;
    const donorsRoleID = specialIDs.donorsRole;
    const modsRoleID = specialIDs.modsRole;
    const patronsRoleID = specialIDs.patronsRole;
    const supportRoleID = specialIDs.supportRole;
    const testersRoleID = specialIDs.testersRole;
    const translatorsRoleID = specialIDs.translatorsRole;

    const devIcon = 'https://i.imgur.com/ThSx8bZ.png';
    const modsIcon = 'https://i.imgur.com/vntgkTs.png';
    const contributorsIcon = 'https://i.imgur.com/kH49M8d.png';
    const donorsIcon = 'https://i.imgur.com/0Jfh057.png';
    const patronsIcon = 'https://i.imgur.com/VZePUfw.png';
    const supportIcon = 'http://i.imgur.com/HM9UD6w.png';
    const testersIcon = 'https://i.imgur.com/fVIW1Uy.png';
    const translatorsIcon = 'https://i.imgur.com/COwpvnK.png';
    // const partners = 'https://cdn.discordapp.com/emojis/314068430556758017.png';
    // const hype = 'https://cdn.discordapp.com/emojis/314068430854684672.png';
    // const nitro = 'https://cdn.discordapp.com/emojis/314068430611415041.png';

    if (bastionGuildMember.roles.has(devRoleID)) {
      return devIcon;
    }
    if (bastionGuildMember.roles.has(modsRoleID)) {
      return modsIcon;
    }
    else if (bastionGuildMember.roles.has(contributorsRoleID)) {
      return contributorsIcon;
    }
    else if (bastionGuildMember.roles.has(supportRoleID)) {
      return supportIcon;
    }
    else if (bastionGuildMember.roles.has(patronsRoleID)) {
      return patronsIcon;
    }
    else if (bastionGuildMember.roles.has(donorsRoleID)) {
      return donorsIcon;
    }
    else if (bastionGuildMember.roles.has(testersRoleID)) {
      return testersIcon;
    }
    else if (bastionGuildMember.roles.has(translatorsRoleID)) {
      return translatorsIcon;
    }
  }
  catch (e) {
    process.stderr.write(`${e}\n`);
  }
}
<commit_msg>commands: display progress %age in profile<commit_after>/**
 * @file profile command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license GPL-3.0
 */

const specialIDs = xrequire('./assets/specialIDs.json');

exports.exec = async (Bastion, message, args) => {
  let user;
  if (message.mentions.users.size) {
    user = message.mentions.users.first();
  }
  else if (args.id) {
    user = await Bastion.utils.fetchMember(message.guild, args.id);
    if (user) {
      user = user.user;
    }
  }
  if (!user) {
    user = message.author;
  }

  let guildMemberModel = await Bastion.database.models.guildMember.findOne({
    attributes: Object.keys(Bastion.database.models.guildMember.attributes).concat([
      [ Bastion.database.literal(`(SELECT COUNT(*) FROM guildMembers AS member WHERE member.guildID = ${message.guild.id} AND member.experiencePoints * 1 > guildMember.experiencePoints * 1)`), 'rank' ]
    ]),
    where: {
      userID: user.id,
      guildID: message.guild.id
    }
  });

  let userModel = await Bastion.database.models.user.findOne({
    attributes: [ 'avatar', 'info', 'birthDate', 'color', 'location' ],
    where: {
      userID: user.id
    }
  });

  if (!guildMemberModel) {
    return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'profileNotCreated', `<@${user.id}>`), message.channel);
  }

  let info;
  if (userModel && userModel.dataValues.info) {
    info = await Bastion.utils.decompressString(userModel.dataValues.info);
  }
  else {
    info = `No info has been set. ${user.id === message.author.id ? 'Set your info using `setInfo` command.' : ''}`;
  }

  let rank = parseInt(guildMemberModel.dataValues.rank) + 1;
  let totalExp = Bastion.methods.getRequiredExpForLevel(parseInt(guildMemberModel.dataValues.level, 10) + 1) - 1;
  let progress = guildMemberModel.dataValues.experiencePoints / totalExp * 100;

  let profileData = [
    {
      name: 'Bastion Currency',
      value: guildMemberModel.dataValues.bastionCurrencies,
      inline: true
    },
    {
      name: 'Rank',
      value: rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : rank,
      inline: true
    },
    {
      name: 'Experience Points',
      value: `**${guildMemberModel.dataValues.experiencePoints}** / ${totalExp}`,
      inline: true
    },
    {
      name: 'Level',
      value: guildMemberModel.dataValues.level,
      inline: true
    },
    {
      name: `Progress - ${Math.round(progress)}%`,
      value: Bastion.methods.generateProgressBar(progress, 35)
    }
  ];

  if (userModel && userModel.dataValues.birthDate) {
    profileData.push({
      name: 'Birthday',
      value: new Date(userModel.dataValues.birthDate).toDateString().split(' ').splice(1, 2).join(' '),
      inline: true
    });
  }
  if (userModel && userModel.dataValues.location) {
    profileData.push({
      name: 'Location',
      value: userModel.dataValues.location,
      inline: true
    });
  }

  await message.channel.send({
    embed: {
      color: userModel.dataValues.color ? userModel.dataValues.color : Bastion.colors.BLUE,
      author: {
        name: user.tag,
        icon_url: await getUserIcon(user)
      },
      description: info,
      fields: profileData,
      thumbnail: {
        url: userModel && userModel.dataValues.avatar ? userModel.dataValues.avatar : user.displayAvatarURL
      },
      footer: {
        text: `${guildMemberModel.dataValues.reputations} Reputation${parseInt(guildMemberModel.dataValues.reputations) === 1 ? '' : 's'}`
      }
    }
  });
};

exports.config = {
  aliases: [],
  enabled: true,
  argsDefinitions: [
    { name: 'id', type: String, defaultOption: true }
  ]
};

exports.help = {
  name: 'profile',
  description: 'Shows Bastion user profile of a specified user of your Discord server.',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'profile [@USER_MENTION | USER_ID]',
  example: [ 'profle', 'profile @Bastion#0001', 'profile 167433345337713651' ]
};

/**
 * Returns the provided user's staff icon
 * @function getUserIcon
 * @param {User} user The user for which we need to get the icon
 * @returns {String} The url of the user's staff icon
 */
async function getUserIcon(user) {
  try {
    const bastionGuildID = specialIDs.bastionGuild;
    const bastionGuild = user.client.guilds.get(bastionGuildID);
    if (!bastionGuild) return;
    const bastionGuildMember = await user.client.utils.fetchMember(bastionGuild, user.id);
    if (!bastionGuildMember) return;

    const devRoleID = specialIDs.developerRole;
    const contributorsRoleID = specialIDs.contributorsRole;
    const donorsRoleID = specialIDs.donorsRole;
    const modsRoleID = specialIDs.modsRole;
    const patronsRoleID = specialIDs.patronsRole;
    const supportRoleID = specialIDs.supportRole;
    const testersRoleID = specialIDs.testersRole;
    const translatorsRoleID = specialIDs.translatorsRole;

    const devIcon = 'https://i.imgur.com/ThSx8bZ.png';
    const modsIcon = 'https://i.imgur.com/vntgkTs.png';
    const contributorsIcon = 'https://i.imgur.com/kH49M8d.png';
    const donorsIcon = 'https://i.imgur.com/0Jfh057.png';
    const patronsIcon = 'https://i.imgur.com/VZePUfw.png';
    const supportIcon = 'http://i.imgur.com/HM9UD6w.png';
    const testersIcon = 'https://i.imgur.com/fVIW1Uy.png';
    const translatorsIcon = 'https://i.imgur.com/COwpvnK.png';
    // const partners = 'https://cdn.discordapp.com/emojis/314068430556758017.png';
    // const hype = 'https://cdn.discordapp.com/emojis/314068430854684672.png';
    // const nitro = 'https://cdn.discordapp.com/emojis/314068430611415041.png';

    if (bastionGuildMember.roles.has(devRoleID)) {
      return devIcon;
    }
    if (bastionGuildMember.roles.has(modsRoleID)) {
      return modsIcon;
    }
    else if (bastionGuildMember.roles.has(contributorsRoleID)) {
      return contributorsIcon;
    }
    else if (bastionGuildMember.roles.has(supportRoleID)) {
      return supportIcon;
    }
    else if (bastionGuildMember.roles.has(patronsRoleID)) {
      return patronsIcon;
    }
    else if (bastionGuildMember.roles.has(donorsRoleID)) {
      return donorsIcon;
    }
    else if (bastionGuildMember.roles.has(testersRoleID)) {
      return testersIcon;
    }
    else if (bastionGuildMember.roles.has(translatorsRoleID)) {
      return translatorsIcon;
    }
  }
  catch (e) {
    process.stderr.write(`${e}\n`);
  }
}
<|endoftext|>


<commit_before>var APIKEY = require('./config.js').key;
var _ = require('underscore.deferred');
var I = require('inflection');
var request = require('request');
var article = require('./lib/indefinite');
var Wordnik = require('wordnik-bb').init(APIKEY);

// fill the blacklist from a file

var fs = require('fs');
var blacklist = [];
try {
  var data = fs.readFileSync('badwords.txt', 'ascii');
  data.split('\n').forEach(function (line) {
    if(line.length>0) {
      blacklist.push(line);
    }
  });
}
catch (err) {
  console.error("There was an error opening the file:");
  console.log(err);
}

var express = require('express'),
  app = express();
app.use(express.logger());
app.listen(3000);
console.log('Express server started on port 3000');

function getCoupletPromise() {

  var coupletDeferred = _.Deferred();
  var coupletPromise = coupletDeferred.promise();

  var url = "http://api.wordnik.com//v4/words.json/randomWord?includePartOfSpeech=noun&excludePartOfSpeech=proper-noun,proper-noun-plural,proper-noun-posessive,suffix,family-name,idiom,affix&minCorpusCount=5000&api_key=" + APIKEY;
  var rwDeferred = _.Deferred();
  var randomWordPromise = rwDeferred.promise();
  request({
    url: url
  }, function (error, response, body) {
    if (!error && response.statusCode === 200) {
      //console.log(JSON.parse(body).word);
      //console.log(I.singularize(JSON.parse(body).word));
      var word = new Wordnik.Word({
        word: I.singularize(JSON.parse(body).word),
        params: {
          relationshipTypes: 'rhyme',
          limitPerRelationshipType: 100,
          hasDictionaryDef: true
        }
      });
      rwDeferred.resolve(word);

    }
    else {
      rwDeferred.reject(error);
    }
  });

  randomWordPromise.done(function (word) {
    //console.log("The model for our random word: ", word);
    // We could also get more info about the random word, in this case, relatedWords that rhyme:
    _.when(word.getRelatedWords(),word.getDefinitions())
      .then(function () {
      if (isBlacklisted(word.id)) {
        coupletDeferred.resolve("");
      }
      if (word.get("relatedWords").length > 0) {
        var wordPos = word.get("definitions")[0].partOfSpeech;
        var first = getLine(word.id, wordPos);
        if (first === "") {
          coupletDeferred.resolve(first);
        }
        else {
          var word2 = word.get("relatedWords")[0].words[Math.floor(Math.random() * word.get("relatedWords")[0].words.length)];
          // quit this couplet if blacklisted word comes up
          if (isBlacklisted(word2)) {
            coupletDeferred.resolve("");
          }

          var posPromise = getPartOfSpeech(word2);
          (function (first, word2) {

            posPromise.done(function (pos) {
              var result = "oops!";
              result = getLine(word2, pos);
              if (result === "") {
                coupletDeferred.resolve(result);
              }
              else {
                var regex = /(<([^>]+)>)/ig;
                coupletDeferred.resolve(first + "\n
" + result + "\nTweet!
");
              }
            });
          })(first, word2);
        }
      }
      else {
        //coupletDeferred.resolve("Sorry. We couldn't find anything that rhymes with " + word.id + "!");
        coupletDeferred.resolve("");
      }
    });
  });
  return coupletDeferred.promise();
}

app.get('/', function (req, res) {

  var cypher = "";

  var stuffToDo = [];
  for (var i = 0; i < 12; i++) {
    var cp = getCoupletPromise();
    cp.done(function (couplet) {
      if (couplet !== "") {
        cypher += (couplet);
      }
    });
    stuffToDo.push(cp);
  }

  _.when(stuffToDo).done(function () {
    //console.log(cypher);
    //console.log('*drops the mic*');
    cypher += "
*drops the mic*";
    res.send('<!doctype html><title>Freestyle 80s Battle Rap Generator</title>
Freestyle 80s Battle Rap Generator

' + cypher + '
');

  });

});

function getPartOfSpeech(wordId) {
  // accepts a word string
  var word = new Wordnik.Word({
    word: wordId,
    params: {
      includeSuggestions: true
    }
  });
  var deferred = _.Deferred();
  word.getDefinitions()
    .then(function (word) {
    deferred.resolve(word.get("definitions")[0].partOfSpeech);
  });
  return deferred.promise();
}

function ladiesFellas() {
  return (Math.random() < 0.5) ? "ladies" : "fellas";
}

function womanMan() {
  return (Math.random() < 0.5) ? "woman" : "man";
}

function sistasHomies() {
  return (Math.random() < 0.5) ? "sistas" : "homies";
}

function w(word) {
  return "" + word + "";
}

function getLine(word, pos) {

  var result = "Oops, we didn't account for something.";

  if (pos === 'adjective') {
    var pre = [
      "You can try and battle me, but you're too ",
      "I make the MCs in the place wish that they were ",
      "My rhymes blow your mind and you think it's ",
      "My sweet-ass rhymes make your " + womanMan() + " feel ",
      "Now I'm gonna tell you why you ain't ",
      "You'll never beat me 'cause I'm so "
      ];
    result = pre[Math.floor(Math.random() * pre.length)] + w(word);
  }
  else if (pos === 'noun' || pos === 'proper-noun') {
    var a = article(word) + " ";
    var pre = [
      "I'm the illest MC to ever rock the ",
      "When I'm on the mic you realize you're " + a,
      "My rhymes bring the power like a raging ",
      "If you can't handle this then you're nothing but " + a,
      "When I come to a battle I'm strapped with " + a,
      "When you battle me it's like you battle " + a,
      "Every other MC is a sucker ",
      "There's nobody like me 'cause I'm the greatest ",
      "You hear my freestyle and you drop your ",
      "My flow and my style both blow away the ",
      "My posse's got my back and my " + sistasHomies() + " got my ",
      "Sweeter than molasses, and stronger than " + a,
      "Try to step to me and I'mma wreck your ",
      "Wherever I go, people give me some ",
      "You're nothin' but a scrub, word to your "
      ];
    result = pre[Math.floor(Math.random() * pre.length)] + w(I.singularize(word));
  }
  else if (pos === 'verb-transitive') {
    result = "My rhyme profile makes the " + ladiesFellas() + " " + w(word);
  }
  else if (pos === 'interjection') {
    result = "*skratch solo* ... (" + word[0] + "-" + word[0] + "-" + w(word) + "!)";
  }
  else {
    result = "";
  }

  return result;

}

function isBlacklisted(data) {
  var result = false;
  for (var i=0;i<blacklist.length;i++) {
    if (data.indexOf(blacklist[i]) >= 0) {
      result = true;
    }
  }
  return result;
}

<commit_msg>Adding more parts of speech, more lines, and specific responses for proper nouns<commit_after>var APIKEY = require('./config.js').key;
var _ = require('underscore.deferred');
var I = require('inflection');
var request = require('request');
var article = require('./lib/indefinite');
var Wordnik = require('wordnik-bb').init(APIKEY);

// fill the blacklist from a file

var fs = require('fs');
var blacklist = [];
try {
  var data = fs.readFileSync('badwords.txt', 'ascii');
  data.split('\n').forEach(function (line) {
    if(line.length>0) {
      blacklist.push(line);
    }
  });
}
catch (err) {
  console.error("There was an error opening the file:");
  console.log(err);
}

var express = require('express'),
  app = express();
app.use(express.logger());
app.listen(3000);
console.log('Express server started on port 3000');

function getCoupletPromise() {

  var coupletDeferred = _.Deferred();
  var coupletPromise = coupletDeferred.promise();

  var url = "http://api.wordnik.com//v4/words.json/randomWord?includePartOfSpeech=noun,adjective,verb-transitive&excludePartOfSpeech=proper-noun,proper-noun-plural,proper-noun-posessive,suffix,family-name,idiom,affix&minCorpusCount=5000&api_key=" + APIKEY;
  var rwDeferred = _.Deferred();
  var randomWordPromise = rwDeferred.promise();
  request({
    url: url
  }, function (error, response, body) {
    if (!error && response.statusCode === 200) {
      //console.log(JSON.parse(body).word);
      //console.log(I.singularize(JSON.parse(body).word));
      var word = new Wordnik.Word({
        word: I.singularize(JSON.parse(body).word),
        params: {
          relationshipTypes: 'rhyme',
          limitPerRelationshipType: 100,
          hasDictionaryDef: true
        }
      });
      rwDeferred.resolve(word);

    }
    else {
      rwDeferred.reject(error);
    }
  });

  randomWordPromise.done(function (word) {
    //console.log("The model for our random word: ", word);
    // We could also get more info about the random word, in this case, relatedWords that rhyme:
    _.when(word.getRelatedWords(),word.getDefinitions())
      .then(function () {
      if (isBlacklisted(word.id)) {
        coupletDeferred.resolve("");
      }
      if (word.get("relatedWords").length > 0) {
        var wordPos = word.get("definitions")[0].partOfSpeech;
        var first = getLine(word.id, wordPos);
        if (first === "") {
          coupletDeferred.resolve(first);
        }
        else {
          var word2 = word.get("relatedWords")[0].words[Math.floor(Math.random() * word.get("relatedWords")[0].words.length)];
          // quit this couplet if blacklisted word comes up
          if (isBlacklisted(word2)) {
            coupletDeferred.resolve("");
          }

          var posPromise = getPartOfSpeech(word2);
          (function (first, word2) {

            posPromise.done(function (pos) {
              var result = "oops!";
              result = getLine(word2, pos);
              if (result === "") {
                coupletDeferred.resolve(result);
              }
              else {
                var regex = /(<([^>]+)>)/ig;
                coupletDeferred.resolve(first + "\n
" + result + "\nTweet!
");
              }
            });
          })(first, word2);
        }
      }
      else {
        //coupletDeferred.resolve("Sorry. We couldn't find anything that rhymes with " + word.id + "!");
        coupletDeferred.resolve("");
      }
    });
  });
  return coupletDeferred.promise();
}

app.get('/', function (req, res) {

  var cypher = "";

  var stuffToDo = [];
  for (var i = 0; i < 12; i++) {
    var cp = getCoupletPromise();
    cp.done(function (couplet) {
      if (couplet !== "") {
        cypher += (couplet);
      }
    });
    stuffToDo.push(cp);
  }

  _.when(stuffToDo).done(function () {
    //console.log(cypher);
    //console.log('*drops the mic*');
    cypher += "
*drops the mic*";
    res.send('<!doctype html><title>Freestyle 80s Battle Rap Generator</title>
Freestyle 80s Battle Rap Generator

' + cypher + '
');

  });

});

function getPartOfSpeech(wordId) {
  // accepts a word string
  var word = new Wordnik.Word({
    word: wordId,
    params: {
      includeSuggestions: true
    }
  });
  var deferred = _.Deferred();
  word.getDefinitions()
    .then(function (word) {
    deferred.resolve(word.get("definitions")[0].partOfSpeech);
  });
  return deferred.promise();
}

function ladiesFellas() {
  return (Math.random() < 0.5) ? "ladies" : "fellas";
}

function womanMan() {
  return (Math.random() < 0.5) ? "woman" : "man";
}

function sistasHomies() {
  return (Math.random() < 0.5) ? "sistas" : "homies";
}

function w(word) {
  return "" + word + "";
}

function getLine(word, pos) {

  var result = "Oops, we didn't account for something.";

  if (pos === 'adjective') {
    var pre = [
      "You can try and battle me, but you're too ",
      "I make the MCs in the place wish that they were ",
      "My rhymes blow your mind and you think it's ",
      "My sweet-ass rhymes make your " + womanMan() + " feel ",
      "Now I'm gonna tell you why you ain't ",
      "You'll never beat me 'cause I'm so ",
      "If you're gonna battle me, then you gotta be ",
      "When I rock a mic you know I rock it real ",
      "If a rapper tries to step I'm gonna get "
      ];
    result = pre[Math.floor(Math.random() * pre.length)] + w(word);
  }
  else if (pos === 'noun') {
    var a = article(word) + " ";
    var pre = [
      "I'm the illest MC to ever rock the ",
      "When I'm on the mic you realize you're " + a,
      "My rhymes bring the power like a raging ",
      "If you can't handle this then you're nothing but " + a,
      "When I come to a battle I'm strapped with " + a,
      "When you battle me it's like you battle " + a,
      "Every other MC is a sucker ",
      "There's nobody like me 'cause I'm the greatest ",
      "You hear my freestyle and you drop your ",
      "My flow and my style both blow away the ",
      "My posse's got my back and my " + sistasHomies() + " got my ",
      "Sweeter than molasses, and stronger than " + a,
      "Try to step to me and I'mma wreck your ",
      "Wherever I go, people give me some ",
      "You're nothin' but a scrub, word to your "
      ];
    result = pre[Math.floor(Math.random() * pre.length)] + w(I.singularize(word));
  }
  else if (pos === 'proper-noun') {
    var a = article(word) + " ";
    var pre = [
      "I'm playing you and your best friend ",
      "I know how to charm a " + womanMan() + ", just ask your friend ",
      "I've battled every MC, every Tom, Dick, and "
      ];
    result = pre[Math.floor(Math.random() * pre.length)] + w(I.singularize(word));
  }
  else if (pos === 'verb-transitive') {
    result = "My rhyme profile makes the " + ladiesFellas() + " " + w(word);
  }
  else if (pos === 'interjection') {
    result = "*skratch solo* ... (" + word[0] + "-" + word[0] + "-" + w(word) + "!)";
  }
  else {
    result = "";
  }

  return result;

}

function isBlacklisted(data) {
  var result = false;
  for (var i=0;i<blacklist.length;i++) {
    if (data.indexOf(blacklist[i]) >= 0) {
      result = true;
    }
  }
  return result;
}

<|endoftext|>