<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Error from '../components/Error';
import withIntl from '../lib/withIntl';
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
import Member from './Member';
import { ButtonGroup, Button } from 'react-bootstrap';
import { FormattedMessage } from 'react-intl';
import { uniqBy } from 'lodash';

const MEMBERS_PER_PAGE = 10;

class MembersWithData extends React.Component {

  static propTypes = {
    collective: PropTypes.object,
    tier: PropTypes.object,
    limit: PropTypes.number,
    onChange: PropTypes.func,
    LoggedInUser: PropTypes.object,
    fetchMore: PropTypes.func.isRequired,
    refetch: PropTypes.func.isRequired,
    className: PropTypes.string,
    data: PropTypes.object,
    role: PropTypes.string,
    type: PropTypes.string,
  }

  constructor(props) {
    super(props);
    this.state = {
      role: null,
      loading: false
    };
  }

  componentDidMount() {
    this.onChange();
  }

  onChange = () => {
    const { onChange } = this.props;
    onChange && this.node && onChange({ height: this.node.offsetHeight });
  }

  fetchMore = (e) => {
    e.target.blur();
    this.setState({ loading: true });
    this.props.fetchMore().then(() => {
      this.setState({ loading: false });
      this.onChange();
    });
  }

  refetch = (role) => {
    this.setState({role});
    this.props.refetch({role});
  }

  render() {
    const { data, LoggedInUser, collective, tier, role, type } = this.props;

    if (data.error) {
      console.error("graphql error>>>", data.error.message);
      return (<Error message="GraphQL error" />)
    }
    if (!data.allMembers) {
      return (
);
    }
    let members = [...data.allMembers];
    if (members.length === 0) {
      return (
)
    }

    // need to sort by totalDonations and then date
    // browsers (chrome vs. firefox) appear to handle "0" return cases differently
    members.sort((a, b) => {
      if (b.stats.totalDonations !== a.stats.totalDonations) {
        return b.stats.totalDonations - a.stats.totalDonations;
      } else {
        const aDate = new Date(a.createdAt);
        const bDate = new Date(b.createdAt);
        return bDate - aDate;
      }
    });

    // Make sure we display unique members
    // that should ultimately be addressed on the API side
    members = uniqBy(members, member => member.member.id);

    const size = members.length > 50 ? "small" : "large";
    let viewMode = (type && type.split(',')[0]) || "USER";
    if (tier && tier.name.match(/sponsor/i)) {
      viewMode = "ORGANIZATION";
    }
    const limit = this.props.limit || MEMBERS_PER_PAGE * 2;
    return (

 this.node = node}>

        { !role && !tier &&

            <ButtonGroup className="filterBtnGroup">
              <Button className="filterBtn" bsStyle={!this.state.role ? 'primary' : 'default'} onClick={() => this.refetch()}>
                <FormattedMessage id='members.all' defaultMessage='all' />
              </Button>
              <Button className="filterBtn" bsStyle={this.state.role === 'ADMIN' ? 'primary' : 'default'} onClick={() => this.refetch('ADMIN')}>
                <FormattedMessage id='members.admin' defaultMessage='administrators' />
              </Button>
              <Button className="filterBtn" bsStyle={this.state.role === 'MEMBER' ? 'primary' : 'default'} onClick={() => this.refetch('MEMBER')}>
                <FormattedMessage id='members.members' defaultMessage='members' />
              </Button>
              <Button className="filterBtn" bsStyle={this.state.role === 'BACKER' ? 'primary' : 'default'} onClick={() => this.refetch('BACKER')}>
                <FormattedMessage id='members.paid' defaultMessage='backers' />
              </Button>
            </ButtonGroup>

        }

          {members.map((member) =>
            (<Member
              key={member.id}
              member={member}
              className={classNames(this.props.className, size)}
              collective={collective}
              viewMode={viewMode}
              LoggedInUser={LoggedInUser}
             />)
          )}

        { members.length % 10 === 0 && members.length >= limit &&

            <Button bsStyle='default' onClick={this.fetchMore}>
              {this.state.loading && <FormattedMessage id='loading' defaultMessage='loading' />}
              {!this.state.loading && <FormattedMessage id='loadMore' defaultMessage='load more' />}
            </Button>

        }

    );
  }

}

const getMembersQuery = gql`
query Members($CollectiveId: Int!, $TierId: Int, $role: String, $type: String, $limit: Int, $offset: Int, $orderBy: String) {
  allMembers(CollectiveId: $CollectiveId, TierId: $TierId, role: $role, type: $type, limit: $limit, offset: $offset, orderBy: $orderBy) {
    id
    role
    createdAt
    stats {
      totalDonations
    }
    tier {
      id
      name
    }
    member {
      id
      type
      name
      company
      description
      slug
      image
      backgroundImage
      website
    }
  }
}
`;

