<commit_before>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import {AlertManagerContext} from '@canvas/alerts/react/AlertManager'
import {ApolloProvider} from 'react-apollo'
import {DiscussionEntry} from '../../../../graphql/DiscussionEntry'
import {fireEvent, render} from '@testing-library/react'
import {graphql} from 'msw'
import {handlers} from '../../../../graphql/mswHandlers'
import {IsolatedViewContainer} from '../IsolatedViewContainer'
import {mswClient} from '../../../../../../shared/msw/mswClient'
import {mswServer} from '../../../../../../shared/msw/mswServer'
import {PageInfo} from '../../../../graphql/PageInfo'
import React from 'react'
import {number} from "prop-types";

describe('IsolatedViewContainer', () => {
  const server = mswServer(handlers)
  const setOnFailure = jest.fn()
  const setOnSuccess = jest.fn()
  const onOpenIsolatedView = jest.fn()

  beforeAll(() => {
    // eslint-disable-next-line no-undef
    fetchMock.dontMock()
    server.listen()

    window.ENV = {
      discussion_topic_id: '1',
      manual_mark_as_read: false,
      current_user: {
        id: 'PLACEHOLDER',
        display_name: 'Omar Soto-Fortuño',
        avatar_image_url: 'www.avatar.com'
      },
      course_id: '1'
    }
  })

  afterEach(() => {
    mswClient.resetStore()
    server.resetHandlers()
    setOnFailure.mockClear()
    setOnSuccess.mockClear()
    onOpenIsolatedView.mockClear()
  })

  afterAll(() => {
    server.close()
    // eslint-disable-next-line no-undef
    fetchMock.enableMocks()
  })

  const setup = props => {
    return render(
      <ApolloProvider client={mswClient}>
        <AlertManagerContext.Provider value={{setOnFailure, setOnSuccess}}>
          <IsolatedViewContainer {...props} />
        </AlertManagerContext.Provider>
      </ApolloProvider>
    )
  }

  const defaultProps = overrides => ({
    discussionEntryId: '1',
    open: true,
    onClose: () => {},
    onOpenIsolatedView,
    ...overrides
  })

  it('should render', () => {
    const {container} = setup(defaultProps())
    expect(container).toBeTruthy()
  })

  it('should render a back button', async () => {
    const {findByTestId} = setup(defaultProps())

    const backButton = await findByTestId('back-button')
    expect(backButton).toBeInTheDocument()

    fireEvent.click(backButton)

    expect(onOpenIsolatedView).toHaveBeenCalledWith('77', false)
  })

  it('should go to root reply when clicking Go To Parent', async () => {
    server.use(
      graphql.query('GetDiscussionSubentriesQuery', (req, res, ctx) => {
        return res.once(
          ctx.data({
            legacyNode: DiscussionEntry.mock({
              discussionSubentriesConnection: {
                nodes: [
                  DiscussionEntry.mock({
                    _id: '50',
                    id: '50',
                    message: '
This is the child reply
',
                    rootEntry: DiscussionEntry.mock({id: '70'})
                  })
                ],
                pageInfo: PageInfo.mock(),
                __typename: 'DiscussionSubentriesConnection'
              }
            })
          })
        )
      })
    )

    const {findAllByTestId, findByText} = setup(defaultProps())

    const threadActionsMenus = await findAllByTestId('thread-actions-menu')
    expect(threadActionsMenus[1]).toBeInTheDocument()
    fireEvent.click(threadActionsMenus[1])

    const goToParentButton = await findByText('Go To Parent')
    expect(goToParentButton).toBeInTheDocument()
    fireEvent.click(goToParentButton)

    expect(onOpenIsolatedView).toHaveBeenCalledWith('70', false)
  })

  it('should not render a back button', async () => {
    server.use(
      graphql.query('GetDiscussionSubentriesQuery', (req, res, ctx) => {
        return res.once(
          ctx.data({
            legacyNode: DiscussionEntry.mock({parent: null})
          })
        )
      })
    )
    const {findByText, queryByTestId} = setup(defaultProps())
    expect(await findByText('This is the parent reply')).toBeInTheDocument()
    expect(queryByTestId('back-button')).toBeNull()
  })

  it('allows fetching more discussion entries', async () => {
    const {findByText, queryByText} = setup(defaultProps())

    const showOlderRepliesButton = await findByText('Show older replies')
    expect(showOlderRepliesButton).toBeInTheDocument()
    expect(queryByText('Get riggity riggity wrecked son')).toBe(null)

    server.use(
      graphql.query('GetDiscussionSubentriesQuery', (req, res, ctx) => {
        return res.once(
          ctx.data({
            legacyNode: DiscussionEntry.mock({
              discussionSubentriesConnection: {
                nodes: [
                  DiscussionEntry.mock({
                    id: '1337',
                    _id: '1337',
                    message: '
Get riggity riggity wrecked son
'
                  })
                ],
                pageInfo: PageInfo.mock(),
                __typename: 'DiscussionSubentriesConnection'
              }
            })
          })
        )
      })
    )

    fireEvent.click(showOlderRepliesButton)

    expect(await findByText('Get riggity riggity wrecked son')).toBeInTheDocument()
  })

  it('calls the onOpenIsolatedView callback when clicking View Replies', async () => {
    const {findByText} = setup(defaultProps())

    const viewRepliesButton = await findByText('View Replies')
    fireEvent.click(viewRepliesButton)

    expect(onOpenIsolatedView).toHaveBeenCalledWith('50', false)
  })

  it('calls the onOpenIsolatedView callback when clicking reply', async () => {
    const {findAllByText} = setup(defaultProps())

    const replyButton = await findAllByText('Reply')
    fireEvent.click(replyButton[1])

    expect(onOpenIsolatedView).toHaveBeenCalledWith('50', true)
  })

  describe('replying', () => {
    describe('RCE is open', () => {
      const props = defaultProps({RCEOpen: true})

      it('should not display children', () => {
        const {queryByTestId} = setup(props)
        expect(queryByTestId('isolated-view-children')).toBeFalsy()
      })
    })

    describe('RCE is closed', () => {
      const props = defaultProps({RCEOpen: false})

      it('should display children', async () => {
        const {findByTestId} = setup(props)
        expect(await findByTestId('isolated-view-children')).toBeTruthy()
      })
    })
  })

  it('disables the reply and enables the expand buttons if the RCE is open', async () => {
    const setRCEOpen = jest.fn()
    const {findByTestId} = setup(defaultProps({RCEOpen: true, setRCEOpen}))

    expect(await findByTestId('DiscussionEdit-container')).toBeInTheDocument()
    expect(await findByTestId('threading-toolbar-reply')).toBeDisabled()
    expect(await findByTestId('expand-button')).toBeEnabled()
  })

  it('disables the expand and enables the reply buttons if the RCE is closed', async () => {
    const setRCEOpen = jest.fn()
    const {findAllByTestId, queryByTestId} = setup(defaultProps({RCEOpen: false, setRCEOpen}))

    const replyButtons = await findAllByTestId('threading-toolbar-reply')
    expect(replyButtons[0]).toBeEnabled()
    const expandButtons = await findAllByTestId('expand-button')
    expect(expandButtons[0]).toBeDisabled()
    expect(queryByTestId('DiscussionEdit-container')).toBe(null)
  })

  it('calls the setRCEOpen callback with false when clicking the expand button', async () => {
    const setRCEOpen = jest.fn()
    const {findByTestId} = setup(defaultProps({RCEOpen: true, setRCEOpen}))

    fireEvent.click(await findByTestId('expand-button'))
    expect(setRCEOpen).toHaveBeenCalledWith(false)
  })

  it('calls the setRCEOpen callback with true when clicking the reply button', async () => {
    const setRCEOpen = jest.fn()
    const {findAllByTestId} = setup(defaultProps({RCEOpen: false, setRCEOpen}))

    const replyButtons = await findAllByTestId('threading-toolbar-reply')
    fireEvent.click(replyButtons[0])
    expect(setRCEOpen).toHaveBeenCalledWith(true)
  })
})
<commit_msg>Remove unnecessary import.<commit_after>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import {AlertManagerContext} from '@canvas/alerts/react/AlertManager'
import {ApolloProvider} from 'react-apollo'
import {DiscussionEntry} from '../../../../graphql/DiscussionEntry'
import {fireEvent, render} from '@testing-library/react'
import {graphql} from 'msw'
import {handlers} from '../../../../graphql/mswHandlers'
import {IsolatedViewContainer} from '../IsolatedViewContainer'
import {mswClient} from '../../../../../../shared/msw/mswClient'
import {mswServer} from '../../../../../../shared/msw/mswServer'
import {PageInfo} from '../../../../graphql/PageInfo'
import React from 'react'