export const addMembersData = graphql(getMembersQuery, {
  options(props) {
    return {
      variables: {
        CollectiveId: props.collective.id,
        TierId: props.tier && props.tier.id,
        offset: 0,
        type: props.type,
        role: props.role,
        orderBy: props.orderBy,
        limit: props.limit || MEMBERS_PER_PAGE * 2
      }
    }
  },
  props: ({ data }) => ({
    data,
    fetchMore: () => {
      return data.fetchMore({
        variables: {
          offset: data.allMembers.length,
          limit: MEMBERS_PER_PAGE
        },
        updateQuery: (previousResult, { fetchMoreResult }) => {
          if (!fetchMoreResult) {
            return previousResult
          }
          return Object.assign({}, previousResult, {
            // Append the new posts results to the old one
            allMembers: [...previousResult.allMembers, ...fetchMoreResult.allMembers]
          })
        }
      })
    }
  })
});

export default addMembersData(withIntl(MembersWithData));
<commit_msg>ignore FUNDRAISER in backers<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Error from '../components/Error';
import withIntl from '../lib/withIntl';
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
import Member from './Member';
import { ButtonGroup, Button } from 'react-bootstrap';
import { FormattedMessage } from 'react-intl';
import { uniqBy } from 'lodash';

const MEMBERS_PER_PAGE = 10;

class MembersWithData extends React.Component {

  static propTypes = {
    collective: PropTypes.object,
    tier: PropTypes.object,
    limit: PropTypes.number,
    onChange: PropTypes.func,
    LoggedInUser: PropTypes.object,
    fetchMore: PropTypes.func.isRequired,
    refetch: PropTypes.func,
    className: PropTypes.string,
    data: PropTypes.object,
    role: PropTypes.string,
    type: PropTypes.string,
  }

  constructor(props) {
    super(props);
    this.state = {
      role: null,
      loading: false
    };
  }

  componentDidMount() {
    this.onChange();
  }

  onChange = () => {
    const { onChange } = this.props;
    onChange && this.node && onChange({ height: this.node.offsetHeight });
  }

  fetchMore = (e) => {
    e.target.blur();
    this.setState({ loading: true });
    this.props.fetchMore().then(() => {
      this.setState({ loading: false });
      this.onChange();
    });
  }

  refetch = (role) => {
    this.setState({role});
    this.props.refetch({role});
  }

  render() {
    const { data, LoggedInUser, collective, tier, role, type } = this.props;

    if (data.error) {
      console.error("graphql error>>>", data.error.message);
      return (<Error message="GraphQL error" />)
    }
    if (!data.allMembers) {
      return (
);
    }
    let members = [...data.allMembers];
    if (members.length === 0) {
      return (
)
    }

    // need to sort by totalDonations and then date
    // browsers (chrome vs. firefox) appear to handle "0" return cases differently
    members.sort((a, b) => {
      if (b.stats.totalDonations !== a.stats.totalDonations) {
        return b.stats.totalDonations - a.stats.totalDonations;
      } else {
        const aDate = new Date(a.createdAt);
        const bDate = new Date(b.createdAt);
        return bDate - aDate;
      }
    });

    // Make sure we display unique members
    // that should ultimately be addressed on the API side
    members = members.filter(member => member.role !== 'FUNDRAISER');
    members = uniqBy(members, member => member.member.id);

    const size = members.length > 50 ? "small" : "large";
    let viewMode = (type && type.split(',')[0]) || "USER";
    if (tier && tier.name.match(/sponsor/i)) {
      viewMode = "ORGANIZATION";
    }
    const limit = this.props.limit || MEMBERS_PER_PAGE * 2;
    return (

 this.node = node}>

        { !role && !tier &&

            <ButtonGroup className="filterBtnGroup">
              <Button className="filterBtn" bsStyle={!this.state.role ? 'primary' : 'default'} onClick={() => this.refetch()}>
                <FormattedMessage id='members.all' defaultMessage='all' />
              </Button>
              <Button className="filterBtn" bsStyle={this.state.role === 'ADMIN' ? 'primary' : 'default'} onClick={() => this.refetch('ADMIN')}>
                <FormattedMessage id='members.admin' defaultMessage='administrators' />
              </Button>
              <Button className="filterBtn" bsStyle={this.state.role === 'MEMBER' ? 'primary' : 'default'} onClick={() => this.refetch('MEMBER')}>
                <FormattedMessage id='members.members' defaultMessage='members' />
              </Button>
              <Button className="filterBtn" bsStyle={this.state.role === 'BACKER' ? 'primary' : 'default'} onClick={() => this.refetch('BACKER')}>
                <FormattedMessage id='members.paid' defaultMessage='backers' />
              </Button>
            </ButtonGroup>

        }

          {members.map((member) =>
            (<Member
              key={member.id}
              member={member}
              className={classNames(this.props.className, size)}
              collective={collective}
              viewMode={viewMode}
              LoggedInUser={LoggedInUser}
             />)
          )}

        { members.length % 10 === 0 && members.length >= limit &&

            <Button bsStyle='default' onClick={this.fetchMore}>
              {this.state.loading && <FormattedMessage id='loading' defaultMessage='loading' />}
              {!this.state.loading && <FormattedMessage id='loadMore' defaultMessage='load more' />}
            </Button>

        }

    );
  }

}

const getMembersQuery = gql`
query Members($CollectiveId: Int!, $TierId: Int, $role: String, $type: String, $limit: Int, $offset: Int, $orderBy: String) {
  allMembers(CollectiveId: $CollectiveId, TierId: $TierId, role: $role, type: $type, limit: $limit, offset: $offset, orderBy: $orderBy) {
    id
    role
    createdAt
    stats {
      totalDonations
    }
    tier {
      id
      name
    }
    member {
      id
      type
      name
      company
      description
      slug
      image
      backgroundImage
      website
    }
  }
}
`;

export const addMembersData = graphql(getMembersQuery, {
  options(props) {
    return {
      variables: {
        CollectiveId: props.collective.id,
        TierId: props.tier && props.tier.id,
        offset: 0,
        type: props.type,
        role: props.role,
        orderBy: props.orderBy,
        limit: props.limit || MEMBERS_PER_PAGE * 2
      }
    }
  },
  props: ({ data }) => ({
    data,
    fetchMore: () => {
      return data.fetchMore({
        variables: {
          offset: data.allMembers.length,
          limit: MEMBERS_PER_PAGE
        },
        updateQuery: (previousResult, { fetchMoreResult }) => {
          if (!fetchMoreResult) {
            return previousResult
          }
          return Object.assign({}, previousResult, {
            // Append the new posts results to the old one
            allMembers: [...previousResult.allMembers, ...fetchMoreResult.allMembers]
          })
        }
      })
    }
  })
});

export default addMembersData(withIntl(MembersWithData));
<|endoftext|>


<commit_before>/**
 * jQuery UI PickList Widget
 *
 * Copyright (c) 2012 Awnry Software
 * Distributed under the terms of the MIT License.
 *
 * http://www.awnry.com/
 */
(function($)
{
	$.widget("awnry.pickList",
	{
		options:
		{
			// Container classes
			mainClass:					"pickList",
			listContainerClass:			"pickList_listContainer",
			sourceListContainerClass:	"pickList_sourceListContainer",
			controlsContainerClass:		"pickList_controlsContainer",
			targetListContainerClass:	"pickList_targetListContainer",
			listClass:					"pickList_list",
			sourceListClass:			"pickList_sourceList",
			targetListClass:			"pickList_targetList",
			clearClass:					"pickList_clear",

			// List item classes
			listItemClass:				"pickList_listItem",
			selectedListItemClass:		"pickList_selectedListItem",

			// Control classes
			addAllClass:				"pickList_addAll",
			addClass:					"pickList_add",
			removeAllClass:				"pickList_removeAll",
			removeClass:				"pickList_remove",

			// Control labels
			addAllLabel:				">>",
			addLabel:					">",
			removeAllLabel:				"<<",
			removeLabel:				"<",

			// List labels
			listLabelClass:				"pickList_listLabel",
			sourceListLabel:			"Available",
			sourceListLabelClass:		"pickList_sourceListLabel",
			targetListLabel:			"Selected",
			targetListLabelClass:		"pickList_targetListLabel",

			// Behavior
			sortItems:					true
		},

		_create: function()
		{
			var self = this;

			self._buildPickList();
			self._refresh();
		},

		_buildPickList: function()
		{
			var self = this;

			self.pickList = $("<div/>")
					.hide()
					.addClass(self.options.mainClass)
					.insertAfter(self.element)
					.append(self._buildSourceList())
					.append(self._buildControls())
					.append(self._buildTargetList())
					.append( $("<div/>").addClass(self.options.clearClass) );

			self._populateLists();

			self.element.hide();
			self.pickList.show();
		},

		_buildSourceList: function()
		{
			var self = this;

			var container = $("<div/>")
					.addClass(self.options.listContainerClass)
					.addClass(self.options.sourceListContainerClass)
					.disableSelection();

			var label = $("<div/>")
					.text(self.options.sourceListLabel)
					.addClass(self.options.listLabelClass)
					.addClass(self.options.sourceListLabelClass);

			self.sourceList = $("<ul/>")
					.addClass(self.options.listClass)
					.addClass(self.options.sourceListClass)
					.delegate("li", "click", {pickList: self}, self._changeHandler);

			container
					.append(label)
					.append(self.sourceList);

			return container;
		},

		_buildTargetList: function()
		{
			var self = this;

			var container = $("<div/>")
					.addClass(self.options.listContainerClass)
					.addClass(self.options.targetListContainerClass)
					.disableSelection();

			var label = $("<div/>")
					.text(self.options.targetListLabel)
					.addClass(self.options.listLabelClass)
					.addClass(self.options.targetListLabelClass);

			self.targetList = $("<ul/>")
					.addClass(self.options.listClass)
					.addClass(self.options.targetListClass)
					.delegate("li", "click", {pickList: self}, self._changeHandler);

			container
					.append(label)
					.append(self.targetList);

			return container;
		},

		_buildControls: function()
		{
			var self = this;

			self.controls = $("<div/>").addClass(self.options.controlsContainerClass);

			self.addAllButton = $("<button/>").button().click({pickList: self}, self._addAllHandler).html(self.options.addAllLabel).addClass(self.options.addAllClass);
			self.addButton = $("<button/>").button().click({pickList: self}, self._addHandler).html(self.options.addLabel).addClass(self.options.addClass);
			self.removeButton = $("<button/>").button().click({pickList: self}, self._removeHandler).html(self.options.removeLabel).addClass(self.options.removeClass);
			self.removeAllButton = $("<button/>").button().click({pickList: self}, self._removeAllHandler).html(self.options.removeAllLabel).addClass(self.options.removeAllClass);

			self.controls
					.append(self.addAllButton)
					.append(self.addButton)
					.append(self.removeButton)
					.append(self.removeAllButton);

			return self.controls;
		},

		_populateLists: function()
		{
			var self = this;

			self.element.children().each(function()
			{
				var text = $(this).text();
				var copy = $("<li/>")
						.text(text)
						.val($(this).val())
						.addClass(self.options.listItemClass);

				if($(this).attr("selected") == "selected")
				{
					self.targetList.append( copy );
				}
				else
				{
					self.sourceList.append( copy );
				}
			});
		},

		_addItem: function(value)
		{
			var self = this;

			self.sourceList.children().each(function()
			{
				if($(this).val() == value)
				{
					self.targetList.append( self._removeSelection($(this)) );
				}
			});

			self.element.children().each(function()
			{
				if($(this).val() == value)
				{
					$(this).attr("selected", "selected");
				}
			});
		},

		_removeItem: function(value)
		{
			var self = this;

			self.targetList.children().each(function()
			{
				if($(this).val() == value)
				{
					self.sourceList.append( self._removeSelection($(this)) );
				}
			});

			self.element.children().each(function()
			{
				if($(this).val() == value)
				{
					$(this).removeAttr("selected");
				}
			});
		},

		_addAllHandler: function(e)
		{
			var self = e.data.pickList;

			self.sourceList.children().each(function()
			{
				self._addItem( $(this).val() );
			});

			self._refresh();
		},

		_addHandler: function(e)
		{
			var self = e.data.pickList;

			self.sourceList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					self._addItem( $(this).val() );
				}
			});

			self._refresh();
		},

		_removeHandler: function(e)
		{
			var self = e.data.pickList;

			self.targetList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					self._removeItem( $(this).val() );
				}
			});

			self._refresh();
		},

		_removeAllHandler: function(e)
		{
			var self = e.data.pickList;

			self.targetList.children().each(function()
			{
				self._removeItem( $(this).val() );
			});

			self._refresh();
		},

		_refresh: function()
		{
			var self = this;

			// Refresh the states of the Add All and Remove All buttons.
			self.addAllButton.button( (self.sourceList.children().length > 0) ? "enable" : "disable" );
			self.removeAllButton.button( (self.targetList.children().length > 0) ? "enable" : "disable" );

			// Refresh the Add button state.
			var sourceSelected = false;
			self.sourceList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					sourceSelected = true;
				}
			});
			self.addButton.button( sourceSelected ? "enable" : "disable" );

			// Refresh the Remove button state.
			var targetSelected = false;
			self.targetList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					targetSelected = true;
				}
			});
			self.removeButton.button( targetSelected ? "enable" : "disable" );

			// Refresh the buttons.
			self.controls.children().each(function()
			{
				$(this).button("refresh");
			});

			// Sort the selection lists.
			if(self.options.sortItems)
			{
				self._sortItems(self.sourceList);
				self._sortItems(self.targetList);
			}
		},

		_sortItems: function(list)
		{
			var items = new Array();

			list.children().each(function()
			{
				items.push( $(this) );
			});

			items.sort(function(a, b)
			{
				if(a.text() > b.text())
				{
					return 1;
				}
				else if(a.text() == b.text())
				{
					return 0;
				}
				else
				{
					return -1;
				}
			});

			list.empty();

			for(var i = 0; i < items.length; i++)
			{
				list.append(items[i]);
			}
		},

		_changeHandler: function(e)
		{
			var self = e.data.pickList;

			if(e.ctrlKey)
			{
				if(self._isSelected( $(this) ))
				{
					self._removeSelection( $(this) );
				}
				else
				{
					self._addSelection( $(this) );
				}
			}
			else
			{
				$(this).parent().children().each(function()
				{
					self._removeSelection( $(this) );
				});

				self._addSelection( $(this) );
			}

			self._refresh();
		},

		_isSelected: function(listItem)
		{
			return listItem.hasClass("ui-selected");
		},

		_addSelection: function(listItem)
		{
			var self = this;

			return listItem
					.addClass("ui-selected")
					.addClass("ui-state-highlight")
					.addClass(self.options.selectedListItemClass);
		},

		_removeSelection: function(listItem)
		{
			var self = this;

			return listItem
					.removeClass("ui-selected")
					.removeClass("ui-state-highlight")
					.removeClass(self.options.selectedListItemClass);
		},

		_setOption: function(key, value)
		{
			switch(key)
			{
				case "clear":
				{
					break;
				}
			}

			$.Widget.prototype._setOption.apply(this, arguments);
		},

		destroy: function()
		{
			$.Widget.prototype.destroy.call(this);
		}
	});
}(jQuery));<commit_msg>Added support for SHIFT+clicking to select a range of list items.<commit_after>/**
 * jQuery UI PickList Widget
 *
 * Copyright (c) 2012 Awnry Software
 * Distributed under the terms of the MIT License.
 *
 * http://www.awnry.com/
 */