describe('IsolatedViewContainer', () => {
  const server = mswServer(handlers)
  const setOnFailure = jest.fn()
  const setOnSuccess = jest.fn()
  const onOpenIsolatedView = jest.fn()

  beforeAll(() => {
    // eslint-disable-next-line no-undef
    fetchMock.dontMock()
    server.listen()

    window.ENV = {
      discussion_topic_id: '1',
      manual_mark_as_read: false,
      current_user: {
        id: 'PLACEHOLDER',
        display_name: 'Omar Soto-Fortuño',
        avatar_image_url: 'www.avatar.com'
      },
      course_id: '1'
    }
  })

  afterEach(() => {
    mswClient.resetStore()
    server.resetHandlers()
    setOnFailure.mockClear()
    setOnSuccess.mockClear()
    onOpenIsolatedView.mockClear()
  })

  afterAll(() => {
    server.close()
    // eslint-disable-next-line no-undef
    fetchMock.enableMocks()
  })

  const setup = props => {
    return render(
      <ApolloProvider client={mswClient}>
        <AlertManagerContext.Provider value={{setOnFailure, setOnSuccess}}>
          <IsolatedViewContainer {...props} />
        </AlertManagerContext.Provider>
      </ApolloProvider>
    )
  }

  const defaultProps = overrides => ({
    discussionEntryId: '1',
    open: true,
    onClose: () => {},
    onOpenIsolatedView,
    ...overrides
  })

  it('should render', () => {
    const {container} = setup(defaultProps())
    expect(container).toBeTruthy()
  })

  it('should render a back button', async () => {
    const {findByTestId} = setup(defaultProps())

    const backButton = await findByTestId('back-button')
    expect(backButton).toBeInTheDocument()

    fireEvent.click(backButton)

    expect(onOpenIsolatedView).toHaveBeenCalledWith('77', false)
  })

  it('should go to root reply when clicking Go To Parent', async () => {
    server.use(
      graphql.query('GetDiscussionSubentriesQuery', (req, res, ctx) => {
        return res.once(
          ctx.data({
            legacyNode: DiscussionEntry.mock({
              discussionSubentriesConnection: {
                nodes: [
                  DiscussionEntry.mock({
                    _id: '50',
                    id: '50',
                    message: '
This is the child reply
',
                    rootEntry: DiscussionEntry.mock({id: '70'})
                  })
                ],
                pageInfo: PageInfo.mock(),
                __typename: 'DiscussionSubentriesConnection'
              }
            })
          })
        )
      })
    )

    const {findAllByTestId, findByText} = setup(defaultProps())

    const threadActionsMenus = await findAllByTestId('thread-actions-menu')
    expect(threadActionsMenus[1]).toBeInTheDocument()
    fireEvent.click(threadActionsMenus[1])

    const goToParentButton = await findByText('Go To Parent')
    expect(goToParentButton).toBeInTheDocument()
    fireEvent.click(goToParentButton)

    expect(onOpenIsolatedView).toHaveBeenCalledWith('70', false)
  })

  it('should not render a back button', async () => {
    server.use(
      graphql.query('GetDiscussionSubentriesQuery', (req, res, ctx) => {
        return res.once(
          ctx.data({
            legacyNode: DiscussionEntry.mock({parent: null})
          })
        )
      })
    )
    const {findByText, queryByTestId} = setup(defaultProps())
    expect(await findByText('This is the parent reply')).toBeInTheDocument()
    expect(queryByTestId('back-button')).toBeNull()
  })

  it('allows fetching more discussion entries', async () => {
    const {findByText, queryByText} = setup(defaultProps())

    const showOlderRepliesButton = await findByText('Show older replies')
    expect(showOlderRepliesButton).toBeInTheDocument()
    expect(queryByText('Get riggity riggity wrecked son')).toBe(null)

    server.use(
      graphql.query('GetDiscussionSubentriesQuery', (req, res, ctx) => {
        return res.once(
          ctx.data({
            legacyNode: DiscussionEntry.mock({
              discussionSubentriesConnection: {
                nodes: [
                  DiscussionEntry.mock({
                    id: '1337',
                    _id: '1337',
                    message: '
Get riggity riggity wrecked son
'
                  })
                ],
                pageInfo: PageInfo.mock(),
                __typename: 'DiscussionSubentriesConnection'
              }
            })
          })
        )
      })
    )

    fireEvent.click(showOlderRepliesButton)

    expect(await findByText('Get riggity riggity wrecked son')).toBeInTheDocument()
  })

  it('calls the onOpenIsolatedView callback when clicking View Replies', async () => {
    const {findByText} = setup(defaultProps())

    const viewRepliesButton = await findByText('View Replies')
    fireEvent.click(viewRepliesButton)

    expect(onOpenIsolatedView).toHaveBeenCalledWith('50', false)
  })

  it('calls the onOpenIsolatedView callback when clicking reply', async () => {
    const {findAllByText} = setup(defaultProps())

    const replyButton = await findAllByText('Reply')
    fireEvent.click(replyButton[1])

    expect(onOpenIsolatedView).toHaveBeenCalledWith('50', true)
  })

  describe('replying', () => {
    describe('RCE is open', () => {
      const props = defaultProps({RCEOpen: true})

      it('should not display children', () => {
        const {queryByTestId} = setup(props)
        expect(queryByTestId('isolated-view-children')).toBeFalsy()
      })
    })

    describe('RCE is closed', () => {
      const props = defaultProps({RCEOpen: false})

      it('should display children', async () => {
        const {findByTestId} = setup(props)
        expect(await findByTestId('isolated-view-children')).toBeTruthy()
      })
    })
  })

  it('disables the reply and enables the expand buttons if the RCE is open', async () => {
    const setRCEOpen = jest.fn()
    const {findByTestId} = setup(defaultProps({RCEOpen: true, setRCEOpen}))

    expect(await findByTestId('DiscussionEdit-container')).toBeInTheDocument()
    expect(await findByTestId('threading-toolbar-reply')).toBeDisabled()
    expect(await findByTestId('expand-button')).toBeEnabled()
  })

  it('disables the expand and enables the reply buttons if the RCE is closed', async () => {
    const setRCEOpen = jest.fn()
    const {findAllByTestId, queryByTestId} = setup(defaultProps({RCEOpen: false, setRCEOpen}))

    const replyButtons = await findAllByTestId('threading-toolbar-reply')
    expect(replyButtons[0]).toBeEnabled()
    const expandButtons = await findAllByTestId('expand-button')
    expect(expandButtons[0]).toBeDisabled()
    expect(queryByTestId('DiscussionEdit-container')).toBe(null)
  })

  it('calls the setRCEOpen callback with false when clicking the expand button', async () => {
    const setRCEOpen = jest.fn()
    const {findByTestId} = setup(defaultProps({RCEOpen: true, setRCEOpen}))

    fireEvent.click(await findByTestId('expand-button'))
    expect(setRCEOpen).toHaveBeenCalledWith(false)
  })

  it('calls the setRCEOpen callback with true when clicking the reply button', async () => {
    const setRCEOpen = jest.fn()
    const {findAllByTestId} = setup(defaultProps({RCEOpen: false, setRCEOpen}))

    const replyButtons = await findAllByTestId('threading-toolbar-reply')
    fireEvent.click(replyButtons[0])
    expect(setRCEOpen).toHaveBeenCalledWith(true)
  })
})
<|endoftext|>


<commit_before>import React from 'react';

class EmptyState extends React.Component {

  render() {
    return (

        {this.props.icon}

{this.props.title}

{this.props.message}

        {this.props.children}

    );
  }

}

export default EmptyState;
<commit_msg>fixing lint issues in EmptyState component<commit_after>import React from 'react';

class EmptyState extends React.PureComponent {

  render() {
    return (

        {this.props.icon}

{this.props.title}

{this.props.message}

        {this.props.children}

    );
  }

}

EmptyState.propTypes = {
  icon: React.PropTypes.string,
  title: React.PropTypes.string,
  message: React.PropTypes.string,
  children: React.PropTypes.node,
};

export default EmptyState;
<|endoftext|>


<commit_before>/*
   Copyright (c) 2015, salesforce.com, inc. All rights reserved.
   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
   Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
   Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */

import React from 'react';
import ReactDOM from 'react-dom';

import Item from './Item';

const displayName = 'SLDSLookup-Menu';
const propTypes = {
  boldRegex: React.PropTypes.instanceOf(RegExp),
  emptyMessage: React.PropTypes.string,
  errors: React.PropTypes.arrayOf(React.PropTypes.string),
  filterWith: React.PropTypes.func,
  focusIndex: React.PropTypes.number,
  getListLength: React.PropTypes.func,
  iconCategory: React.PropTypes.string,
  items: React.PropTypes.array,
  label: React.PropTypes.string,
  listLength: React.PropTypes.number,
  searchTerm: React.PropTypes.string,
  setFocus: React.PropTypes.func,
  salesforceObj: React.PropTypes.string,
};
const defaultProps = {
  emptyMessage: "No matches found.",
  errors: [],
  messages: [],
};
class Menu extends React.Component {
  constructor(props){
    super(props);
    this.state = {};
  }

  //Set filtered list length in parent to determine active indexes for aria-activedescendent
  componentDidUpdate(prevProps, prevState){
    // make an array of the children of the list but only count the actual items (ignore errors/messages)
    let list = [].slice.call(ReactDOM.findDOMNode(this.refs.list).children)
      .filter((child) => child.className.indexOf("slds-lookup__item") > -1).length;
    this.props.getListLength(list);
  }

  filter(item){
    return this.props.filterWith(this.props.searchTerm, item);
  }

  filteredItems() {
    return this.props.items.filter(this.filter, this)
  }

  //Scroll menu up/down when using mouse keys
  handleItemFocus(itemIndex, itemHeight){
    if (this.refs.list) {
      ReactDOM.findDOMNode(this.refs.list).scrollTop = itemIndex * itemHeight;
    }
  }

  renderHeader(){
    return this.props.header;
  }

  renderFooter(){
    if (this.props.footer) {
      let footerActive = false;
      let isActiveClass = null;
      if (this.props.focusIndex === this.props.listLength+1) {
        footerActive = true;
        isActiveClass = 'slds-theme--shade';
      }else{
        footerActive = false;
        isActiveClass = '';
      }
      return
{this.props.footer}
;
    }
  }

  renderErrors(){
    return this.props.errors.map((error) => {
      return (

          {error}

      );
    });
  }

  renderItems(){
    return this.filteredItems().map((c, i) => {
      //isActive means it is aria-activedescendant
      const id = c.id;
      let isActive = false;
      if (this.props.header) {
        isActive = this.props.focusIndex === i + 1 ? true : false;
      }else{
        isActive = this.props.focusIndex === i  ? true : false;
      }
      return <Item
        boldRegex={this.props.boldRegex}
        data={c.data}
        handleItemFocus={this.handleItemFocus.bind(this)}
        iconCategory={this.props.iconCategory}
        iconClasses={this.props.iconClasses}
        iconName={this.props.iconName}
        id={id}
        index={i}
        isActive={isActive}
        key={id}
        listItemLabelRenderer={this.props.listItemLabelRenderer}
        onSelect={this.props.onSelect}
        searchTerm={this.props.searchTerm}
        setFocus={this.props.setFocus}
        salesforceObj={this.props.salesforceObj}>
          {c}
      </Item>
    });
  }

  renderMessages(){
    return this.props.messages.map((message) => {
      return (

          {message}

      );
    });
  }

  renderContent(){
    if (this.props.errors.length > 0)
      return this.renderErrors()
    else if (this.filteredItems().length === 0)
      return (

          {this.props.emptyMessage}

      );

    let elements = this.renderItems();
    if (this.props.messages.length > 0) {
      elements.concat(this.renderMessages());
    }
    return elements;
  }

  render(){
    return (

        {this.renderHeader()}

          {this.renderContent()}

        {this.renderFooter()}

    );
  }
}

Menu.displayName = displayName;
Menu.propTypes = propTypes;
Menu.defaultProps = defaultProps;

module.exports = Menu;
<commit_msg>Add padding around Empty Message in Lookup<commit_after>/*
   Copyright (c) 2015, salesforce.com, inc. All rights reserved.
   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
   Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
   Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */

import React from 'react';
import ReactDOM from 'react-dom';

import Item from './Item';