(function($)
{
	$.widget("awnry.pickList",
	{
		options:
		{
			// Container classes
			mainClass:					"pickList",
			listContainerClass:			"pickList_listContainer",
			sourceListContainerClass:	"pickList_sourceListContainer",
			controlsContainerClass:		"pickList_controlsContainer",
			targetListContainerClass:	"pickList_targetListContainer",
			listClass:					"pickList_list",
			sourceListClass:			"pickList_sourceList",
			targetListClass:			"pickList_targetList",
			clearClass:					"pickList_clear",

			// List item classes
			listItemClass:				"pickList_listItem",
			selectedListItemClass:		"pickList_selectedListItem",

			// Control classes
			addAllClass:				"pickList_addAll",
			addClass:					"pickList_add",
			removeAllClass:				"pickList_removeAll",
			removeClass:				"pickList_remove",

			// Control labels
			addAllLabel:				">>",
			addLabel:					">",
			removeAllLabel:				"<<",
			removeLabel:				"<",

			// List labels
			listLabelClass:				"pickList_listLabel",
			sourceListLabel:			"Available",
			sourceListLabelClass:		"pickList_sourceListLabel",
			targetListLabel:			"Selected",
			targetListLabelClass:		"pickList_targetListLabel",

			// Behavior
			sortItems:					true
		},

		_create: function()
		{
			var self = this;

			self._buildPickList();
			self._refresh();
		},

		_buildPickList: function()
		{
			var self = this;

			self.pickList = $("<div/>")
					.hide()
					.addClass(self.options.mainClass)
					.insertAfter(self.element)
					.append(self._buildSourceList())
					.append(self._buildControls())
					.append(self._buildTargetList())
					.append( $("<div/>").addClass(self.options.clearClass) );

			self._populateLists();

			self.element.hide();
			self.pickList.show();
		},

		_buildSourceList: function()
		{
			var self = this;

			var container = $("<div/>")
					.addClass(self.options.listContainerClass)
					.addClass(self.options.sourceListContainerClass)
					.disableSelection();

			var label = $("<div/>")
					.text(self.options.sourceListLabel)
					.addClass(self.options.listLabelClass)
					.addClass(self.options.sourceListLabelClass);

			self.sourceList = $("<ul/>")
					.addClass(self.options.listClass)
					.addClass(self.options.sourceListClass)
					.delegate("li", "click", {pickList: self}, self._changeHandler);

			container
					.append(label)
					.append(self.sourceList);

			return container;
		},

		_buildTargetList: function()
		{
			var self = this;

			var container = $("<div/>")
					.addClass(self.options.listContainerClass)
					.addClass(self.options.targetListContainerClass)
					.disableSelection();

			var label = $("<div/>")
					.text(self.options.targetListLabel)
					.addClass(self.options.listLabelClass)
					.addClass(self.options.targetListLabelClass);

			self.targetList = $("<ul/>")
					.addClass(self.options.listClass)
					.addClass(self.options.targetListClass)
					.delegate("li", "click", {pickList: self}, self._changeHandler);

			container
					.append(label)
					.append(self.targetList);

			return container;
		},

		_buildControls: function()
		{
			var self = this;

			self.controls = $("<div/>").addClass(self.options.controlsContainerClass);

			self.addAllButton = $("<button/>").button().click({pickList: self}, self._addAllHandler).html(self.options.addAllLabel).addClass(self.options.addAllClass);
			self.addButton = $("<button/>").button().click({pickList: self}, self._addHandler).html(self.options.addLabel).addClass(self.options.addClass);
			self.removeButton = $("<button/>").button().click({pickList: self}, self._removeHandler).html(self.options.removeLabel).addClass(self.options.removeClass);
			self.removeAllButton = $("<button/>").button().click({pickList: self}, self._removeAllHandler).html(self.options.removeAllLabel).addClass(self.options.removeAllClass);

			self.controls
					.append(self.addAllButton)
					.append(self.addButton)
					.append(self.removeButton)
					.append(self.removeAllButton);

			return self.controls;
		},

		_populateLists: function()
		{
			var self = this;

			self.element.children().each(function()
			{
				var text = $(this).text();
				var copy = $("<li/>")
						.text(text)
						.val($(this).val())
						.addClass(self.options.listItemClass);

				if($(this).attr("selected") == "selected")
				{
					self.targetList.append( copy );
				}
				else
				{
					self.sourceList.append( copy );
				}
			});
		},

		_addItem: function(value)
		{
			var self = this;

			self.sourceList.children().each(function()
			{
				if($(this).val() == value)
				{
					self.targetList.append( self._removeSelection($(this)) );
				}
			});

			self.element.children().each(function()
			{
				if($(this).val() == value)
				{
					$(this).attr("selected", "selected");
				}
			});
		},

		_removeItem: function(value)
		{
			var self = this;

			self.targetList.children().each(function()
			{
				if($(this).val() == value)
				{
					self.sourceList.append( self._removeSelection($(this)) );
				}
			});

			self.element.children().each(function()
			{
				if($(this).val() == value)
				{
					$(this).removeAttr("selected");
				}
			});
		},

		_addAllHandler: function(e)
		{
			var self = e.data.pickList;

			self.sourceList.children().each(function()
			{
				self._addItem( $(this).val() );
			});

			self._refresh();
		},

		_addHandler: function(e)
		{
			var self = e.data.pickList;

			self.sourceList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					self._addItem( $(this).val() );
				}
			});

			self._refresh();
		},

		_removeHandler: function(e)
		{
			var self = e.data.pickList;

			self.targetList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					self._removeItem( $(this).val() );
				}
			});

			self._refresh();
		},

		_removeAllHandler: function(e)
		{
			var self = e.data.pickList;

			self.targetList.children().each(function()
			{
				self._removeItem( $(this).val() );
			});

			self._refresh();
		},

		_refresh: function()
		{
			var self = this;

			// Refresh the states of the Add All and Remove All buttons.
			self.addAllButton.button( (self.sourceList.children().length > 0) ? "enable" : "disable" );
			self.removeAllButton.button( (self.targetList.children().length > 0) ? "enable" : "disable" );

			// Refresh the Add button state.
			var sourceSelected = false;
			self.sourceList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					sourceSelected = true;
				}
			});
			self.addButton.button( sourceSelected ? "enable" : "disable" );

			// Refresh the Remove button state.
			var targetSelected = false;
			self.targetList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					targetSelected = true;
				}
			});
			self.removeButton.button( targetSelected ? "enable" : "disable" );

			// Refresh the buttons.
			self.controls.children().each(function()
			{
				$(this).button("refresh");
			});

			// Sort the selection lists.
			if(self.options.sortItems)
			{
				self._sortItems(self.sourceList);
				self._sortItems(self.targetList);
			}
		},

		_sortItems: function(list)
		{
			var items = new Array();

			list.children().each(function()
			{
				items.push( $(this) );
			});

			items.sort(function(a, b)
			{
				if(a.text() > b.text())
				{
					return 1;
				}
				else if(a.text() == b.text())
				{
					return 0;
				}
				else
				{
					return -1;
				}
			});

			list.empty();

			for(var i = 0; i < items.length; i++)
			{
				list.append(items[i]);
			}
		},

		_changeHandler: function(e)
		{
			var self = e.data.pickList;

			if(e.ctrlKey)
			{
				if(self._isSelected( $(this) ))
				{
					self._removeSelection( $(this) );
				}
				else
				{
					self.lastSelectedItem = $(this);
					self._addSelection( $(this) );
				}
			}
			else if(e.shiftKey)
			{
				var current = $(this).val();
				var last = self.lastSelectedItem.val();

				if($(this).index() < $(self.lastSelectedItem).index())
				{
					var temp = current;
					current = last;
					last = temp;
				}

				var pastStart = false;
				var beforeEnd = true;

				self._clearSelections( $(this).parent() );

				$(this).parent().children().each(function()
				{
					if($(this).val() == last)
					{
						pastStart = true;
					}

					if(pastStart && beforeEnd)
					{
						self._addSelection( $(this) );
					}

					if($(this).val() == current)
					{
						beforeEnd = false;
					}

				});
			}
			else
			{
				self.lastSelectedItem = $(this);
				self._clearSelections( $(this).parent() );
				self._addSelection( $(this) );
			}

			self._refresh();
		},

		_isSelected: function(listItem)
		{
			return listItem.hasClass("ui-selected");
		},

		_addSelection: function(listItem)
		{
			var self = this;

			return listItem
					.addClass("ui-selected")
					.addClass("ui-state-highlight")
					.addClass(self.options.selectedListItemClass);
		},

		_removeSelection: function(listItem)
		{
			var self = this;

			return listItem
					.removeClass("ui-selected")
					.removeClass("ui-state-highlight")
					.removeClass(self.options.selectedListItemClass);
		},

		_clearSelections: function(list)
		{
			var self = this;

			list.children().each(function()
			{
				self._removeSelection( $(this) );
			});
		},

		_setOption: function(key, value)
		{
			switch(key)
			{
				case "clear":
				{
					break;
				}
			}

			$.Widget.prototype._setOption.apply(this, arguments);
		},

		destroy: function()
		{
			$.Widget.prototype.destroy.call(this);
		}
	});
}(jQuery));<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import Slider from 'material-ui/Slider';
import RaisedButton from 'material-ui/RaisedButton';
import FontIcon from 'material-ui/FontIcon';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import TextField from 'material-ui/TextField';
import { green700 } from 'material-ui/styles/colors';

const styles = {
  menu: {
    margin: '10px 20px 30px'
  },

  reachabilitySlider: {
    marginBottom: '0px'
  },

  batteryLevel: {
    display: 'flex',
    justifyContent: 'space-between'
  },

  batteryLevelValue: {
    fontWeight: 'bold',
    color: green700,
    fontSize: '14px'
  },

  textField: {
    display: 'inherit',
    position: 'relative',
  },

  rangeTextField: {
    display: 'inherit',
    position: 'relative',
    marginBottom: '25px'
  },

  buttonDiv: {
    display: 'flex',
  },

  showButton: {
    marginRight: '18'
  }
};

export default class ReachabilityTab extends Component {
  render() {
    return (

        <TextField
          onClick={() => this.props.updateRangeFromSelected(true)}
          onChange={(e, val) => this.props.updateRangeFromField(val)}
          style={styles.textField}
          floatingLabelText="From"
          value={this.props.rangeFromField}
        />
        <TextField
          onClick={() => this.props.updateRangeToSelected(true)}
          onChange={(e, val) => this.props.updateRangeToField(val)}
          style={styles.textField}
          floatingLabelText="To"
          value={this.props.rangeToField}
        />
        <SelectField
          floatingLabelText="Vehicle"
          value={this.props.vehicle}
          onChange={this.props.vehicleChange}
          maxHeight={210}
          fullWidth
        >
          {this.props.getVehicles().map((vehicle, index) => (
            <MenuItem key={vehicle} value={index} primaryText={vehicle} />
          ))}
        </SelectField>

          Battery Level
           (this.batteryLevel = node)}
          >
            {`${this.props.batteryPecentage}%`}

        <Slider
          onChange={this.props.updateBatterySlider}
          value={this.props.batteryLevel}
          sliderStyle={styles.reachabilitySlider}
        />
        <TextField
          onChange={this.props.updateRemainingRange}
          style={styles.rangeTextField}
          floatingLabelText="Remaining Range"
          value={this.props.remainingRange}
        />

          <RaisedButton
            label="Display"
            onClick={this.props.getRangeVisualisation}
            icon={<FontIcon className="material-icons">map</FontIcon>}
            style={styles.showButton}
          />
          {this.props.rangePolygonShowing ?
            <RaisedButton
              label="Hide"
              onClick={this.props.hideRangeVisualisation}
              icon={<FontIcon className="material-icons">map</FontIcon>}
            /> : null}

    );
  }
}