const displayName = 'SLDSLookup-Menu';
const propTypes = {
  boldRegex: React.PropTypes.instanceOf(RegExp),
  emptyMessage: React.PropTypes.string,
  errors: React.PropTypes.arrayOf(React.PropTypes.string),
  filterWith: React.PropTypes.func,
  focusIndex: React.PropTypes.number,
  getListLength: React.PropTypes.func,
  iconCategory: React.PropTypes.string,
  items: React.PropTypes.array,
  label: React.PropTypes.string,
  listLength: React.PropTypes.number,
  searchTerm: React.PropTypes.string,
  setFocus: React.PropTypes.func,
  salesforceObj: React.PropTypes.string,
};
const defaultProps = {
  emptyMessage: "No matches found.",
  errors: [],
  messages: [],
};
class Menu extends React.Component {
  constructor(props){
    super(props);
    this.state = {};
  }

  //Set filtered list length in parent to determine active indexes for aria-activedescendent
  componentDidUpdate(prevProps, prevState){
    // make an array of the children of the list but only count the actual items (ignore errors/messages)
    let list = [].slice.call(ReactDOM.findDOMNode(this.refs.list).children)
      .filter((child) => child.className.indexOf("slds-lookup__item") > -1).length;
    this.props.getListLength(list);
  }

  filter(item){
    return this.props.filterWith(this.props.searchTerm, item);
  }

  filteredItems() {
    return this.props.items.filter(this.filter, this)
  }

  //Scroll menu up/down when using mouse keys
  handleItemFocus(itemIndex, itemHeight){
    if (this.refs.list) {
      ReactDOM.findDOMNode(this.refs.list).scrollTop = itemIndex * itemHeight;
    }
  }

  renderHeader(){
    return this.props.header;
  }

  renderFooter(){
    if (this.props.footer) {
      let footerActive = false;
      let isActiveClass = null;
      if (this.props.focusIndex === this.props.listLength+1) {
        footerActive = true;
        isActiveClass = 'slds-theme--shade';
      }else{
        footerActive = false;
        isActiveClass = '';
      }
      return
{this.props.footer}
;
    }
  }

  renderErrors(){
    return this.props.errors.map((error) => {
      return (

          {error}

      );
    });
  }

  renderItems(){
    return this.filteredItems().map((c, i) => {
      //isActive means it is aria-activedescendant
      const id = c.id;
      let isActive = false;
      if (this.props.header) {
        isActive = this.props.focusIndex === i + 1 ? true : false;
      }else{
        isActive = this.props.focusIndex === i  ? true : false;
      }
      return <Item
        boldRegex={this.props.boldRegex}
        data={c.data}
        handleItemFocus={this.handleItemFocus.bind(this)}
        iconCategory={this.props.iconCategory}
        iconClasses={this.props.iconClasses}
        iconName={this.props.iconName}
        id={id}
        index={i}
        isActive={isActive}
        key={id}
        listItemLabelRenderer={this.props.listItemLabelRenderer}
        onSelect={this.props.onSelect}
        searchTerm={this.props.searchTerm}
        setFocus={this.props.setFocus}
        salesforceObj={this.props.salesforceObj}>
          {c}
      </Item>
    });
  }

  renderMessages(){
    return this.props.messages.map((message) => {
      return (

          {message}

      );
    });
  }

  renderContent(){
    if (this.props.errors.length > 0)
      return this.renderErrors()
    else if (this.filteredItems().length === 0)
      return (

          {this.props.emptyMessage}

      );

    let elements = this.renderItems();
    if (this.props.messages.length > 0) {
      elements.concat(this.renderMessages());
    }
    return elements;
  }

  render(){
    return (

        {this.renderHeader()}

          {this.renderContent()}

        {this.renderFooter()}

    );
  }
}

Menu.displayName = displayName;
Menu.propTypes = propTypes;
Menu.defaultProps = defaultProps;

module.exports = Menu;
<|endoftext|>