ReachabilityTab.propTypes = {
  vehicle: PropTypes.number.isRequired,
  batteryLevel: PropTypes.number.isRequired,
  batteryPecentage: PropTypes.number.isRequired,
  updateBatterySlider: PropTypes.func.isRequired,
  rangePolygonShowing: PropTypes.bool.isRequired,
  remainingRange: PropTypes.number,
  updateRemainingRange: PropTypes.func.isRequired,
  getVehicles: PropTypes.func.isRequired,
  vehicleChange: PropTypes.func.isRequired,
  getRangeVisualisation: PropTypes.func.isRequired,
  hideRangeVisualisation: PropTypes.func.isRequired,
  rangeFromField: PropTypes.string.isRequired,
  updateRangeFromField: PropTypes.func.isRequired,
  updateRangeFromSelected: PropTypes.func.isRequired,
  rangeToField: PropTypes.string.isRequired,
  updateRangeToField: PropTypes.func.isRequired,
  updateRangeToSelected: PropTypes.func.isRequired,
};

ReachabilityTab.defaultProps = {
  remainingRange: undefined
};
<commit_msg>Updated RemainingRangeTextField Prop<commit_after>import React, { Component, PropTypes } from 'react';
import Slider from 'material-ui/Slider';
import RaisedButton from 'material-ui/RaisedButton';
import FontIcon from 'material-ui/FontIcon';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import TextField from 'material-ui/TextField';
import { green700 } from 'material-ui/styles/colors';