<commit_before>/*global define*/
define([
        '../Core/DeveloperError',
        '../Core/defaultValue',
        '../Core/defined',
        '../Core/Color',
        '../Core/destroyObject',
        '../Core/Math',
        '../Core/Ellipsoid',
        '../Core/GeometryInstance',
        '../Core/PolygonGeometry',
        '../Core/PolygonPipeline',
        '../Core/Queue',
        './EllipsoidSurfaceAppearance',
        './Primitive',
        './Material'
    ], function(
        DeveloperError,
        defaultValue,
        defined,
        Color,
        destroyObject,
        CesiumMath,
        Ellipsoid,
        GeometryInstance,
        PolygonGeometry,
        PolygonPipeline,
        Queue,
        EllipsoidSurfaceAppearance,
        Primitive,
        Material) {
    "use strict";

    /**
     * A renderable polygon or hierarchy of polygons.
     *
     * @alias Polygon
     * @constructor
     *
     * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid that the extent is drawn on.
     * @param {Array} [options.positions=undefined] The cartesian positions of the polygon.
     * @param {Object} [options.polygonHierarchy=undefined] An object defining the vertex positions of each nested polygon as defined in {@link Polygon#configureFromPolygonHierarchy}.
     * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude in the underlying geometry.
     * @param {Number} [options.height=0.0] The height, in meters, that the extent is raised above the {@link ExtentPrimitive#ellipsoid}.
     * @param {Number} [options.textureRotationAngle=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise.
     * @param {Boolean} [options.show=true] Determines if this primitive will be shown.
     * @param {Material} [options.material=undefined] The surface appearance of the primitive.
     * @param {Boolean} [options.asynchronous=true] Determines if the extent will be created asynchronously or block until ready.
     *
     * @exception {DeveloperError} Either options.positions or options.polygonHierarchy can be provided, but not both.
     * @exception {DeveloperError} When options.positions is provided, at least three positions are required.
     *
     * @example
     * // Example 1
     * var polygon = new Polygon({
     *   positions : [
     *     ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *     ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *     ellipsoid.cartographicToCartesian(new Cartographic(...))
     *   ]
     * });
     *
     * // Example 2
     * var polygon = new Polygon();
     * polygon.material.uniforms.color = {
     *   red   : 1.0,
     *   green : 0.0,
     *   blue  : 0.0,
     *   alpha : 1.0
     * };
     * polygon.setPositions([
     *   ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *   ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *   ellipsoid.cartographicToCartesian(new Cartographic(...))
     * ]);
     *
     * @demo Cesium Sandcastle Polygons Demo
     */
    var Polygon = function(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        /**
         * The ellipsoid that the polygon is drawn on.
         *
         * @type Ellipsoid
         *
         * @default Ellipsoid.WGS84
         */
        this.ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
        this._ellipsoid = undefined;

        /**
         * The distance, in radians, between each latitude and longitude in the underlying geometry.
         * A lower granularity fits the curvature of the {@link Polygon#ellipsoid} better,
         * but uses more triangles.
         *
         * @type Number
         *
         * @default CesiumMath.RADIANS_PER_DEGREE
         */
        this.granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
        this._granularity = undefined;

        /**
         * The height, in meters, that the polygon is raised above the {@link Polygon#ellipsoid}.
         *
         * @type Number
         *
         * @default 0.0
         */
        this.height = defaultValue(options.height, 0.0);
        this._height = undefined;

        /**
         * The angle, in radians, relative to north that the polygon's texture is rotated.
         * Positive angles rotate counter-clockwise.
         *
         * @type Number
         *
         * @default 0.0
         */
        this.textureRotationAngle = defaultValue(options.textureRotationAngle, 0.0);
        this._textureRotationAngle = undefined;

        /**
         * Determines if this primitive will be shown.
         *
         * @type {Boolean}
         * @default true
         */
        this.show = defaultValue(options.show, true);

        var material = Material.fromType(Material.ColorType);
        material.uniforms.color = new Color(1.0, 1.0, 0.0, 0.5);

        /**
         * The surface appearance of the primitive.  This can be one of several built-in {@link Material} objects or a custom material, scripted with
         * Fabric.
         *

         * The default material is Material.ColorType.
         *

         *
         * @type {Material}
         * @default Material.fromType(Material.ColorType)
         *
         * @example
         * // 1. Change the color of the default material to yellow
         * polygon.material.uniforms.color = new Color(1.0, 1.0, 0.0, 1.0);
         *
         * // 2. Change material to horizontal stripes
         * polygon.material = Material.fromType( Material.StripeType);
         *
         * @see Fabric
         */
        this.material = defaultValue(options.material, material);

        /**
         * Determines if the geometry instances will be created and batched on
         * a web worker.
         *
         * @type Boolean
         *
         * @default true
         */
        this.asynchronous = defaultValue(options.asynchronous, true);

        this._positions = undefined;
        this._polygonHierarchy = undefined;
        this._createPrimitive = false;
        this._primitive = undefined;

        if (defined(options.positions) && defined(options.polygonHierarchy)) {
            throw new DeveloperError('Either options.positions or options.polygonHierarchy can be provided, but not both.');
        } else if (defined(options.positions)) {
            this.setPositions(options.positions);
        } else if (defined(options.polygonHierarchy)) {
            this.configureFromPolygonHierarchy(options.polygonHierarchy);
        }
    };

    /**
     * Returns the positions that define the boundary of the polygon.  If {@link Polygon#configureFromPolygonHierarchy}
     * was used, this returns undefined.
     *
     * @memberof Polygon
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see Polygon#setPositions
     */
    Polygon.prototype.getPositions = function() {
        return this._positions;
    };

    /**
     * Sets positions that define the boundary of the polygon.
     *
     * @memberof Polygon
     *
     * @exception {DeveloperError} At least three positions are required.
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see Polygon#getPositions
     *
     * @param {Array} positions The cartesian positions of the polygon.
     *
     * @example
     * polygon.setPositions([
     *   ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *   ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *   ellipsoid.cartographicToCartesian(new Cartographic(...))
     * ]);
     */
    Polygon.prototype.setPositions = function(positions) {
        // positions can be undefined
        if (defined(positions) && (positions.length < 3)) {
            throw new DeveloperError('At least three positions are required.');
        }
        this._positions = positions;
        this._polygonHierarchy = undefined;
        this._createPrimitive = true;
    };

    /**
     * Create a set of polygons with holes from a nested hierarchy.
     *
     * @memberof Polygon
     *
     * @param {Object} hierarchy An object defining the vertex positions of each nested polygon.
     * For example, the following polygon has two holes, and one hole has a hole. holes is optional.
     * Leaf nodes only have positions.
     *

     *
     * {
     *  positions : [ ... ],    // The polygon's outer boundary
     *  holes : [               // The polygon's inner holes
     *    {
     *      positions : [ ... ]
     *    },
     *    {
     *      positions : [ ... ],
     *      holes : [           // A polygon within a hole
     *       {
     *         positions : [ ... ]
     *       }
     *      ]
     *    }
     *  ]
     * }
     *
     *

     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @example
     * // A triangle within a triangle
     * var hierarchy = {
     *   positions : [
     *     new Cartesian3(-634066.5629045101, -4608738.034138676, 4348640.761750969),
     *     new Cartesian3(-1321523.0597310204, -5108871.981065817, 3570395.2500986718),
     *     new Cartesian3(46839.74837473363, -5303481.972379478, 3530933.5841716)
     *   ],
     *   holes : [{
     *     positions :[
     *       new Cartesian3(-646079.44483647, -4811233.11175887, 4123187.2266941597),
     *       new Cartesian3(-1024015.4454943262, -5072141.413164587, 3716492.6173834214),
     *       new Cartesian3(-234678.22583880965, -5189078.820849883, 3688809.059214336)
     *     ]
     *   }]
     * };
     */
    Polygon.prototype.configureFromPolygonHierarchy  = function(hierarchy) {
        this._positions = undefined;
        this._polygonHierarchy = hierarchy;
        this._createPrimitive = true;
    };

    /**
     * @private
     */
    Polygon.prototype.update = function(context, frameState, commandList) {
        if (!defined(this.ellipsoid)) {
            throw new DeveloperError('this.ellipsoid must be defined.');
        }

        if (!defined(this.material)) {
            throw new DeveloperError('this.material must be defined.');
        }

        if (this.granularity < 0.0) {
            throw new DeveloperError('this.granularity and scene2D/scene3D overrides must be greater than zero.');
        }

        if (!this.show) {
            return;
        }

        if (!this._createPrimitive && (!defined(this._primitive))) {
            // No positions/hierarchy to draw
            return;
        }

        if (this._createPrimitive ||
            (this._ellipsoid !== this.ellipsoid) ||
            (this._granularity !== this.granularity) ||
            (this._height !== this.height) ||
            (this._textureRotationAngle !== this.textureRotationAngle)) {

            this._createPrimitive = false;
            this._ellipsoid = this.ellipsoid;
            this._granularity = this.granularity;
            this._height = this.height;
            this._textureRotationAngle = this.textureRotationAngle;

            this._primitive = this._primitive && this._primitive.destroy();

            if (!defined(this._positions) && !defined(this._polygonHierarchy)) {
                return;
            }

            var instance;
            if (defined(this._positions)) {
                instance = new GeometryInstance({
                    geometry : PolygonGeometry.fromPositions({
                        positions : this._positions,
                        height : this.height,
                        vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                        stRotation : this.textureRotationAngle,
                        ellipsoid : this.ellipsoid,
                        granularity : this.granularity
                    }),
                    pickPrimitive : this
                });
            } else {
                instance = new GeometryInstance({
                    geometry : new PolygonGeometry({
                        polygonHierarchy : this._polygonHierarchy,
                        height : this.height,
                        vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                        stRotation : this.textureRotationAngle,
                        ellipsoid : this.ellipsoid,
                        granularity : this.granularity
                    }),
                    pickPrimitive : this
                });
            }

            this._primitive = new Primitive({
                geometryInstances : instance,
                appearance : new EllipsoidSurfaceAppearance({
                    aboveGround : (this.height > 0.0)
                }),
                asynchronous : this.asynchronous
            });
        }

        this._primitive.appearance.material = this.material;
        this._primitive.update(context, frameState, commandList);
    };

    /**
     * Returns true if this object was destroyed; otherwise, false.
     *

     * If this object was destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.
     *
     * @memberof Polygon
     *
     * @returns {Boolean} true if this object was destroyed; otherwise, false.
     *
     * @see Polygon#destroy
     */
    Polygon.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic
     * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
     *

     * Once an object is destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.  Therefore,
     * assign the return value (undefined) to the object as done in the example.
     *
     * @memberof Polygon
     *
     * @returns {undefined}
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see Polygon#isDestroyed
     *
     * @example
     * polygon = polygon && polygon.destroy();
     */
    Polygon.prototype.destroy = function() {
        this._primitive = this._primitive && this._primitive.destroy();
        return destroyObject(this);
    };

    return Polygon;
});
<commit_msg>Minor doc fix.<commit_after>/*global define*/
define([
        '../Core/DeveloperError',
        '../Core/defaultValue',
        '../Core/defined',
        '../Core/Color',
        '../Core/destroyObject',
        '../Core/Math',
        '../Core/Ellipsoid',
        '../Core/GeometryInstance',
        '../Core/PolygonGeometry',
        '../Core/PolygonPipeline',
        '../Core/Queue',
        './EllipsoidSurfaceAppearance',
        './Primitive',
        './Material'
    ], function(
        DeveloperError,
        defaultValue,
        defined,
        Color,
        destroyObject,
        CesiumMath,
        Ellipsoid,
        GeometryInstance,
        PolygonGeometry,
        PolygonPipeline,
        Queue,
        EllipsoidSurfaceAppearance,
        Primitive,
        Material) {
    "use strict";

    /**
     * A renderable polygon or hierarchy of polygons.
     *
     * @alias Polygon
     * @constructor
     *
     * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid that the polygon is drawn on.
     * @param {Array} [options.positions=undefined] The cartesian positions of the polygon.
     * @param {Object} [options.polygonHierarchy=undefined] An object defining the vertex positions of each nested polygon as defined in {@link Polygon#configureFromPolygonHierarchy}.
     * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude in the underlying geometry.
     * @param {Number} [options.height=0.0] The height, in meters, that the extent is raised above the {@link ExtentPrimitive#ellipsoid}.
     * @param {Number} [options.textureRotationAngle=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise.
     * @param {Boolean} [options.show=true] Determines if this primitive will be shown.
     * @param {Material} [options.material=undefined] The surface appearance of the primitive.
     * @param {Boolean} [options.asynchronous=true] Determines if the extent will be created asynchronously or block until ready.
     *
     * @exception {DeveloperError} Either options.positions or options.polygonHierarchy can be provided, but not both.
     * @exception {DeveloperError} When options.positions is provided, at least three positions are required.
     *
     * @example
     * // Example 1
     * var polygon = new Polygon({
     *   positions : [
     *     ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *     ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *     ellipsoid.cartographicToCartesian(new Cartographic(...))
     *   ]
     * });
     *
     * // Example 2
     * var polygon = new Polygon();
     * polygon.material.uniforms.color = {
     *   red   : 1.0,
     *   green : 0.0,
     *   blue  : 0.0,
     *   alpha : 1.0
     * };
     * polygon.setPositions([
     *   ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *   ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *   ellipsoid.cartographicToCartesian(new Cartographic(...))
     * ]);
     *
     * @demo Cesium Sandcastle Polygons Demo
     */
    var Polygon = function(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        /**
         * The ellipsoid that the polygon is drawn on.
         *
         * @type Ellipsoid
         *
         * @default Ellipsoid.WGS84
         */
        this.ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
        this._ellipsoid = undefined;

        /**
         * The distance, in radians, between each latitude and longitude in the underlying geometry.
         * A lower granularity fits the curvature of the {@link Polygon#ellipsoid} better,
         * but uses more triangles.
         *
         * @type Number
         *
         * @default CesiumMath.RADIANS_PER_DEGREE
         */
        this.granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
        this._granularity = undefined;

        /**
         * The height, in meters, that the polygon is raised above the {@link Polygon#ellipsoid}.
         *
         * @type Number
         *
         * @default 0.0
         */
        this.height = defaultValue(options.height, 0.0);
        this._height = undefined;

        /**
         * The angle, in radians, relative to north that the polygon's texture is rotated.
         * Positive angles rotate counter-clockwise.
         *
         * @type Number
         *
         * @default 0.0
         */
        this.textureRotationAngle = defaultValue(options.textureRotationAngle, 0.0);
        this._textureRotationAngle = undefined;

        /**
         * Determines if this primitive will be shown.
         *
         * @type {Boolean}
         * @default true
         */
        this.show = defaultValue(options.show, true);

        var material = Material.fromType(Material.ColorType);
        material.uniforms.color = new Color(1.0, 1.0, 0.0, 0.5);

        /**
         * The surface appearance of the primitive.  This can be one of several built-in {@link Material} objects or a custom material, scripted with
         * Fabric.
         *

         * The default material is Material.ColorType.
         *

         *
         * @type {Material}
         * @default Material.fromType(Material.ColorType)
         *
         * @example
         * // 1. Change the color of the default material to yellow
         * polygon.material.uniforms.color = new Color(1.0, 1.0, 0.0, 1.0);
         *
         * // 2. Change material to horizontal stripes
         * polygon.material = Material.fromType( Material.StripeType);
         *
         * @see Fabric
         */
        this.material = defaultValue(options.material, material);

        /**
         * Determines if the geometry instances will be created and batched on
         * a web worker.
         *
         * @type Boolean
         *
         * @default true
         */
        this.asynchronous = defaultValue(options.asynchronous, true);

        this._positions = undefined;
        this._polygonHierarchy = undefined;
        this._createPrimitive = false;
        this._primitive = undefined;

        if (defined(options.positions) && defined(options.polygonHierarchy)) {
            throw new DeveloperError('Either options.positions or options.polygonHierarchy can be provided, but not both.');
        } else if (defined(options.positions)) {
            this.setPositions(options.positions);
        } else if (defined(options.polygonHierarchy)) {
            this.configureFromPolygonHierarchy(options.polygonHierarchy);
        }
    };

    /**
     * Returns the positions that define the boundary of the polygon.  If {@link Polygon#configureFromPolygonHierarchy}
     * was used, this returns undefined.
     *
     * @memberof Polygon
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see Polygon#setPositions
     */
    Polygon.prototype.getPositions = function() {
        return this._positions;
    };

    /**
     * Sets positions that define the boundary of the polygon.
     *
     * @memberof Polygon
     *
     * @exception {DeveloperError} At least three positions are required.
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see Polygon#getPositions
     *
     * @param {Array} positions The cartesian positions of the polygon.
     *
     * @example
     * polygon.setPositions([
     *   ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *   ellipsoid.cartographicToCartesian(new Cartographic(...)),
     *   ellipsoid.cartographicToCartesian(new Cartographic(...))
     * ]);
     */
    Polygon.prototype.setPositions = function(positions) {
        // positions can be undefined
        if (defined(positions) && (positions.length < 3)) {
            throw new DeveloperError('At least three positions are required.');
        }
        this._positions = positions;
        this._polygonHierarchy = undefined;
        this._createPrimitive = true;
    };

    /**
     * Create a set of polygons with holes from a nested hierarchy.
     *
     * @memberof Polygon
     *
     * @param {Object} hierarchy An object defining the vertex positions of each nested polygon.
     * For example, the following polygon has two holes, and one hole has a hole. holes is optional.
     * Leaf nodes only have positions.
     *

     *
     * {
     *  positions : [ ... ],    // The polygon's outer boundary
     *  holes : [               // The polygon's inner holes
     *    {
     *      positions : [ ... ]
     *    },
     *    {
     *      positions : [ ... ],
     *      holes : [           // A polygon within a hole
     *       {
     *         positions : [ ... ]
     *       }
     *      ]
     *    }
     *  ]
     * }
     *
     *

     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @example
     * // A triangle within a triangle
     * var hierarchy = {
     *   positions : [
     *     new Cartesian3(-634066.5629045101, -4608738.034138676, 4348640.761750969),
     *     new Cartesian3(-1321523.0597310204, -5108871.981065817, 3570395.2500986718),
     *     new Cartesian3(46839.74837473363, -5303481.972379478, 3530933.5841716)
     *   ],
     *   holes : [{
     *     positions :[
     *       new Cartesian3(-646079.44483647, -4811233.11175887, 4123187.2266941597),
     *       new Cartesian3(-1024015.4454943262, -5072141.413164587, 3716492.6173834214),
     *       new Cartesian3(-234678.22583880965, -5189078.820849883, 3688809.059214336)
     *     ]
     *   }]
     * };
     */
    Polygon.prototype.configureFromPolygonHierarchy  = function(hierarchy) {
        this._positions = undefined;
        this._polygonHierarchy = hierarchy;
        this._createPrimitive = true;
    };

    /**
     * @private
     */
    Polygon.prototype.update = function(context, frameState, commandList) {
        if (!defined(this.ellipsoid)) {
            throw new DeveloperError('this.ellipsoid must be defined.');
        }

        if (!defined(this.material)) {
            throw new DeveloperError('this.material must be defined.');
        }

        if (this.granularity < 0.0) {
            throw new DeveloperError('this.granularity and scene2D/scene3D overrides must be greater than zero.');
        }

        if (!this.show) {
            return;
        }

        if (!this._createPrimitive && (!defined(this._primitive))) {
            // No positions/hierarchy to draw
            return;
        }

        if (this._createPrimitive ||
            (this._ellipsoid !== this.ellipsoid) ||
            (this._granularity !== this.granularity) ||
            (this._height !== this.height) ||
            (this._textureRotationAngle !== this.textureRotationAngle)) {

            this._createPrimitive = false;
            this._ellipsoid = this.ellipsoid;
            this._granularity = this.granularity;
            this._height = this.height;
            this._textureRotationAngle = this.textureRotationAngle;

            this._primitive = this._primitive && this._primitive.destroy();

            if (!defined(this._positions) && !defined(this._polygonHierarchy)) {
                return;
            }

            var instance;
            if (defined(this._positions)) {
                instance = new GeometryInstance({
                    geometry : PolygonGeometry.fromPositions({
                        positions : this._positions,
                        height : this.height,
                        vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                        stRotation : this.textureRotationAngle,
                        ellipsoid : this.ellipsoid,
                        granularity : this.granularity
                    }),
                    pickPrimitive : this
                });
            } else {
                instance = new GeometryInstance({
                    geometry : new PolygonGeometry({
                        polygonHierarchy : this._polygonHierarchy,
                        height : this.height,
                        vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                        stRotation : this.textureRotationAngle,
                        ellipsoid : this.ellipsoid,
                        granularity : this.granularity
                    }),
                    pickPrimitive : this
                });
            }

            this._primitive = new Primitive({
                geometryInstances : instance,
                appearance : new EllipsoidSurfaceAppearance({
                    aboveGround : (this.height > 0.0)
                }),
                asynchronous : this.asynchronous
            });
        }

        this._primitive.appearance.material = this.material;
        this._primitive.update(context, frameState, commandList);
    };

    /**
     * Returns true if this object was destroyed; otherwise, false.
     *

     * If this object was destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.
     *
     * @memberof Polygon
     *
     * @returns {Boolean} true if this object was destroyed; otherwise, false.
     *
     * @see Polygon#destroy
     */
    Polygon.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic
     * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
     *

     * Once an object is destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.  Therefore,
     * assign the return value (undefined) to the object as done in the example.
     *
     * @memberof Polygon
     *
     * @returns {undefined}
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see Polygon#isDestroyed
     *
     * @example
     * polygon = polygon && polygon.destroy();
     */
    Polygon.prototype.destroy = function() {
        this._primitive = this._primitive && this._primitive.destroy();
        return destroyObject(this);
    };

    return Polygon;
});
<|endoftext|>