const styles = {
  menu: {
    margin: '10px 20px 30px'
  },

  reachabilitySlider: {
    marginBottom: '0px'
  },

  batteryLevel: {
    display: 'flex',
    justifyContent: 'space-between'
  },

  batteryLevelValue: {
    fontWeight: 'bold',
    color: green700,
    fontSize: '14px'
  },

  textField: {
    display: 'inherit',
    position: 'relative',
  },

  rangeTextField: {
    display: 'inherit',
    position: 'relative',
    marginBottom: '25px'
  },

  buttonDiv: {
    display: 'flex',
  },

  showButton: {
    marginRight: '18'
  }
};

export default class ReachabilityTab extends Component {
  render() {
    return (

        <TextField
          onClick={() => this.props.updateRangeFromSelected(true)}
          onChange={(e, val) => this.props.updateRangeFromField(val)}
          style={styles.textField}
          floatingLabelText="From"
          value={this.props.rangeFromField}
        />
        <TextField
          onClick={() => this.props.updateRangeToSelected(true)}
          onChange={(e, val) => this.props.updateRangeToField(val)}
          style={styles.textField}
          floatingLabelText="To"
          value={this.props.rangeToField}
        />
        <SelectField
          floatingLabelText="Vehicle"
          value={this.props.vehicle}
          onChange={this.props.vehicleChange}
          maxHeight={210}
          fullWidth
        >
          {this.props.getVehicles().map((vehicle, index) => (
            <MenuItem key={vehicle} value={index} primaryText={vehicle} />
          ))}
        </SelectField>

          Battery Level
           (this.batteryLevel = node)}
          >
            {`${this.props.batteryPecentage}%`}

        <Slider
          onChange={this.props.updateBatterySlider}
          value={this.props.batteryLevel}
          sliderStyle={styles.reachabilitySlider}
        />
        <TextField
          onChange={this.props.updateRemainingRange}
          style={styles.rangeTextField}
          floatingLabelText="Remaining Range"
          value={this.props.remainingRange || ''}
        />

          <RaisedButton
            label="Display"
            onClick={this.props.getRangeVisualisation}
            icon={<FontIcon className="material-icons">map</FontIcon>}
            style={styles.showButton}
          />
          {this.props.rangePolygonShowing ?
            <RaisedButton
              label="Hide"
              onClick={this.props.hideRangeVisualisation}
              icon={<FontIcon className="material-icons">map</FontIcon>}
            /> : null}

    );
  }
}