<commit_before>//-------------------------------------
//-- Common
//-------------------------------------

//= require components/common/scripts/init
//= require components/common/scripts/util
<commit_msg>Il semble que requireJS aille besoin des '_' pour les includes<commit_after>//-------------------------------------
//-- Common
//-------------------------------------

//= require components/common/scripts/_init
//= require components/common/scripts/_util
<|endoftext|>


<commit_before>/*global window, document*/
/*jshint laxbreak:true*/

var Util = (function() {
	var PI2 = Math.PI * 2;

	return {
		PI2: PI2,

		singleRun: function(aFunction) {
			var started = false;

			return function() {
				if (!started) {
					started = true;
					aFunction.apply(this, arguments);
				}
			};
		},

		noop: function() {
		},

		degToRad: function(aDegrees) {
			return aDegrees * Math.PI / 180;
		},

		normalizeAngle: function(aAngle) {
			while (aAngle < 0) {
				aAngle += PI2;
			}

			return aAngle % PI2;
		},

		normalizeAngleSimple: function(aAngle) {
			if (aAngle < 0) {
				return aAngle + PI2;
			}

			return (aAngle > PI2) ? (aAngle - PI2) : aAngle;
		}
	};
})();

function FPSCounter(aUpdateInterval, aContext) {
	var count = 0;
	var fps = '...';
	var time = +new Date();
	var modifier = 1000 / aUpdateInterval;

	var Y = aContext.canvas.height - 10;
	var X = aContext.canvas.width - 50;

	this.update = function(aTime) {
		var diff = aTime - time;

		++count;
		if (diff > aUpdateInterval) {
			fps = 'FPS: ' + Math.floor(count * modifier);

			count = 0;
			time = aTime;
		}
	};

	this.draw = function() {
		aContext.fillStyle = 'red';
		aContext.font = '10px Verdana';
		aContext.fillText(fps, X, Y);
	};
}

function penta(aX) {
	return aX * aX * aX * aX * aX;
}

function Tunnel(aContext, aCenter) {
	var rays = 6;
	var offset = 0;
	var delta = Util.degToRad(2);
	var speed = Util.degToRad(0.5);
	var rayAngle = 2 * Math.PI / rays;

	var center = new Point(aContext.canvas.width / 2, aContext.canvas.height / 2);
	var outerRadius = Math.max(aContext.canvas.height, aContext.canvas.width);
	var innerRadius = outerRadius / 20;

	this.act = function() {
		offset = Util.normalizeAngleSimple(offset + speed);
	};

	this.draw = function() {
		aContext.lineWidth = 1;
		aContext.fillStyle = 'purple';

		var i;
		for (i = 0; i < rays; ++i) {
			var angle = offset + (i * rayAngle);

			var angle1Point = Point.singularCirclePoint(angle);
			var angle2Point = Point.singularCirclePoint(angle + delta);

			var outerPoint1 = angle1Point.multiply(outerRadius).translate(aCenter.x, aCenter.y);
			var outerPoint2 = angle2Point.multiply(outerRadius).translate(aCenter.x, aCenter.y);

			var innerPoint1 = angle1Point.multiply(innerRadius).translate(aCenter.x, aCenter.y);
			var innerPoint2 = angle2Point.multiply(innerRadius).translate(aCenter.x, aCenter.y);

			aContext.beginPath();

			aContext.moveTo(outerPoint1.x, outerPoint1.y);
			aContext.lineTo(innerPoint1.x, innerPoint1.y);
			aContext.lineTo(innerPoint2.x, innerPoint2.y);
			aContext.lineTo(outerPoint2.x, outerPoint2.y);

			aContext.closePath();

			aContext.fill();
		}
	};
}

function Game() {
	var rings = [];
	var canvas = document.getElementById("canvas");
	var context = canvas.getContext('2d');
	var fpsCounter = new FPSCounter(1000, context);

	var WIDTH = canvas.width;
	var HEIGHT = canvas.height;
	var center = new Point(WIDTH / 2, HEIGHT / 2);
	var tunnel = new Tunnel(context, center);
	var MAX_SIDE = Math.max(center.x, center.y);

	var RING_INITIAL_RADIUS = 10;

	var RING_MAX_ROTATION = 60;

	var INITIAL_SPEED = 60;
	var SPEED = INITIAL_SPEED;

	var INITIAL_DISTANCE = 1000;
	var ACCELERATION = 0.1;

	var ticker = (function() {
		return window.requestAnimationFrame
			|| window.webkitRequestAnimationFrame
			|| window.mozRequestAnimationFrame
			|| window.msRequestAnimationFrame
			|| window.oRequestAnimationFrame
			|| function(callback) {
			window.setTimeout(callback, 1000 / 60);
		};
	})();

	function spawnRing(aDistance) {
		//currently the rings always spawn far away in the distance
		rings.push(new Ring(aDistance));
	}

	function action(aDelta) {
		//first convert the elapsed time from milliseconds to seconds
		var delta = aDelta / 1000;

		//calculating dinstance travelled between the last and current seconds
		var distanceTravelled = SPEED * delta + (ACCELERATION * delta * delta ) / 2;
		for (var i = 0, len = rings.length; i < len; i++) {
			//the rings should calculate their new position on the z axis and if necessary do collision checks + destroy + spawn
			rings[i].act(distanceTravelled, delta);
		}

		tunnel.act();

		//now that the rings have moved according to the elapsed time we can calculate the base speed for the next frame
		SPEED += ACCELERATION * delta;
	}

	function drawBgr() {
		context.rect(0, 0, WIDTH, HEIGHT);
		context.fillStyle = '#FFFFFF';
		context.fill();
	}

	function draw() {
		drawBgr();
		tunnel.draw();

		for (var i = rings.length - 1; i >= 0; i--) {
			rings[i].draw();
		}

		drawSpeed();
		fpsCounter.draw();
	}

	function drawSpeed() {
		context.fillStyle = 'red';
		context.font = '10px Verdana';
		context.fillText('SPD: ' + SPEED.toFixed(3), WIDTH - 125, HEIGHT - 10);
	}

	function Ring(aZ) {
		// the z field holds the distance between the object and the camera (camera is at 0)
		this.z = aZ;
		this.radius = RING_INITIAL_RADIUS;
		this.center = center;
		this.angle = Math.random() * 360;
		this.angleIncrement = Math.random() * RING_MAX_ROTATION * (-1 + Math.round(Math.random()) * 2);

		switch (Math.floor(Math.random() * 8)) {
			case 0:
				this.type = new EmptyRing(this.radius, this.center, this.angle);
				break;
			case 1:
				this.type = new FanRing(this.radius, this.center, this.angle);
				break;
			case 2:
				this.type = new DoorLockRing(this.radius, this.center, this.angle);
				break;
			case 3:
				this.type = new RectangleRingSingle(this.radius, this.center, this.angle);
				break;
			case 4:
				this.type = new HalfRing(this.radius, this.center, this.angle);
				break;
			case 5:
				this.type = new HoleRing(this.radius, this.center, this.angle, 3, 0.3, 0.6);
				break;
			case 6:
				this.type = new HoleRing(this.radius, this.center, this.angle, 2, 0.3, 0.4);
				break;
			case 7:
				this.type = new HoleRing(this.radius, this.center, this.angle, 1, 0.4, 0.6);
				break;
		}

		this.draw = function() {
			this.type.draw(context);
		};

		this.act = function(aDistanceTravelled, aDelta) {
			//if the ring is beyond the camera - destroy it
			if (this.z < 0) {
				rings.shift();
				spawnRing(INITIAL_DISTANCE + this.z);
			}
			//calculate new z-position
			this.z -= aDistanceTravelled;
			this.radius = MAX_SIDE * penta((INITIAL_DISTANCE - Math.min(this.z, INITIAL_DISTANCE)) / INITIAL_DISTANCE);
			this.angle += this.angleIncrement * aDelta;
			if (this.angle >= 360) {
				this.angle -= 360;
			} else if (this.angle < 0) {
				this.angle += 360;
			}
			this.type.radius = this.radius;
			this.type.angle = this.angle;
			this.type.rescale();
		};
	}

	var lastTick = +Date.now();

	function render() {
		var now = +Date.now();

		var delta = now - lastTick;

		lastTick = now;
		fpsCounter.update(now);

		action(delta);
		draw();
	}

	var loop = function() {
		ticker(loop);
		render();
	};

	function mouseMoveListener(aEvent) {
		center.x = WIDTH - aEvent.clientX;
		center.y = HEIGHT - aEvent.clientY;
	}

	function init() {
		spawnRing(750);
		spawnRing(1000);
		spawnRing(1250);
		spawnRing(1500);
	}

	this.start = Util.singleRun(function() {
		canvas.addEventListener('mousemove', mouseMoveListener, false);

		init();
		loop();
	});

	this.destroy = function() {
		canvas.removeEventListener('mousemove', mouseMoveListener, false);
		loop = Util.noop;
	};
}

new Game().start();
<commit_msg> - more realistic tunnel/moving experience<commit_after>/*global window, document*/
/*jshint laxbreak:true*/

var Util = (function() {
	var PI2 = Math.PI * 2;

	return {
		PI2: PI2,

		singleRun: function(aFunction) {
			var started = false;

			return function() {
				if (!started) {
					started = true;
					aFunction.apply(this, arguments);
				}
			};
		},

		noop: function() {
		},

		degToRad: function(aDegrees) {
			return aDegrees * Math.PI / 180;
		},

		normalizeAngle: function(aAngle) {
			while (aAngle < 0) {
				aAngle += PI2;
			}

			return aAngle % PI2;
		},

		normalizeAngleSimple: function(aAngle) {
			if (aAngle < 0) {
				return aAngle + PI2;
			}

			return (aAngle > PI2) ? (aAngle - PI2) : aAngle;
		},

		clamp: function(aX, aMin, aMax) {
			return Math.min(aMax, Math.max(aMin, aX));
		}
	};
})();

function FPSCounter(aUpdateInterval, aContext) {
	var count = 0;
	var fps = '...';
	var time = +new Date();
	var modifier = 1000 / aUpdateInterval;

	var Y = aContext.canvas.height - 10;
	var X = aContext.canvas.width - 50;

	this.update = function(aTime) {
		var diff = aTime - time;

		++count;
		if (diff > aUpdateInterval) {
			fps = 'FPS: ' + Math.floor(count * modifier);

			count = 0;
			time = aTime;
		}
	};

	this.draw = function() {
		aContext.fillStyle = 'red';
		aContext.font = '10px Verdana';
		aContext.fillText(fps, X, Y);
	};
}

function penta(aX) {
	return aX * aX * aX * aX * aX;
}

function Tunnel(aContext, aCenter) {
	var kx;
	var ky;
	var rays = 6;
	var offset = 0;
	var delta = Util.degToRad(2);
	var speed = Util.degToRad(0.5);
	var rayAngle = 2 * Math.PI / rays;

	var center = new Point(aContext.canvas.width / 2, aContext.canvas.height / 2);
	var outerRadius = Math.max(aContext.canvas.height, aContext.canvas.width);
	var innerRadius = outerRadius / 20;

	this.act = function(aKX, aKY) {
		kx = aKX;
		ky = aKY;

		offset = Util.normalizeAngleSimple(offset + speed);
	};

	this.draw = function() {
		aContext.lineWidth = 1;
		aContext.fillStyle = 'purple';

		var i;
		for (i = 0; i < rays; ++i) {
			var angle = offset + (i * rayAngle);

			var angle1Point = Point.singularCirclePoint(angle);
			var angle2Point = Point.singularCirclePoint(angle + delta);

			var outerPoint1 = angle1Point.multiply(outerRadius).translate(aCenter.x + outerRadius * kx, aCenter.y + outerRadius * ky);
			var outerPoint2 = angle2Point.multiply(outerRadius).translate(aCenter.x + outerRadius * kx, aCenter.y + outerRadius * ky);

			var innerPoint1 = angle1Point.multiply(innerRadius).translate(aCenter.x + innerRadius * kx, aCenter.y + innerRadius * ky);
			var innerPoint2 = angle2Point.multiply(innerRadius).translate(aCenter.x + innerRadius * kx, aCenter.y + innerRadius * ky);

			aContext.beginPath();

			aContext.moveTo(outerPoint1.x, outerPoint1.y);
			aContext.lineTo(innerPoint1.x, innerPoint1.y);
			aContext.lineTo(innerPoint2.x, innerPoint2.y);
			aContext.lineTo(outerPoint2.x, outerPoint2.y);

			aContext.closePath();

			aContext.fill();
		}
	};
}

function Game() {
	var rings = [];
	var canvas = document.getElementById("canvas");
	var context = canvas.getContext('2d');
	var fpsCounter = new FPSCounter(1000, context);

	var WIDTH = canvas.width;
	var HEIGHT = canvas.height;
	var center = new Point(WIDTH / 2, HEIGHT / 2);
	var mousePosition = center.translate();
	var tunnel = new Tunnel(context, center);
	var MAX_SIDE = Math.max(center.x, center.y);

	var RING_INITIAL_RADIUS = 10;

	var RING_MAX_ROTATION = 60;

	var INITIAL_SPEED = 60;
	var SPEED = INITIAL_SPEED;

	var INITIAL_DISTANCE = 1000;
	var ACCELERATION = 5;

	var ticker = (function() {
		return window.requestAnimationFrame
			|| window.webkitRequestAnimationFrame
			|| window.mozRequestAnimationFrame
			|| window.msRequestAnimationFrame
			|| window.oRequestAnimationFrame
			|| function(callback) {
			window.setTimeout(callback, 1000 / 60);
		};
	})();

	function spawnRing(aDistance) {
		//currently the rings always spawn far away in the distance
		rings.push(new Ring(aDistance));
	}

	function action(aDelta) {
		//first convert the elapsed time from milliseconds to seconds
		var delta = aDelta / 1000;

		//calculating dinstance travelled between the last and current seconds
		var distanceTravelled = SPEED * delta + (ACCELERATION * delta * delta ) / 2;

		var w = WIDTH / 2;
		var kx = Util.clamp((w - mousePosition.x) / w, -0.7, 0.7);

		var h = HEIGHT / 2;
		var ky = Util.clamp((h - mousePosition.y) / h, -0.7, 0.7);

		console.log('kx=', kx, ', ky=', ky);

		for (var i = 0, len = rings.length; i < len; i++) {
			//the rings should calculate their new position on the z axis and if necessary do collision checks + destroy + spawn
			rings[i].act(distanceTravelled, delta, kx, ky);
		}

		tunnel.act(kx, ky);

		//now that the rings have moved according to the elapsed time we can calculate the base speed for the next frame
		SPEED += ACCELERATION * delta;
	}

	function drawBgr() {
		context.rect(0, 0, WIDTH, HEIGHT);
		context.fillStyle = '#FFFFFF';
		context.fill();
	}

	function draw() {
		drawBgr();
		tunnel.draw();

		for (var i = rings.length - 1; i >= 0; i--) {
			rings[i].draw();
		}

		drawSpeed();
		fpsCounter.draw();
	}

	function drawSpeed() {
		context.fillStyle = 'red';
		context.font = '10px Verdana';
		context.fillText('SPD: ' + SPEED.toFixed(3), WIDTH - 125, HEIGHT - 10);
	}

	function Ring(aZ) {
		// the z field holds the distance between the object and the camera (camera is at 0)
		this.z = aZ;
		this.radius = RING_INITIAL_RADIUS;
		this.center = center;
		this.angle = Math.random() * 360;
		this.angleIncrement = Math.random() * RING_MAX_ROTATION * (-1 + Math.round(Math.random()) * 2);

		switch (Math.floor(Math.random() * 8)) {
			case 0:
				this.type = new EmptyRing(this.radius, this.center, this.angle);
				break;
			case 1:
				this.type = new FanRing(this.radius, this.center, this.angle);
				break;
			case 2:
				this.type = new DoorLockRing(this.radius, this.center, this.angle);
				break;
			case 3:
				this.type = new RectangleRingSingle(this.radius, this.center, this.angle);
				break;
			case 4:
				this.type = new HalfRing(this.radius, this.center, this.angle);
				break;
			case 5:
				this.type = new HoleRing(this.radius, this.center, this.angle, 3, 0.3, 0.6);
				break;
			case 6:
				this.type = new HoleRing(this.radius, this.center, this.angle, 2, 0.3, 0.4);
				break;
			case 7:
				this.type = new HoleRing(this.radius, this.center, this.angle, 1, 0.4, 0.6);
				break;
		}

		this.draw = function() {
			this.type.draw(context);
		};

		this.act = function(aDistanceTravelled, aDelta, aKX, aKY) {
			//if the ring is beyond the camera - destroy it
			if (this.z < 0) {
				rings.shift();
				spawnRing(INITIAL_DISTANCE + this.z);
			}

			//calculate new z-position
			this.z -= aDistanceTravelled;
			this.radius = MAX_SIDE * penta((INITIAL_DISTANCE - Math.min(this.z, INITIAL_DISTANCE)) / INITIAL_DISTANCE);
			this.angle += this.angleIncrement * aDelta;

			if (this.angle >= 360) {
				this.angle -= 360;
			} else if (this.angle < 0) {
				this.angle += 360;
			}

			this.type.radius = this.radius;
			this.type.angle = this.angle;

			this.type.center = center.translate(aKX * this.type.radius, aKY * this.type.radius);
			this.type.rescale();
		};
	}

	var lastTick = +Date.now();
	function render() {
		var now = +Date.now();

		var delta = now - lastTick;

		lastTick = now;
		fpsCounter.update(now);

		action(delta);
		draw();
	}

	var loop = function() {
		ticker(loop);
		render();
	};

	function mouseMoveListener(aEvent) {
		mousePosition.x = aEvent.clientX;
		mousePosition.y = aEvent.clientY;
	}

	function init() {
		spawnRing(750);
		spawnRing(1000);
		spawnRing(1250);
		spawnRing(1500);
	}

	this.start = Util.singleRun(function() {
		canvas.addEventListener('mousemove', mouseMoveListener, false);

		init();
		loop();
	});

	this.destroy = function() {
		canvas.removeEventListener('mousemove', mouseMoveListener, false);
		loop = Util.noop;
	};
}