ReachabilityTab.propTypes = {
  vehicle: PropTypes.number.isRequired,
  batteryLevel: PropTypes.number.isRequired,
  batteryPecentage: PropTypes.number.isRequired,
  updateBatterySlider: PropTypes.func.isRequired,
  rangePolygonShowing: PropTypes.bool.isRequired,
  remainingRange: PropTypes.number,
  updateRemainingRange: PropTypes.func.isRequired,
  getVehicles: PropTypes.func.isRequired,
  vehicleChange: PropTypes.func.isRequired,
  getRangeVisualisation: PropTypes.func.isRequired,
  hideRangeVisualisation: PropTypes.func.isRequired,
  rangeFromField: PropTypes.string.isRequired,
  updateRangeFromField: PropTypes.func.isRequired,
  updateRangeFromSelected: PropTypes.func.isRequired,
  rangeToField: PropTypes.string.isRequired,
  updateRangeToField: PropTypes.func.isRequired,
  updateRangeToSelected: PropTypes.func.isRequired,
};

ReachabilityTab.defaultProps = {
  remainingRange: 0
};
<|endoftext|>


<commit_before>// Copyright 2017, University of Colorado Boulder

/**
 * wrapper type for SphereBucket
 * @author Sam Reid (PhET Interactive Simulations)
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // modules
  var phetcommon = require( 'PHETCOMMON/phetcommon' );

  // phet-io modules
  var assertInstanceOf = require( 'ifphetio!PHET_IO/assertions/assertInstanceOf' );
  var phetio = require( 'ifphetio!PHET_IO/phetio' );
  var phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );
  var TObject = require( 'ifphetio!PHET_IO/types/TObject' );

  /**
   * @param {SphereBucket} instance
   * @param {string} phetioID
   * @constructor
   */
  function TSphereBucket( instance, phetioID ) {
    assert && assertInstanceOf( instance, phet.phetcommon.SphereBucket );
    TObject.call( this, instance, phetioID );
  }

  // helper function for retrieving the tandem for a particle
  function getParticleTandemID( particle ) {
    return particle.particleTandem.id;
  }

  phetioInherit( TObject, 'TSphereBucket', TSphereBucket, {}, {

    /**
     * create a description of the state that isn't automatically handled by the framework (e.g. Property instances)
     * @param {SphereBucket} instance
     */
    toStateObject: function( instance ) {
      return instance._particles.map( getParticleTandemID );
    },

    /**
     * @param {string[]} stateArray
     * @returns {Particle[]}
     */
    fromStateObject: function( stateArray ) {
      return stateArray.map( function( tandemID ) { return phetio.getInstance( tandemID ); } );
    },

    /**
     * @param {SphereBucket} instance
     * @param {Particle[]} particleArray
     */
    setValue: function( instance, particleArray ) {

      // remove all the particles from the observable arrays
      instance.reset();

      // add back the particles
      particleArray.forEach( function( particle ) { instance.addParticle( particle ); } );
    },

    documentation: 'A model of a bucket into which spherical objects can be placed.'
  } );

  phetcommon.register( 'TSphereBucket', TSphereBucket );

  return TSphereBucket;
} );<commit_msg>Moved assertInstanceOf up a level and eliminated assertions/ directory, see https://github.com/phetsims/phet-io/issues/1250<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * wrapper type for SphereBucket
 * @author Sam Reid (PhET Interactive Simulations)
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // modules
  var phetcommon = require( 'PHETCOMMON/phetcommon' );

  // phet-io modules
  var assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  var phetio = require( 'ifphetio!PHET_IO/phetio' );
  var phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );
  var TObject = require( 'ifphetio!PHET_IO/types/TObject' );

  /**
   * @param {SphereBucket} instance
   * @param {string} phetioID
   * @constructor
   */
  function TSphereBucket( instance, phetioID ) {
    assert && assertInstanceOf( instance, phet.phetcommon.SphereBucket );
    TObject.call( this, instance, phetioID );
  }

  // helper function for retrieving the tandem for a particle
  function getParticleTandemID( particle ) {
    return particle.particleTandem.id;
  }

  phetioInherit( TObject, 'TSphereBucket', TSphereBucket, {}, {

    /**
     * create a description of the state that isn't automatically handled by the framework (e.g. Property instances)
     * @param {SphereBucket} instance
     */
    toStateObject: function( instance ) {
      return instance._particles.map( getParticleTandemID );
    },

    /**
     * @param {string[]} stateArray
     * @returns {Particle[]}
     */
    fromStateObject: function( stateArray ) {
      return stateArray.map( function( tandemID ) { return phetio.getInstance( tandemID ); } );
    },

    /**
     * @param {SphereBucket} instance
     * @param {Particle[]} particleArray
     */
    setValue: function( instance, particleArray ) {

      // remove all the particles from the observable arrays
      instance.reset();

      // add back the particles
      particleArray.forEach( function( particle ) { instance.addParticle( particle ); } );
    },

    documentation: 'A model of a bucket into which spherical objects can be placed.'
  } );

  phetcommon.register( 'TSphereBucket', TSphereBucket );

  return TSphereBucket;
} );<|endoftext|>