new Game().start();
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import DayPicker from 'react-day-picker';
import NavigationBar from './NavigationBar';
import WeekDay from './WeekDay';
import { convertModifiersToClassnames } from './utils';
import cx from 'classnames';
import theme from './theme.css';

class DatePicker extends PureComponent {
  state = {
    selectedDate: null,
  };

  static getDerivedStateFromProps(props, state) {
    if(props.selectedDate !== undefined && props.selectedDate !== state.selectedDate){
      return {
        selectedDate: props.selectedDate,
      };
    }

    return null;
  }

  handleDayClick = day => {
    this.setState(
      {
        selectedDate: day,
      },
      () => this.props.onChange(day),
    );
  };

  render() {
    const { className, modifiers, size, ...others } = this.props;
    const { selectedDate } = this.state;

    const classNames = cx(theme['date-picker'], theme[`is-${size}`], className);

    return (
      <DayPicker
        {...others}
        className={classNames}
        classNames={theme}
        modifiers={convertModifiersToClassnames(modifiers, theme)}
        navbarElement={<NavigationBar size={size} />}
        onDayClick={this.handleDayClick}
        selectedDays={selectedDate}
        weekdayElement={<WeekDay size={size} />}
      />
    );
  }
}

DatePicker.propTypes = {
  className: PropTypes.string,
  modifiers: PropTypes.object,
  onChange: PropTypes.func,
  selectedDate: PropTypes.instanceOf(Date),
  size: PropTypes.oneOf(['small', 'medium', 'large']),
};

DatePicker.defaultProps = {
  size: 'medium',
};

export default DatePicker;
<commit_msg>Prettier Linting™<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import DayPicker from 'react-day-picker';
import NavigationBar from './NavigationBar';
import WeekDay from './WeekDay';
import { convertModifiersToClassnames } from './utils';
import cx from 'classnames';
import theme from './theme.css';

class DatePicker extends PureComponent {
  state = {
    selectedDate: null,
  };

  static getDerivedStateFromProps(props, state) {
    if (props.selectedDate !== undefined && props.selectedDate !== state.selectedDate) {
      return {
        selectedDate: props.selectedDate,
      };
    }

    return null;
  }

  handleDayClick = day => {
    this.setState(
      {
        selectedDate: day,
      },
      () => this.props.onChange(day),
    );
  };

  render() {
    const { className, modifiers, size, ...others } = this.props;
    const { selectedDate } = this.state;

    const classNames = cx(theme['date-picker'], theme[`is-${size}`], className);

    return (
      <DayPicker
        {...others}
        className={classNames}
        classNames={theme}
        modifiers={convertModifiersToClassnames(modifiers, theme)}
        navbarElement={<NavigationBar size={size} />}
        onDayClick={this.handleDayClick}
        selectedDays={selectedDate}
        weekdayElement={<WeekDay size={size} />}
      />
    );
  }
}

DatePicker.propTypes = {
  className: PropTypes.string,
  modifiers: PropTypes.object,
  onChange: PropTypes.func,
  selectedDate: PropTypes.instanceOf(Date),
  size: PropTypes.oneOf(['small', 'medium', 'large']),
};

DatePicker.defaultProps = {
  size: 'medium',
};

export default DatePicker;
<|endoftext|>


<commit_before>"use strict";

treeherder.provider('thServiceDomain', function() {
    this.$get = function() {
        // The SERVICE_DOMAIN global is set by webpack's DefinePlugin.
        return (typeof SERVICE_DOMAIN !== 'undefined') ? SERVICE_DOMAIN : "";
    };
});

treeherder.provider('thResultStatusList', function() {
    var all = function() {
        return ['success', 'testfailed', 'busted', 'exception', 'retry', 'usercancel', 'running', 'pending', 'coalesced', 'runnable'];
    };

    var defaultFilters = function() {
        return ['success', 'testfailed', 'busted', 'exception', 'retry', 'usercancel', 'running', 'pending', 'runnable'];
    };

    this.$get = function() {
        return {
            all: all,
            defaultFilters: defaultFilters
        };
    };
});

treeherder.provider('thResultStatus', function() {
    this.$get = function() {
        return function(job) {
            if (job.state === "completed") {
                return job.result;
            }
            // Coalesced jobs are marked as pending by the API, this will be fixed by bug 1132546.
            if (job.job_coalesced_to_guid !== null) {
                return 'coalesced';
            }
            return job.state;
        };
    };
});

treeherder.provider('thResultStatusObject', function() {
    var getResultStatusObject = function(){
        return {
            'running':0,
            'pending':0,
            'completed':0
        };
    };

    this.$get = function() {
        return {
            getResultStatusObject:getResultStatusObject
        };
    };
});

treeherder.provider('thResultStatusInfo', function() {
    this.$get = function() {
        return function(resultState, failure_classification_id) {
            // default if there is no match, used for pending
            var resultStatusInfo = {
                btnClass: "btn-default"
            };

            switch (resultState) {
                case "busted":
                case "failures":
                    resultStatusInfo = {
                        btnClass: "btn-red",
                        countText: "busted"
                    };
                    break;
                case "exception":
                    resultStatusInfo = {
                        btnClass: "btn-purple",
                        countText: "exception"
                    };
                    break;
                case "testfailed":
                    resultStatusInfo = {
                        btnClass: "btn-orange",
                        countText: "failed"
                    };
                    break;
                case "unknown":
                    resultStatusInfo = {
                        btnClass: "btn-yellow",
                        countText: "unknown"
                    };
                    break;
                case "usercancel":
                    resultStatusInfo = {
                        btnClass: "btn-pink",
                        countText: "cancel"
                    };
                    break;
                case "retry":
                    resultStatusInfo = {
                        btnClass: "btn-dkblue",
                        countText: "retry"
                    };
                    break;
                case "success":
                    resultStatusInfo = {
                        btnClass: "btn-green",
                        countText: "success"
                    };
                    break;
                case "running":
                case "in progress":
                    resultStatusInfo = {
                        btnClass: "btn-dkgray",
                        countText: "running"
                    };
                    break;
                case "pending":
                    resultStatusInfo = {
                        btnClass: "btn-ltgray",
                        countText: "pending"
                    };
                    break;
                case "coalesced":
                    resultStatusInfo = {
                        btnClass: "btn-ltblue",
                        countText: "coalesced"
                    };
                    break;
            }

            // handle if a job is classified
            var classificationId = parseInt(failure_classification_id, 10);
            if (classificationId > 1) {
                resultStatusInfo.btnClass = resultStatusInfo.btnClass + "-classified";
                // autoclassification-only case
                if (classificationId === 7) {
                    resultStatusInfo.btnClass += " autoclassified";
                }
                resultStatusInfo.countText = "classified " + resultStatusInfo.countText;
            }
            return resultStatusInfo;
        };

    };
});

/**
 * The set of custom Treeherder events.
 *
 * These are/can be used via $rootScope.$emit.
 */
treeherder.provider('thEvents', function() {
    this.$get = function() {
        return {

            // fired when a list of revisions has been loaded by button-click
            revisionsLoaded: "revisions-loaded-EVT",

            // fired (surprisingly) when a job is clicked
            jobClick: "job-click-EVT",

            // fired when the job details are loaded
            jobDetailLoaded: "job-detail-loaded-EVT",

            // fired with a selected job on 't'
            selectNextTab: "select-next-tab-EVT",

            // fired with a selected job on spacebar
            jobPin: "job-pin-EVT",

            // fired with a selected job on ctrl/cmd-click
            toggleJobPin: "job-togglepin-EVT",

            // fired with api call to increment the pinned jobs
            pulsePinCount: "pulse-pin-count-EVT",

            // fired with a selected job on 'r'
            jobRetrigger: "job-retrigger-EVT",

            // fired when the user middle-clicks on a job to view the log
            jobContextMenu: "job-context-menu-EVT",

            // fired when jobs are classified locally
            jobsClassified: "jobs-classified-EVT",

            // fired when bugs are associated to jobs locally
            bugsAssociated: "bugs-associated-EVT",

            // after loading a group of jobs
            jobsLoaded: "jobs-loaded-EVT",

            // after deselecting a job via click outside/esc
            clearSelectedJob: "clear-selected-job-EVT",

            // fired when a global filter has changed
            globalFilterChanged: "status-filter-changed-EVT",

            // after something happened that requires the number
            // of unclassified jobs by tier to be recalculated
            recalculateUnclassified: "recalc-unclassified-EVT",

            groupStateChanged: "group-state-changed-EVT",

            duplicateJobsVisibilityChanged: "duplicate-jobs-visibility-changed-EVT",

            toggleRevisions: "toggle-revisions-EVT",

            showRunnableJobs: "show-runnable-jobs-EVT",

            deleteRunnableJobs: "delete-runnable-jobs-EVT",

            toggleAllRevisions: "toggle-all-revisions-EVT",

            toggleUnclassifiedFailures: "toggle-unclassified-failures-EVT",

            changeSelection: "next-previous-job-EVT",

            addRelatedBug: "add-related-bug-EVT",

            saveClassification: "save-classification-EVT",

            deleteClassification: "delete-classification-EVT",

            clearPinboard: "clear-pinboard-EVT",

            searchPage: "search-page-EVT",

            selectJob: "select-job-EVT",

            mapResultSetJobs: "map-result-set-jobs-EVT",

            applyNewJobs: "apply-new-jobs-EVT",

            openLogviewer: "open-logviewer-EVT",

            autoclassifyVerified: "ac-verified-EVT",

            autoclassifySaveAll: "ac-save-all-EVT",

            autoclassifySave: "ac-save-EVT",

            autoclassifyIgnore: "ac-ignore-EVT",

            autoclassifyOther: "ac-other-EVT",

            autoclassifySelectOption: "ac-select-EVT",

            autoclassifyChangeSelection: "ac-change-selection-EVT",

            autoclassifyToggleExpandOptions: "ac-toggle-expand-options-EVT",

            autoclassifyToggleEdit: "ac-toggle-edit-EVT"

        };
    };
});

treeherder.provider('thAggregateIds', function() {

    var escape = function(id) {
        return id.replace(/(:|\[|\]|\?|,|\.|\s+)/g, '-');
    };

    var getPlatformRowId = function(
        repoName, resultsetId, platformName, platformOptions) {
        // ensure there are no invalid characters in the id (like spaces, etc)
        return escape(repoName +
                      resultsetId +
                      platformName +
                      platformOptions);
    };

    var getResultsetTableId = function(repoName, resultsetId, revision){
        return escape(repoName + resultsetId + revision);
    };

    var getGroupMapKey = function(result_set_id, grSymbol, grTier, plName, plOpt) {
        //Build string key for groupMap entries
        return escape(result_set_id + grSymbol + grTier + plName + plOpt);
    };

    var getJobMapKey = function(job) {
        //Build string key for jobMap entries
        return 'key' + job.id;
    };

    this.$get = function() {
        return {
            getPlatformRowId:getPlatformRowId,
            getResultsetTableId:getResultsetTableId,
            getJobMapKey: getJobMapKey,
            getGroupMapKey: getGroupMapKey,
            escape: escape
        };
    };
});

treeherder.provider('thReftestStatus', function() {
    this.$get = function() {
        return function(job) {
            if (job.job_group_name) {
                return (job.job_group_name.toLowerCase().indexOf('reftest') !== -1);
            }
        };
    };
});
<commit_msg>Bug 1362812 - Consider wpt reftests to be reftests.<commit_after>"use strict";

treeherder.provider('thServiceDomain', function() {
    this.$get = function() {
        // The SERVICE_DOMAIN global is set by webpack's DefinePlugin.
        return (typeof SERVICE_DOMAIN !== 'undefined') ? SERVICE_DOMAIN : "";
    };
});

treeherder.provider('thResultStatusList', function() {
    var all = function() {
        return ['success', 'testfailed', 'busted', 'exception', 'retry', 'usercancel', 'running', 'pending', 'coalesced', 'runnable'];
    };

    var defaultFilters = function() {
        return ['success', 'testfailed', 'busted', 'exception', 'retry', 'usercancel', 'running', 'pending', 'runnable'];
    };

    this.$get = function() {
        return {
            all: all,
            defaultFilters: defaultFilters
        };
    };
});

treeherder.provider('thResultStatus', function() {
    this.$get = function() {
        return function(job) {
            if (job.state === "completed") {
                return job.result;
            }
            // Coalesced jobs are marked as pending by the API, this will be fixed by bug 1132546.
            if (job.job_coalesced_to_guid !== null) {
                return 'coalesced';
            }
            return job.state;
        };
    };
});

treeherder.provider('thResultStatusObject', function() {
    var getResultStatusObject = function(){
        return {
            'running':0,
            'pending':0,
            'completed':0
        };
    };

    this.$get = function() {
        return {
            getResultStatusObject:getResultStatusObject
        };
    };
});

treeherder.provider('thResultStatusInfo', function() {
    this.$get = function() {
        return function(resultState, failure_classification_id) {
            // default if there is no match, used for pending
            var resultStatusInfo = {
                btnClass: "btn-default"
            };

            switch (resultState) {
                case "busted":
                case "failures":
                    resultStatusInfo = {
                        btnClass: "btn-red",
                        countText: "busted"
                    };
                    break;
                case "exception":
                    resultStatusInfo = {
                        btnClass: "btn-purple",
                        countText: "exception"
                    };
                    break;
                case "testfailed":
                    resultStatusInfo = {
                        btnClass: "btn-orange",
                        countText: "failed"
                    };
                    break;
                case "unknown":
                    resultStatusInfo = {
                        btnClass: "btn-yellow",
                        countText: "unknown"
                    };
                    break;
                case "usercancel":
                    resultStatusInfo = {
                        btnClass: "btn-pink",
                        countText: "cancel"
                    };
                    break;
                case "retry":
                    resultStatusInfo = {
                        btnClass: "btn-dkblue",
                        countText: "retry"
                    };
                    break;
                case "success":
                    resultStatusInfo = {
                        btnClass: "btn-green",
                        countText: "success"
                    };
                    break;
                case "running":
                case "in progress":
                    resultStatusInfo = {
                        btnClass: "btn-dkgray",
                        countText: "running"
                    };
                    break;
                case "pending":
                    resultStatusInfo = {
                        btnClass: "btn-ltgray",
                        countText: "pending"
                    };
                    break;
                case "coalesced":
                    resultStatusInfo = {
                        btnClass: "btn-ltblue",
                        countText: "coalesced"
                    };
                    break;
            }

            // handle if a job is classified
            var classificationId = parseInt(failure_classification_id, 10);
            if (classificationId > 1) {
                resultStatusInfo.btnClass = resultStatusInfo.btnClass + "-classified";
                // autoclassification-only case
                if (classificationId === 7) {
                    resultStatusInfo.btnClass += " autoclassified";
                }
                resultStatusInfo.countText = "classified " + resultStatusInfo.countText;
            }
            return resultStatusInfo;
        };

    };
});

/**
 * The set of custom Treeherder events.
 *
 * These are/can be used via $rootScope.$emit.
 */
treeherder.provider('thEvents', function() {
    this.$get = function() {
        return {

            // fired when a list of revisions has been loaded by button-click
            revisionsLoaded: "revisions-loaded-EVT",

            // fired (surprisingly) when a job is clicked
            jobClick: "job-click-EVT",

            // fired when the job details are loaded
            jobDetailLoaded: "job-detail-loaded-EVT",

            // fired with a selected job on 't'
            selectNextTab: "select-next-tab-EVT",

            // fired with a selected job on spacebar
            jobPin: "job-pin-EVT",

            // fired with a selected job on ctrl/cmd-click
            toggleJobPin: "job-togglepin-EVT",

            // fired with api call to increment the pinned jobs
            pulsePinCount: "pulse-pin-count-EVT",

            // fired with a selected job on 'r'
            jobRetrigger: "job-retrigger-EVT",

            // fired when the user middle-clicks on a job to view the log
            jobContextMenu: "job-context-menu-EVT",

            // fired when jobs are classified locally
            jobsClassified: "jobs-classified-EVT",

            // fired when bugs are associated to jobs locally
            bugsAssociated: "bugs-associated-EVT",

            // after loading a group of jobs
            jobsLoaded: "jobs-loaded-EVT",

            // after deselecting a job via click outside/esc
            clearSelectedJob: "clear-selected-job-EVT",

            // fired when a global filter has changed
            globalFilterChanged: "status-filter-changed-EVT",

            // after something happened that requires the number
            // of unclassified jobs by tier to be recalculated
            recalculateUnclassified: "recalc-unclassified-EVT",

            groupStateChanged: "group-state-changed-EVT",

            duplicateJobsVisibilityChanged: "duplicate-jobs-visibility-changed-EVT",

            toggleRevisions: "toggle-revisions-EVT",

            showRunnableJobs: "show-runnable-jobs-EVT",

            deleteRunnableJobs: "delete-runnable-jobs-EVT",

            toggleAllRevisions: "toggle-all-revisions-EVT",

            toggleUnclassifiedFailures: "toggle-unclassified-failures-EVT",

            changeSelection: "next-previous-job-EVT",

            addRelatedBug: "add-related-bug-EVT",

            saveClassification: "save-classification-EVT",

            deleteClassification: "delete-classification-EVT",

            clearPinboard: "clear-pinboard-EVT",

            searchPage: "search-page-EVT",

            selectJob: "select-job-EVT",

            mapResultSetJobs: "map-result-set-jobs-EVT",

            applyNewJobs: "apply-new-jobs-EVT",

            openLogviewer: "open-logviewer-EVT",

            autoclassifyVerified: "ac-verified-EVT",

            autoclassifySaveAll: "ac-save-all-EVT",

            autoclassifySave: "ac-save-EVT",

            autoclassifyIgnore: "ac-ignore-EVT",

            autoclassifyOther: "ac-other-EVT",

            autoclassifySelectOption: "ac-select-EVT",

            autoclassifyChangeSelection: "ac-change-selection-EVT",

            autoclassifyToggleExpandOptions: "ac-toggle-expand-options-EVT",

            autoclassifyToggleEdit: "ac-toggle-edit-EVT"

        };
    };
});

treeherder.provider('thAggregateIds', function() {

    var escape = function(id) {
        return id.replace(/(:|\[|\]|\?|,|\.|\s+)/g, '-');
    };

    var getPlatformRowId = function(
        repoName, resultsetId, platformName, platformOptions) {
        // ensure there are no invalid characters in the id (like spaces, etc)
        return escape(repoName +
                      resultsetId +
                      platformName +
                      platformOptions);
    };

    var getResultsetTableId = function(repoName, resultsetId, revision){
        return escape(repoName + resultsetId + revision);
    };

    var getGroupMapKey = function(result_set_id, grSymbol, grTier, plName, plOpt) {
        //Build string key for groupMap entries
        return escape(result_set_id + grSymbol + grTier + plName + plOpt);
    };

    var getJobMapKey = function(job) {
        //Build string key for jobMap entries
        return 'key' + job.id;
    };

    this.$get = function() {
        return {
            getPlatformRowId:getPlatformRowId,
            getResultsetTableId:getResultsetTableId,
            getJobMapKey: getJobMapKey,
            getGroupMapKey: getGroupMapKey,
            escape: escape
        };
    };
});

treeherder.provider('thReftestStatus', function() {
    this.$get = function() {
        return function(job) {
            if (job.job_group_name) {
                return (job.job_group_name.toLowerCase().indexOf('reftest') !== -1 ||
                        job.job_type_name.toLowerCase().indexOf('reftest') !== -1);
            }
        };
    };
});
<|endoftext|>