<commit_before>import React, { useState, useEffect } from 'react';
import { PropTypes as T } from 'prop-types';
import { connect } from 'react-redux';
import _ from 'lodash';

import { getCountryBbox } from '../../utils/countries';
import { formatThousands } from '../../utils/format';
import config from '../../config';
import { openDownloadModal } from '../../actions/action-creators';

import Header, { LoadingHeader, ErrorHeader } from '../../components/header';
import LoadingMessage from '../../components/loading-message';
import InfoMessage from '../../components/info-message';
import MapComponent from '../../components/map';
import LocationsSource from '../../components/map/locations-source';
import MeasurementsLayer from '../../components/map/measurements-layer';
import Legend from '../../components/map/legend';
import LocationCard from '../locations-hub/location-card';

const defaultLocations = {
  locationFetching: false,
  locationError: null,
  locations: null,
};

const defaultCountry = {
  countryFetching: false,
  countryError: null,
  country: null,
};

function Country(props) {
  const id = props.match.params.name;
  const [
    { locationFetching, locationError, locations },
    setLocations,
  ] = useState(defaultLocations);
  const [{ countryFetching, countryError, country }, setCountry] = useState(
    defaultCountry
  );

  useEffect(() => {
    const fetchLocations = id => {
      setLocations(state => ({
        ...state,
        locationFetching: true,
        locationError: null,
      }));
      let limit = 10000;
      fetch(`${config.api}/locations?limit=${limit}&country=${id}`)
        .then(response => {
          if (response.status >= 400) {
            throw new Error('Bad response');
          }
          return response.json();
        })
        .then(
          json => {
            setLocations(state => ({
              ...state,
              fetched: true,
              locationFetching: false,
              locations: json.results,
            }));
          },
          e => {
            console.log('e', e);
            setLocations(state => ({
              ...state,
              locationFetching: false,
              locationError: e,
            }));
          }
        );
    };

    const fetchCountry = id => {
      setCountry(state => ({
        ...state,
        countryFetching: true,
        countryError: null,
      }));
      fetch(`${config.api}/countries/${id}`)
        .then(response => {
          if (response.status >= 400) {
            throw new Error('Bad response');
          }
          return response.json();
        })
        .then(
          json => {
            setCountry(state => ({
              ...state,
              countryFetching: false,
              country: json.results[0],
            }));
          },
          e => {
            console.log('e', e);
            setCountry(state => ({
              ...state,
              countryFetching: false,
              countryError: e,
            }));
          }
        );
    };

    fetchLocations(id);
    fetchCountry(id);

    return () => {
      setLocations(defaultLocations);
      setCountry(defaultCountry);
    };
  }, [id]);

  function onDownloadClick(data) {
    // e && e.preventDefault();
    props._openDownloadModal(data);
  }

  // gets sources from locations and uses set to create an array with no duplicates
  const locationSources =
    locations &&
    locations
      .map(location => location.sources)
      .flat()
      .filter(source => source !== undefined);
  const sourceList = locations
    ? Array.from(new Set(locationSources.map(source => source.id))).map(
        sourceId => {
          return locationSources.find(source => source.id === sourceId);
        }
      )
    : null;

  let locationGroups = _(locations).sortBy('city').groupBy('city').value();

  return (

      {countryFetching || locationFetching ? (
        <LoadingHeader />
      ) : country && locations && !countryError ? (
        <>
           1 ? 'locations' : 'location',
              },
              {
                number: formatThousands(country.count),
                label: 'measurements',
              },
              {
                number: sourceList.length,
                label: sourceList.length > 1 ? 'sources' : 'source',
              },
            ]}
            action={{
              api: config.apiDocs,
              download: () =>
                onDownloadClick({
                  country: id,
                }),
            }}
          />

                <MapComponent
                  bbox={getCountryBbox(country.code)}
                  scrollZoomDisabled
                >
                  <LocationsSource activeParameter={2}>
                    <MeasurementsLayer
                      activeParameter={2}
                      country={country.code}
                    />
                  </LocationsSource>
                  <Legend
                    activeParameter={{ parameterId: 2, displayName: 'PM2.5' }}
                  />
                </MapComponent>

            {locationFetching ? (
              <LoadingMessage />
            ) : !locationError ? (

                {Object.entries(locationGroups).map(([city, cityLocations]) => (

                          {city}{' '}

                            {cityLocations.length}{' '}
                            {cityLocations.length > 1
                              ? 'locations'
                              : 'location'}

                          <a
                            href="#"
                            className="location-download-button"
                            title={`Download ${city} data`}
                            onClick={() =>
                              onDownloadClick({
                                country: id,
                                area: city,
                              })
                            }
                          >
                            Download

                        {cityLocations.map(loc => {
                          let openModal = () =>
                            onDownloadClick({
                              country: loc.country,
                              area: loc.city,
                              location: loc.id,
                            });
                          return (
                            <LocationCard
                              mobile={loc.isMobile}
                              key={loc.id}
                              city={loc.city}
                              country={loc.country}
                              firstUpdated={loc.firstUpdated}
                              id={loc.id}
                              lastUpdated={loc.lastUpdated}
                              name={loc.name}
                              onDownloadClick={openModal}
                              parametersList={loc.parameters}
                              sources={loc.sources}
                              sourceType={loc.sourceType}
                              totalMeasurements={loc.measurements}
                            />
                          );
                        })}

                ))}

            ) : (
              <InfoMessage standardMessage />
            )}

        </>
      ) : (
        <ErrorHeader />
      )}

  );
}

Country.propTypes = {
  match: T.object,

  _openDownloadModal: T.func,
};

// /////////////////////////////////////////////////////////////////// //
// Connect functions

function dispatcher(dispatch) {
  return {
    _openDownloadModal: (...args) => dispatch(openDownloadModal(...args)),
  };
}

export default connect(null, dispatcher)(Country);
<commit_msg>Remove area number from country page entirely<commit_after>import React, { useState, useEffect } from 'react';
import { PropTypes as T } from 'prop-types';
import { connect } from 'react-redux';
import _ from 'lodash';

import { getCountryBbox } from '../../utils/countries';
import { formatThousands } from '../../utils/format';
import config from '../../config';
import { openDownloadModal } from '../../actions/action-creators';

import Header, { LoadingHeader, ErrorHeader } from '../../components/header';
import LoadingMessage from '../../components/loading-message';
import InfoMessage from '../../components/info-message';
import MapComponent from '../../components/map';
import LocationsSource from '../../components/map/locations-source';
import MeasurementsLayer from '../../components/map/measurements-layer';
import Legend from '../../components/map/legend';
import LocationCard from '../locations-hub/location-card';

const defaultLocations = {
  locationFetching: false,
  locationError: null,
  locations: null,
};

const defaultCountry = {
  countryFetching: false,
  countryError: null,
  country: null,
};

function Country(props) {
  const id = props.match.params.name;
  const [
    { locationFetching, locationError, locations },
    setLocations,
  ] = useState(defaultLocations);
  const [{ countryFetching, countryError, country }, setCountry] = useState(
    defaultCountry
  );

  useEffect(() => {
    const fetchLocations = id => {
      setLocations(state => ({
        ...state,
        locationFetching: true,
        locationError: null,
      }));
      let limit = 10000;
      fetch(`${config.api}/locations?limit=${limit}&country=${id}`)
        .then(response => {
          if (response.status >= 400) {
            throw new Error('Bad response');
          }
          return response.json();
        })
        .then(
          json => {
            setLocations(state => ({
              ...state,
              fetched: true,
              locationFetching: false,
              locations: json.results,
            }));
          },
          e => {
            console.log('e', e);
            setLocations(state => ({
              ...state,
              locationFetching: false,
              locationError: e,
            }));
          }
        );
    };

    const fetchCountry = id => {
      setCountry(state => ({
        ...state,
        countryFetching: true,
        countryError: null,
      }));
      fetch(`${config.api}/countries/${id}`)
        .then(response => {
          if (response.status >= 400) {
            throw new Error('Bad response');
          }
          return response.json();
        })
        .then(
          json => {
            setCountry(state => ({
              ...state,
              countryFetching: false,
              country: json.results[0],
            }));
          },
          e => {
            console.log('e', e);
            setCountry(state => ({
              ...state,
              countryFetching: false,
              countryError: e,
            }));
          }
        );
    };

    fetchLocations(id);
    fetchCountry(id);

    return () => {
      setLocations(defaultLocations);
      setCountry(defaultCountry);
    };
  }, [id]);

  function onDownloadClick(data) {
    // e && e.preventDefault();
    props._openDownloadModal(data);
  }

  // gets sources from locations and uses set to create an array with no duplicates
  const locationSources =
    locations &&
    locations
      .map(location => location.sources)
      .flat()
      .filter(source => source !== undefined);
  const sourceList = locations
    ? Array.from(new Set(locationSources.map(source => source.id))).map(
        sourceId => {
          return locationSources.find(source => source.id === sourceId);
        }
      )
    : null;

  let locationGroups = _(locations).sortBy('city').groupBy('city').value();

  return (

      {countryFetching || locationFetching ? (
        <LoadingHeader />
      ) : country && locations && !countryError ? (
        <>
           1 ? 'locations' : 'location',
              },
              {
                number: formatThousands(country.count),
                label: 'measurements',
              },
              {
                number: sourceList.length,
                label: sourceList.length > 1 ? 'sources' : 'source',
              },
            ]}
            action={{
              api: config.apiDocs,
              download: () =>
                onDownloadClick({
                  country: id,
                }),
            }}
          />

                <MapComponent
                  bbox={getCountryBbox(country.code)}
                  scrollZoomDisabled
                >
                  <LocationsSource activeParameter={2}>
                    <MeasurementsLayer
                      activeParameter={2}
                      country={country.code}
                    />
                  </LocationsSource>
                  <Legend
                    activeParameter={{ parameterId: 2, displayName: 'PM2.5' }}
                  />
                </MapComponent>

            {locationFetching ? (
              <LoadingMessage />
            ) : !locationError ? (

                {Object.entries(locationGroups).map(([city, cityLocations]) => (

                          {city}{' '}

                            {cityLocations.length}{' '}
                            {cityLocations.length > 1
                              ? 'locations'
                              : 'location'}

                          <a
                            href="#"
                            className="location-download-button"
                            title={`Download ${city} data`}
                            onClick={() =>
                              onDownloadClick({
                                country: id,
                                area: city,
                              })
                            }
                          >
                            Download

                        {cityLocations.map(loc => {
                          let openModal = () =>
                            onDownloadClick({
                              country: loc.country,
                              area: loc.city,
                              location: loc.id,
                            });
                          return (
                            <LocationCard
                              mobile={loc.isMobile}
                              key={loc.id}
                              city={loc.city}
                              country={loc.country}
                              firstUpdated={loc.firstUpdated}
                              id={loc.id}
                              lastUpdated={loc.lastUpdated}
                              name={loc.name}
                              onDownloadClick={openModal}
                              parametersList={loc.parameters}
                              sources={loc.sources}
                              sourceType={loc.sourceType}
                              totalMeasurements={loc.measurements}
                            />
                          );
                        })}

                ))}

            ) : (
              <InfoMessage standardMessage />
            )}

        </>
      ) : (
        <ErrorHeader />
      )}

  );
}

Country.propTypes = {
  match: T.object,

  _openDownloadModal: T.func,
};

// /////////////////////////////////////////////////////////////////// //
// Connect functions

function dispatcher(dispatch) {
  return {
    _openDownloadModal: (...args) => dispatch(openDownloadModal(...args)),
  };
}

export default connect(null, dispatcher)(Country);
<|endoftext|>


<commit_before>angular.module('npact')

  .controller('npactGraphContainerCtrl', function($scope, $element, $window, npactConstants, Utils, GraphConfig, Evt, $log, GraphingCalculator) {
    'use strict';

    var baseOpts = angular.extend({ width: $element.width() },
                                  npactConstants.graphSpecDefaults),
        updateBaseOptions = function() {
          baseOpts.headerSpec = GraphConfig.headerSpec();
          // Disabling margin, we're not sure this really is worth it
          // and it is causing Z-index paint problems at the moment.
          // baseOpts.margin = Utils.orderOfMagnitude(GraphConfig.basesPerGraph, -1);
          baseOpts.margin = 0;
          baseOpts.colors = GraphConfig.colorBlindFriendly ?
            npactConstants.colorBlindLineColors : npactConstants.lineColors;
          baseOpts.headerY = baseOpts.headerSpec.headerY;
          baseOpts.axisTitle = GraphConfig.profileTitle();
          return baseOpts;
        },
        onPan = function(evt, opts) {
          $log.log('Pan event:', opts);
          var offset = Math.floor(opts.newStartBase - opts.oldStartBase);
          GraphConfig.offset += offset;
          // TODO: Event originated from outside ng, but why doesn't
          // `$watch` pick up the `offset` change?
          $scope.$apply();
        },
        onZoom = function(evt, opts) {
          $log.log('Zoom event:', opts);
          var zoomOpts = angular.extend({}, opts, GraphConfig),
              res = GraphingCalculator.zoom(zoomOpts);
          GraphConfig.offset = res.offset;
          GraphConfig.basesPerGraph = res.basesPerGraph;
          // TODO: Event originated from outside ng, but why doesn't
          // `$watch` pick up the `offset` change?
          $scope.$apply();
        },
        redraw = function() {
          updateBaseOptions();
          $scope.$broadcast(Evt.REDRAW);
        },
        rebuild = function() {
          $scope.graphSpecs = GraphConfig.partition();
          $log.log('Partitioned into', $scope.graphSpecs.length, 'rows.');
          updateVisibility();
          //TODO: this would be the DISCARD event
          redraw();
        };

    this.graphOptions = function(idx, element) {
      range = $scope.graphSpecs[idx];
      var opts = angular.extend(
        {$scope: $scope, element: element},
        baseOpts, range);
      opts.m = GraphingCalculator.chart(opts);
      opts.xaxis = GraphingCalculator.xaxis(opts);
      return opts;
    };

    // listen for graph events
    $scope.$on(Evt.PAN, onPan);
    $scope.$on(Evt.ZOOM, onZoom);

    // watch the environment for changes we care about
    $scope.$watchCollection(function() {
      return [ GraphConfig.length, GraphConfig.basesPerGraph,
               GraphConfig.offset, GraphConfig.startBase,
               GraphConfig.endBase, GraphConfig.profileTitle() ];
    }, rebuild);
    $scope.$watch(GraphConfig.headerSpec, redraw, true);
    $scope.$watch(function() { return GraphConfig.colorBlindFriendly; }, redraw);

    /***  Scrolling and Graph Visibility management ***/
    var $win = angular.element($window),
        winHeight = $win.height(),
        borderHeight = 1,
        graphBoxHeight = npactConstants.graphSpecDefaults.height + borderHeight,
        slack = 50, // how many pixels outside of viewport to render
        topOffset = $element.offset().top,
        topIdx = 0, bottomIdx = 0,
        updateVisibility = function() {
          var scrollDist = $window.scrollY - topOffset - slack;
          topIdx = Math.floor(scrollDist / graphBoxHeight);
          bottomIdx = topIdx + Math.ceil((winHeight) / graphBoxHeight);
        },
        onScroll = function() {
          updateVisibility();
          $scope.$apply();
        },
        onResize = function() {
          winHeight = $win.height();
          topOffset = $element.offset().top;
          updateVisibility();
          if($element.width() !== baseOpts.width) {
            baseOpts.width = $element.width();
            redraw();
          }
          $scope.$apply();
        };
    $scope.graphHeight = npactConstants.graphSpecDefaults.height;
    this.visible = function(idx) {
      return idx >= topIdx && idx <= bottomIdx;
    };
    $win.on('resize', onResize);
    $win.on('scroll', onScroll);
    $scope.$on('$destroy', function() {
      $win.off('resize', onResize);
      $win.off('scroll', onScroll);
    });
  })
  .directive('npactGraphContainer', function(STATIC_BASE_URL) {
    return {
      restrict: 'A',
      scope: {},
      templateUrl: STATIC_BASE_URL + 'js/graphs/graph-container.html',
      controller: 'npactGraphContainerCtrl as ctrl'
    };
  })

  .directive('npactGraph', function npactGraph(Grapher, Evt, GraphingCalculator, $log) {
    'use strict';
    return {
      restrict: 'A',
      require: '^npactGraphContainer',
      link: function($scope, $element, $attrs, ctrl) {
        var g = null,
            visible = ctrl.visible,
            idx = $attrs.idx,
            el = $element[0],
            draw = _.debounce(function() {
              if(g || !visible(idx)) return;
              var graphUpdateStart = new Date();
              g = new Grapher(ctrl.graphOptions(idx, el));
              g.redraw().then(function() {
                $log.log('Draw of startBase:',
                         $scope.spec.startBase, 'took',
                         new Date() - graphUpdateStart, 'ms');
              });
            }, 50, {maxWait: 120}),
            redraw = function() {
              g.redraw();
            },
            discard = function() {
              if(g !== null) {
                g.destroy();
                g = null;
              }
              //Don't need to do anything: `$scope.$watch(draw);`
              //handles it
            };
        $scope.$on(Evt.REDRAW, discard);
        //Just call draw every time
        $scope.$watch(draw);
        $scope.$on('$destroy', discard);
      }
    };
  })
  .directive('npactExtract', function(STATIC_BASE_URL) {
    return {
      restrict: 'A',
      scope: { extract: '=npactExtract'},
      templateUrl: STATIC_BASE_URL + 'js/graphs/extract.html'
    };
  })
;
<commit_msg>jshint fix<commit_after>angular.module('npact')

  .controller('npactGraphContainerCtrl', function($scope, $element, $window, npactConstants, Utils, GraphConfig, Evt, $log, GraphingCalculator) {
    'use strict';

    var baseOpts = angular.extend({ width: $element.width() },
                                  npactConstants.graphSpecDefaults),
        updateBaseOptions = function() {
          baseOpts.headerSpec = GraphConfig.headerSpec();
          // Disabling margin, we're not sure this really is worth it
          // and it is causing Z-index paint problems at the moment.
          // baseOpts.margin = Utils.orderOfMagnitude(GraphConfig.basesPerGraph, -1);
          baseOpts.margin = 0;
          baseOpts.colors = GraphConfig.colorBlindFriendly ?
            npactConstants.colorBlindLineColors : npactConstants.lineColors;
          baseOpts.headerY = baseOpts.headerSpec.headerY;
          baseOpts.axisTitle = GraphConfig.profileTitle();
          return baseOpts;
        },
        onPan = function(evt, opts) {
          $log.log('Pan event:', opts);
          var offset = Math.floor(opts.newStartBase - opts.oldStartBase);
          GraphConfig.offset += offset;
          // TODO: Event originated from outside ng, but why doesn't
          // `$watch` pick up the `offset` change?
          $scope.$apply();
        },
        onZoom = function(evt, opts) {
          $log.log('Zoom event:', opts);
          var zoomOpts = angular.extend({}, opts, GraphConfig),
              res = GraphingCalculator.zoom(zoomOpts);
          GraphConfig.offset = res.offset;
          GraphConfig.basesPerGraph = res.basesPerGraph;
          // TODO: Event originated from outside ng, but why doesn't
          // `$watch` pick up the `offset` change?
          $scope.$apply();
        },
        redraw = function() {
          updateBaseOptions();
          $scope.$broadcast(Evt.REDRAW);
        },
        rebuild = function() {
          $scope.graphSpecs = GraphConfig.partition();
          $log.log('Partitioned into', $scope.graphSpecs.length, 'rows.');
          updateVisibility();
          //TODO: this would be the DISCARD event
          redraw();
        };

    this.graphOptions = function(idx, element) {
      var range = $scope.graphSpecs[idx],
          opts = angular.extend(
            {$scope: $scope, element: element},
            baseOpts, range);
      opts.m = GraphingCalculator.chart(opts);
      opts.xaxis = GraphingCalculator.xaxis(opts);
      return opts;
    };

    // listen for graph events
    $scope.$on(Evt.PAN, onPan);
    $scope.$on(Evt.ZOOM, onZoom);

    // watch the environment for changes we care about
    $scope.$watchCollection(function() {
      return [ GraphConfig.length, GraphConfig.basesPerGraph,
               GraphConfig.offset, GraphConfig.startBase,
               GraphConfig.endBase, GraphConfig.profileTitle() ];
    }, rebuild);
    $scope.$watch(GraphConfig.headerSpec, redraw, true);
    $scope.$watch(function() { return GraphConfig.colorBlindFriendly; }, redraw);

    /***  Scrolling and Graph Visibility management ***/
    var $win = angular.element($window),
        winHeight = $win.height(),
        borderHeight = 1,
        graphBoxHeight = npactConstants.graphSpecDefaults.height + borderHeight,
        slack = 50, // how many pixels outside of viewport to render
        topOffset = $element.offset().top,
        topIdx = 0, bottomIdx = 0,
        updateVisibility = function() {
          var scrollDist = $window.scrollY - topOffset - slack;
          topIdx = Math.floor(scrollDist / graphBoxHeight);
          bottomIdx = topIdx + Math.ceil((winHeight) / graphBoxHeight);
        },
        onScroll = function() {
          updateVisibility();
          $scope.$apply();
        },
        onResize = function() {
          winHeight = $win.height();
          topOffset = $element.offset().top;
          updateVisibility();
          if($element.width() !== baseOpts.width) {
            baseOpts.width = $element.width();
            redraw();
          }
          $scope.$apply();
        };
    $scope.graphHeight = npactConstants.graphSpecDefaults.height;
    this.visible = function(idx) {
      return idx >= topIdx && idx <= bottomIdx;
    };
    $win.on('resize', onResize);
    $win.on('scroll', onScroll);
    $scope.$on('$destroy', function() {
      $win.off('resize', onResize);
      $win.off('scroll', onScroll);
    });
  })
  .directive('npactGraphContainer', function(STATIC_BASE_URL) {
    return {
      restrict: 'A',
      scope: {},
      templateUrl: STATIC_BASE_URL + 'js/graphs/graph-container.html',
      controller: 'npactGraphContainerCtrl as ctrl'
    };
  })

  .directive('npactGraph', function npactGraph(Grapher, Evt, GraphingCalculator, $log) {
    'use strict';
    return {
      restrict: 'A',
      require: '^npactGraphContainer',
      link: function($scope, $element, $attrs, ctrl) {
        var g = null,
            visible = ctrl.visible,
            idx = $attrs.idx,
            el = $element[0],
            draw = _.debounce(function() {
              if(g || !visible(idx)) return;
              var graphUpdateStart = new Date();
              g = new Grapher(ctrl.graphOptions(idx, el));
              g.redraw().then(function() {
                $log.log('Draw of startBase:',
                         $scope.spec.startBase, 'took',
                         new Date() - graphUpdateStart, 'ms');
              });
            }, 50, {maxWait: 120}),
            redraw = function() {
              g.redraw();
            },
            discard = function() {
              if(g !== null) {
                g.destroy();
                g = null;
              }
              //Don't need to do anything: `$scope.$watch(draw);`
              //handles it
            };
        $scope.$on(Evt.REDRAW, discard);
        //Just call draw every time
        $scope.$watch(draw);
        $scope.$on('$destroy', discard);
      }
    };
  })
  .directive('npactExtract', function(STATIC_BASE_URL) {
    return {
      restrict: 'A',
      scope: { extract: '=npactExtract'},
      templateUrl: STATIC_BASE_URL + 'js/graphs/extract.html'
    };
  })
;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Tabs, Modal, Button } from 'antd';
import Edit from './Edit.js';
import View from './View.js';
import { Prompt } from 'react-router'
import { fetchInterfaceData } from '../../../../reducer/modules/interface.js';
import { withRouter } from 'react-router-dom';
import Run from './Run/Run.js'
const plugin = require('client/plugin.js');

const TabPane = Tabs.TabPane;
@connect(
  state => {
    return {
      curdata: state.inter.curdata,
      list: state.inter.list,
      editStatus: state.inter.editStatus
    }
  },
  {
    fetchInterfaceData
  }
)
class Content extends Component {
  static propTypes = {
    match: PropTypes.object,
    list: PropTypes.array,
    curdata: PropTypes.object,
    fetchInterfaceData: PropTypes.func,
    history: PropTypes.object,
    editStatus: PropTypes.bool
  }
  constructor(props) {
    super(props)
    this.state = {
      curtab: 'view',
      visible: false,
      nextTab: ''
    }
  }

  componentWillMount() {
    const params = this.props.match.params;
    this.actionId = params.actionId;
    this.handleRequest(this.props);
    window.confirm = () => {};
  }

  componentWillReceiveProps(nextProps) {
    const params = nextProps.match.params;
    if(params.actionId !== this.actionId){
      this.actionId = params.actionId;
      this.handleRequest(nextProps)
    }
  }

  handleRequest(nextProps) {
    const params = nextProps.match.params;
    this.props.fetchInterfaceData(params.actionId)
    this.setState({
      curtab: 'view'
    })
  }

  switchToView = ()=>{
    this.setState({
      curtab: 'view'
    })
  }

  onChange = (key) => {
    if (this.state.curtab === 'edit' && this.props.editStatus) {
      this.showModal();
    } else {
      this.setState({
        curtab: key
      });
    }
    this.setState({
      nextTab: key
    });
  }
  // 确定离开页面
  handleOk = () => {
    this.setState({
      visible: false,
      curtab: this.state.nextTab
    });
  }
  // 离开编辑页面的提示
  showModal = () => {
    this.setState({
      visible: true
    });
  }
  // 取消离开编辑页面
  handleCancel = () => {
    this.setState({
      visible: false
    });
  }
  render() {
    let InterfaceTabs = {
      view: {
        component: View,
        name: '预览'
      },
      edit: {
        component: Edit,
        name: '编辑'
      },
      run: {
        component: Run,
        name: '运行'
      }
    }

    plugin.emitHook('interface_tab', InterfaceTabs);

    const tabs = <Tabs onChange={this.onChange} activeKey={this.state.curtab} defaultActiveKey="view"   >
      {Object.keys(InterfaceTabs).map(key=>{
        let item = InterfaceTabs[key];
        return <TabPane tab={item.name} key={key}></TabPane>
      })}
    </Tabs>;
    let tabContent = null;
    if (this.state.curtab) {
      let C = InterfaceTabs[this.state.curtab].component;
      tabContent = <C switchToView={this.switchToView} />;
    }

    return

      <Prompt
        when={this.state.curtab === 'edit' && this.props.editStatus ? true : false}
        message={() => {
          this.showModal();
          return '离开页面会丢失当前编辑的内容，确定要离开吗？';
        }}
      />
      {tabs}
      {tabContent}
      <Modal
        title="你即将离开编辑页面"
        visible={this.state.visible}
        onCancel={this.handleCancel}
        footer={[
          <Button key="back" onClick={this.handleCancel}>取 消</Button>,
          <Button key="submit" onClick={this.handleOk}>确 定</Button>
        ]}
      >

离开页面会丢失当前编辑的内容，确定要离开吗？

      </Modal>

  }
}

export default withRouter(Content)
<commit_msg>feat: 离开编辑页面时的提示<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Tabs, Modal, Button } from 'antd';
import Edit from './Edit.js';
import View from './View.js';
import { Prompt } from 'react-router'
import { fetchInterfaceData } from '../../../../reducer/modules/interface.js';
import { withRouter } from 'react-router-dom';
import Run from './Run/Run.js'
const plugin = require('client/plugin.js');

const TabPane = Tabs.TabPane;
@connect(
  state => {
    return {
      curdata: state.inter.curdata,
      list: state.inter.list,
      editStatus: state.inter.editStatus
    }
  },
  {
    fetchInterfaceData
  }
)
class Content extends Component {
  static propTypes = {
    match: PropTypes.object,
    list: PropTypes.array,
    curdata: PropTypes.object,
    fetchInterfaceData: PropTypes.func,
    history: PropTypes.object,
    editStatus: PropTypes.bool
  }
  constructor(props) {
    super(props)
    this.state = {
      curtab: 'view',
      visible: false,
      nextTab: ''
    }
  }

  componentWillMount() {
    const params = this.props.match.params;
    this.actionId = params.actionId;
    this.handleRequest(this.props);
    // window.confirm = () => {};
  }

  componentWillReceiveProps(nextProps) {
    const params = nextProps.match.params;
    if(params.actionId !== this.actionId){
      this.actionId = params.actionId;
      this.handleRequest(nextProps)
    }
  }

  handleRequest(nextProps) {
    const params = nextProps.match.params;
    this.props.fetchInterfaceData(params.actionId)
    this.setState({
      curtab: 'view'
    })
  }

  switchToView = ()=>{
    this.setState({
      curtab: 'view'
    })
  }

  onChange = (key) => {
    if (this.state.curtab === 'edit' && this.props.editStatus) {
      this.showModal();
    } else {
      this.setState({
        curtab: key
      });
    }
    this.setState({
      nextTab: key
    });
  }
  // 确定离开页面
  handleOk = () => {
    this.setState({
      visible: false,
      curtab: this.state.nextTab
    });
  }
  // 离开编辑页面的提示
  showModal = () => {
    this.setState({
      visible: true
    });
  }
  // 取消离开编辑页面
  handleCancel = () => {
    this.setState({
      visible: false
    });
  }
  render() {
    let InterfaceTabs = {
      view: {
        component: View,
        name: '预览'
      },
      edit: {
        component: Edit,
        name: '编辑'
      },
      run: {
        component: Run,
        name: '运行'
      }
    }

    plugin.emitHook('interface_tab', InterfaceTabs);

    const tabs = <Tabs onChange={this.onChange} activeKey={this.state.curtab} defaultActiveKey="view"   >
      {Object.keys(InterfaceTabs).map(key=>{
        let item = InterfaceTabs[key];
        return <TabPane tab={item.name} key={key}></TabPane>
      })}
    </Tabs>;
    let tabContent = null;
    if (this.state.curtab) {
      let C = InterfaceTabs[this.state.curtab].component;
      tabContent = <C switchToView={this.switchToView} />;
    }

    return

      <Prompt
        when={this.state.curtab === 'edit' && this.props.editStatus ? true : false}
        message={() => {
          this.showModal();
          return '离开页面会丢失当前编辑的内容，确定要离开吗？';
        }}
      />
      {tabs}
      {tabContent}
      <Modal
        title="你即将离开编辑页面"
        visible={this.state.visible}
        onCancel={this.handleCancel}
        footer={[
          <Button key="back" onClick={this.handleCancel}>取 消</Button>,
          <Button key="submit" onClick={this.handleOk}>确 定</Button>
        ]}
      >

离开页面会丢失当前编辑的内容，确定要离开吗？

      </Modal>

  }
}

export default withRouter(Content)
<|endoftext|>


<commit_before>'use strict';

module.exports = function(demo, grid) {

    var idx = grid.behavior.columnEnum;

    var state = {
        columnIndexes: [
            idx.lastName,
            idx.totalNumberOfPetsOwned,
            idx.height,
            idx.birthDate,
            idx.birthTime,
            idx.birthState,
            // idx.residenceState,
            idx.employed,
            // idx.firstName,
            idx.income,
            idx.travel,
            // idx.squareOfIncome
        ],

        noDataMessage: 'No Data to Display',
        backgroundColor: 'white',
        font: 'normal small garamond',
        rowStripes: [
            undefined,
            undefined,
            undefined,
            { color: '#116611', backgroundColor: '#e8ffe8', font: 'italic small garamond' },
            { color: '#116611', backgroundColor: '#e8ffe8', font: 'italic small garamond' },
            { color: '#116611', backgroundColor: '#e8ffe8', font: 'italic small garamond' }
        ],

        fixedColumnCount: 1,
        fixedRowCount: 4,

        columnAutosizing: false,
        headerTextWrapping: true,

        halign: 'left',
        renderFalsy: true,

        scrollbarHoverOff: 'visible',
        scrollbarHoverOver: 'visible',
        columnHeaderBackgroundColor: 'pink',

        checkboxOnlyRowSelections: true,

        autoSelectRows: true,

        rows: {
            header: {
                0: {
                    height: 40
                }
            }
        },

        calculators: {
            Add10: 'function(dataRow,columnName) { return dataRow[columnName] + 10; }'
        },

        columns: {
            height: {
                halign: 'right',
                format: 'foot'
            },

            /* eslint-disable camelcase */
            last_name: {
                columnHeaderBackgroundColor: '#142B6F', //dark blue
                columnHeaderColor: 'white',
                columnHeaderHalign: 'left',
                rightIcon: 'down-rectangle',
                link: true
            },

            first_name: {

            },

            total_number_of_pets_owned: {
                halign: 'center',
                format: 'number',
                calculator: 'Add10',
                color: 'green'
            },

            birthDate: {
                format: 'singdate',
                rightIcon: 'calendar',
                //strikeThrough: true
            },

            birthTime: {
                halign: 'right',
                editor: 'time',
                format: 'hhmm'
            },

            birthState: {
                editor: 'colortext',
                rightIcon: 'down-rectangle'
            },

            residenceState: {
                rightIcon: 'down-rectangle'
            },

            employed: {
                halign: 'right',
                renderer: 'button',
                backgroundColor: 'white'
            },

            income: {
                halign: 'right',
                format: 'pounds'
            },

            travel: {
                halign: 'right',
                format: 'francs'
            }
        },

        // Following `cells` example sets properties for a cell in the data subgrid.
        // Specifying cell properties here in grid state may be useful for static data subgrids
        // where cell coordinates are permanently assigned. Otherwise, for my dynamic grid data,
        // cell properties might more properly accompany the data itself as metadata
        // (i.e., as a hash in dataRow.__META[fieldName]).
        cells: {
            data: {
                16: {
                    height: {
                        font: '10pt Tahoma',
                        color: 'lightblue',
                        backgroundColor: 'red',
                        halign: 'left',
                        reapplyCellProperties: true
                    }
                }
            }
        }
    };

    grid.setState(state);

    grid.takeFocus();

    demo.resetDashboard();
};
<commit_msg>remove deprecated reapplyCellProperties usage from demo<commit_after>'use strict';

module.exports = function(demo, grid) {

    var idx = grid.behavior.columnEnum;

    var state = {
        columnIndexes: [
            idx.lastName,
            idx.totalNumberOfPetsOwned,
            idx.height,
            idx.birthDate,
            idx.birthTime,
            idx.birthState,
            // idx.residenceState,
            idx.employed,
            // idx.firstName,
            idx.income,
            idx.travel,
            // idx.squareOfIncome
        ],

        noDataMessage: 'No Data to Display',
        backgroundColor: 'white',
        font: 'normal small garamond',
        rowStripes: [
            undefined,
            undefined,
            undefined,
            { color: '#116611', backgroundColor: '#e8ffe8', font: 'italic small garamond' },
            { color: '#116611', backgroundColor: '#e8ffe8', font: 'italic small garamond' },
            { color: '#116611', backgroundColor: '#e8ffe8', font: 'italic small garamond' }
        ],

        fixedColumnCount: 1,
        fixedRowCount: 4,

        columnAutosizing: false,
        headerTextWrapping: true,

        halign: 'left',
        renderFalsy: true,

        scrollbarHoverOff: 'visible',
        scrollbarHoverOver: 'visible',
        columnHeaderBackgroundColor: 'pink',

        checkboxOnlyRowSelections: true,

        autoSelectRows: true,

        rows: {
            header: {
                0: {
                    height: 40
                }
            }
        },

        calculators: {
            Add10: 'function(dataRow,columnName) { return dataRow[columnName] + 10; }'
        },

        columns: {
            height: {
                halign: 'right',
                format: 'foot'
            },

            /* eslint-disable camelcase */
            last_name: {
                columnHeaderBackgroundColor: '#142B6F', //dark blue
                columnHeaderColor: 'white',
                columnHeaderHalign: 'left',
                rightIcon: 'down-rectangle',
                link: true
            },

            first_name: {

            },

            total_number_of_pets_owned: {
                halign: 'center',
                format: 'number',
                calculator: 'Add10',
                color: 'green'
            },

            birthDate: {
                format: 'singdate',
                rightIcon: 'calendar',
                //strikeThrough: true
            },

            birthTime: {
                halign: 'right',
                editor: 'time',
                format: 'hhmm'
            },

            birthState: {
                editor: 'colortext',
                rightIcon: 'down-rectangle'
            },

            residenceState: {
                rightIcon: 'down-rectangle'
            },

            employed: {
                halign: 'right',
                renderer: 'button',
                backgroundColor: 'white'
            },

            income: {
                halign: 'right',
                format: 'pounds'
            },

            travel: {
                halign: 'right',
                format: 'francs'
            }
        },

        // Following `cells` example sets properties for a cell in the data subgrid.
        // Specifying cell properties here in grid state may be useful for static data subgrids
        // where cell coordinates are permanently assigned. Otherwise, for my dynamic grid data,
        // cell properties might more properly accompany the data itself as metadata
        // (i.e., as a hash in dataRow.__META[fieldName]).
        cells: {
            data: {
                16: {
                    height: {
                        font: '10pt Tahoma',
                        color: 'lightblue',
                        backgroundColor: 'red',
                        halign: 'left'
                    }
                }
            }
        }
    };

    grid.setState(state);

    grid.takeFocus();

    demo.resetDashboard();
};
<|endoftext|>


<commit_before>import _ from 'underscore';
import $ from 'jquery';
import { GridView } from '../../js/vnext/grid-view.js';
import {
  odata,
  selection,
  columns,
  rows,
  columnGroup,
  cells,
} from '../../js/vnext/projection';
import './index.less';
import * as tmpl from './bodyTemplate.jade';
import 'bootstrap-webpack';

window.t = tmpl;
window.gridViewEl = new GridView({
  el: '.container-element-viewport',
  viewport: '.container-element-viewport',
  stickyHeader: true,
  virtualized: true,
}).pipeDataProjections([
  odata,
]).pipeStructureProjections([
  columns,
  rows,
  selection,
]).pipeContentProjections([
  columnGroup,
  cells,
]).set({
  url: 'http://services.odata.org/V4/Northwind/Northwind.svc/Orders',
  selection: true,
  columns: [{
    name: 'CustomerID',
    //template:tmpl.default,
  },{
    name: 'ShipCity',
  }],
}).render();

/*
window.gridViewWin = new GridView({
  el: '.container-window-viewport',
  stickyHeader: {
    offset() {
      return $('.navbar-container').height();
    },
  },
  virtualized: true,
}).pipeDataProjections([
  odata,
]).pipeStructureProjections([
  columns,
  rows,
  selection,
]).pipeContentProjections([
  columnGroup,
  cells,
]).set({
  url: 'http://services.odata.org/V4/Northwind/Northwind.svc/Orders',
  selection: { single: true },
}).render();
*/
<commit_msg>comment one text<commit_after>import _ from 'underscore';
import $ from 'jquery';
import { GridView } from '../../js/vnext/grid-view.js';
import {
  odata,
  selection,
  columns,
  rows,
  columnGroup,
  cells,
} from '../../js/vnext/projection';
import './index.less';
import * as tmpl from './bodyTemplate.jade';
import 'bootstrap-webpack';

window.gridViewEl = new GridView({
  el: '.container-element-viewport',
  viewport: '.container-element-viewport',
  stickyHeader: true,
  virtualized: true,
}).pipeDataProjections([
  odata,
]).pipeStructureProjections([
  columns,
  rows,
  selection,
]).pipeContentProjections([
  columnGroup,
  cells,
]).set({
  url: 'http://services.odata.org/V4/Northwind/Northwind.svc/Orders',
  selection: true,
  columns: [{
    name: 'CustomerID',
    //template:tmpl.default,
  },{
    name: 'ShipCity',
  }],
}).render();

/*
window.gridViewWin = new GridView({
  el: '.container-window-viewport',
  stickyHeader: {
    offset() {
      return $('.navbar-container').height();
    },
  },
  virtualized: true,
}).pipeDataProjections([
  odata,
]).pipeStructureProjections([
  columns,
  rows,
  selection,
]).pipeContentProjections([
  columnGroup,
  cells,
]).set({
  url: 'http://services.odata.org/V4/Northwind/Northwind.svc/Orders',
  selection: { single: true },
}).render();
*/
<|endoftext|>


<commit_before>// robot reducer
import path from 'path'
import {NAME, actionTypes, constants} from './'

// selector and reducer helpers
const getModuleState = (state) => state[NAME]

const makeRequestState = () => ({inProgress: false, error: null})

const handleRequest = (state, request, payload, error, props = {}) => ({
  ...state,
  ...props,
  [request]: {...state[request], inProgress: true, error: null}
})

const handleResponse = (state, request, payload, error, props = {}) => ({
  ...state,
  ...props,
  [request]: {...state[request], inProgress: false, error}
})

const INITIAL_STATE = {
  // robot connection and prep
  connectRequest: makeRequestState(),
  homeRequest: makeRequestState(),

  // robot protocol and running
  runRequest: makeRequestState(),
  pauseRequest: makeRequestState(),
  resumeRequest: makeRequestState(),
  cancelRequest: makeRequestState(),

  // instantaneous robot state below
  // protocol
  // TODO(mc, 2017-08-24): move protocol to its own state module
  // TODO(mc, 2017-08-23): DO NOT hardcode this protocol!
  protocol: path.join(
    __dirname,
    '../../../api/opentrons/server/tests/data/dinosaur.py'
  ),
  protocolError: null,
  // is connected to compute
  isConnected: false,
  // is running a protocol
  isRunning: false,
  isPaused: false,
  // protocol commands list
  commands: [],
  currentCommand: -1
}

export const selectors = {
  getProtocolFile (allState) {
    return getModuleState(allState).protocol
  },

  getProtocolName (allState) {
    return path.basename(selectors.getProtocolFile(allState))
  },

  getConnectionStatus (allState) {
    const state = getModuleState(allState)
    if (state.isConnected) return constants.CONNECTED
    if (state.connectRequest.inProgress) return constants.CONNECTING
    return constants.DISCONNECTED
  },

  getIsReadyToRun (allState) {
    const state = getModuleState(allState)
    return state.isConnected && (state.commands.length > 0)
  },

  getCommands (allState) {
    const state = getModuleState(allState)
    const currentCommand = state.currentCommand

    return state.commands.map((command, index) => ({
      // TODO(mc, 2017-08-23): generate command IDs on python side instead
      id: index,
      description: command,
      isCurrent: index === currentCommand
    }))
  },

  getRunProgress (allState) {
    const state = getModuleState(allState)

    return 100 * (state.currentCommand + 1) / state.commands.length
  }
}

export function reducer (state = INITIAL_STATE, action) {
  const {type, payload, error} = action

  switch (type) {
    case actionTypes.CONNECT:
      return handleRequest(state, 'connectRequest', payload, error)

    case actionTypes.CONNECT_RESPONSE:
      return handleResponse(state, 'connectRequest', payload, error, {
        isConnected: error == null
      })

    case actionTypes.HOME:
      return handleRequest(state, 'homeRequest', payload, error)

    case actionTypes.HOME_RESPONSE:
      return handleResponse(state, 'homeRequest', payload, error)

    // TODO(mc): for now, naively assume that if a run request is dispatched
    // the robot is running
    case actionTypes.RUN:
      return handleRequest(state, 'runRequest', payload, error, {
        isRunning: true,
        currentCommand: -1
      })

    case actionTypes.RUN_RESPONSE:
      return handleResponse(state, 'runRequest', payload, error, {
        isRunning: false
      })

    case actionTypes.PAUSE:
      return handleRequest(state, 'pauseRequest', payload, error)

    case actionTypes.PAUSE_RESPONSE:
      return handleResponse(state, 'pauseRequest', payload, error, {
        isPaused: error == null
      })

    case actionTypes.RESUME:
      return handleRequest(state, 'resumeRequest', payload, error)

    case actionTypes.RESUME_RESPONSE:
      return handleResponse(state, 'resumeRequest', payload, error, {
        isPaused: error != null
      })

    case actionTypes.CANCEL:
      return handleRequest(state, 'cancelRequest', payload, error)

    case actionTypes.CANCEL_RESPONSE:
      return handleResponse(state, 'cancelRequest', payload, error, {
        isRunning: error != null,
        isPaused: error != null
      })

    case actionTypes.SET_IS_CONNECTED:
      return {...state, ...payload}

    case actionTypes.SET_COMMANDS:
      return {...state, commands: payload.commands, currentCommand: -1}

    case actionTypes.SET_PROTOCOL_ERROR:
      return {...state, protocolError: error}

    case actionTypes.TICK_CURRENT_COMMAND:
      return {...state, currentCommand: state.currentCommand + 1}
  }

  return state
}
<commit_msg>reducer for returning current command index<commit_after>// robot reducer
import path from 'path'
import {NAME, actionTypes, constants} from './'

// selector and reducer helpers
const getModuleState = (state) => state[NAME]

const makeRequestState = () => ({inProgress: false, error: null})

const handleRequest = (state, request, payload, error, props = {}) => ({
  ...state,
  ...props,
  [request]: {...state[request], inProgress: true, error: null}
})

const handleResponse = (state, request, payload, error, props = {}) => ({
  ...state,
  ...props,
  [request]: {...state[request], inProgress: false, error}
})

const INITIAL_STATE = {
  // robot connection and prep
  connectRequest: makeRequestState(),
  homeRequest: makeRequestState(),

  // robot protocol and running
  runRequest: makeRequestState(),
  pauseRequest: makeRequestState(),
  resumeRequest: makeRequestState(),
  cancelRequest: makeRequestState(),

  // instantaneous robot state below
  // protocol
  // TODO(mc, 2017-08-24): move protocol to its own state module
  // TODO(mc, 2017-08-23): DO NOT hardcode this protocol!
  protocol: path.join(
    __dirname,
    '../../../api/opentrons/server/tests/data/dinosaur.py'
  ),
  protocolError: null,
  // is connected to compute
  isConnected: false,
  // is running a protocol
  isRunning: false,
  isPaused: false,
  // protocol commands list
  commands: [],
  currentCommand: -1
}

export const selectors = {
  getProtocolFile (allState) {
    return getModuleState(allState).protocol
  },

  getProtocolName (allState) {
    return path.basename(selectors.getProtocolFile(allState))
  },

  getConnectionStatus (allState) {
    const state = getModuleState(allState)
    if (state.isConnected) return constants.CONNECTED
    if (state.connectRequest.inProgress) return constants.CONNECTING
    return constants.DISCONNECTED
  },

  getIsReadyToRun (allState) {
    const state = getModuleState(allState)
    return state.isConnected && (state.commands.length > 0)
  },

  getCommands (allState) {
    const state = getModuleState(allState)
    const currentCommand = state.currentCommand

    return state.commands.map((command, index) => ({
      // TODO(mc, 2017-08-23): generate command IDs on python side instead
      id: index,
      description: command,
      isCurrent: index === currentCommand
    }))
  },

  getCurrentCommand (allState) {
    const state = getModuleState(allState)
    const currentCommand = state.currentCommand
    return currentCommand
  },

  getRunProgress (allState) {
    const state = getModuleState(allState)

    return 100 * (state.currentCommand + 1) / state.commands.length
  }
}

export function reducer (state = INITIAL_STATE, action) {
  const {type, payload, error} = action

  switch (type) {
    case actionTypes.CONNECT:
      return handleRequest(state, 'connectRequest', payload, error)

    case actionTypes.CONNECT_RESPONSE:
      return handleResponse(state, 'connectRequest', payload, error, {
        isConnected: error == null
      })

    case actionTypes.HOME:
      return handleRequest(state, 'homeRequest', payload, error)

    case actionTypes.HOME_RESPONSE:
      return handleResponse(state, 'homeRequest', payload, error)

    // TODO(mc): for now, naively assume that if a run request is dispatched
    // the robot is running
    case actionTypes.RUN:
      return handleRequest(state, 'runRequest', payload, error, {
        isRunning: true,
        currentCommand: -1
      })

    case actionTypes.RUN_RESPONSE:
      return handleResponse(state, 'runRequest', payload, error, {
        isRunning: false
      })

    case actionTypes.PAUSE:
      return handleRequest(state, 'pauseRequest', payload, error)

    case actionTypes.PAUSE_RESPONSE:
      return handleResponse(state, 'pauseRequest', payload, error, {
        isPaused: error == null
      })

    case actionTypes.RESUME:
      return handleRequest(state, 'resumeRequest', payload, error)

    case actionTypes.RESUME_RESPONSE:
      return handleResponse(state, 'resumeRequest', payload, error, {
        isPaused: error != null
      })

    case actionTypes.CANCEL:
      return handleRequest(state, 'cancelRequest', payload, error)

    case actionTypes.CANCEL_RESPONSE:
      return handleResponse(state, 'cancelRequest', payload, error, {
        isRunning: error != null,
        isPaused: error != null
      })

    case actionTypes.SET_IS_CONNECTED:
      return {...state, ...payload}

    case actionTypes.SET_COMMANDS:
      return {...state, commands: payload.commands, currentCommand: -1}

    case actionTypes.SET_PROTOCOL_ERROR:
      return {...state, protocolError: error}

    case actionTypes.TICK_CURRENT_COMMAND:
      return {...state, currentCommand: state.currentCommand + 1}
  }

  return state
}
<|endoftext|>


<commit_before>import Domify from 'react-domify';
import React from 'react';
import Markdown from 'react-markdown';

const Row = (props) => {
	const styles = {
		display: 'flex',
		flexWrap: 'wrap',
		marginLeft: -10,
		marginRight: -10,
		...props.style,
	};

	return (

	);
};

const Col = (props) => {
	const styles = {
		flex: props.width ? null : '1 1 0',
		minHeight: 1,
		paddingLeft: 10,
		paddingRight: 10,
		width: props.width || '100%',
		...props.style,
	};

	return (

	);
};

const ExplorerFieldType = React.createClass({
	getInitialState () {
		return {
			readmeIsVisible: !!this.props.readme,
			filter: this.props.FilterComponent.getDefaultValue(),
			value: this.props.value,
		};
	},
	componentWillReceiveProps (newProps) {
		if (this.props.params.type === newProps.params.type) return;

		this.setState({
			filter: newProps.FilterComponent.getDefaultValue(),
			readmeIsVisible: newProps.readme
				? this.state.readmeIsVisible
				: false,
			value: newProps.value,
		});
	},
	onFieldChange (e) {
		var logValue = typeof e.value === 'string' ? `"${e.value}"` : e.value;
		console.log(`${this.props.params.type} field value changed:`, logValue);
		this.setState({
			value: e.value,
		});
	},
	onFilterChange (value) {
		console.log(`${this.props.params.type} filter value changed:`, value);
		this.setState({
			filter: value,
		});
	},
	render () {
		const { FieldComponent, FilterComponent, readme, spec } = this.props;
		const { readmeIsVisible } = this.state;

		return (

{spec.label}

					{!!readme && (
						<button
							className="fx-page__header__button mega-octicon octicon-file-text"
							onClick={() => this.setState({ readmeIsVisible: !readmeIsVisible })}
							title={readmeIsVisible ? 'Hide Readme' : 'Show Readme'}
							type="button"
						/>
					)}

					<Row>
						<Col>

									<Row>
										<Col width={300}>
											<FieldComponent
												{...spec}
												onChange={this.onFieldChange}
												value={this.state.value}
											/>
										</Col>
										<Col>
											<Domify
												className="Domify"
												value={{ value: this.state.value }}
											/>
										</Col>
									</Row>

Filter

									<Row>
										<Col width={300}>
											<FilterComponent
												field={spec}
												filter={this.state.filter}
												onChange={this.onFilterChange}
											/>
										</Col>
										<Col>
											<Domify
												className="Domify"
												value={this.state.filter}
											/>
										</Col>
									</Row>

						</Col>
						{!!readmeIsVisible && (
							<Col width={380}>
								<Markdown
									className="Markdown"
									source={readme}
								/>
							</Col>
						)}
					</Row>

		);
	},
});

const STUB_README = `
# Boolean Field

Stores a \`Boolean\` in the model.

## Methods

### \`format\`

Returns the string \`"true"\` or \`"false"\`.

### \`updateItem\`

Because \`FormData\` can only submit \`String\` values, and HTML Forms won't submit fields at all when checkbox inputs are not checked, there are a few unusual behaviours when updating Boolean fields:

* The stored value is always updated, even when \`undefined\` is provided
* Any falsy value, including \`undefined\`, \`null\`, \`false\`, \`""\` and \`0\` will store \`false\`
* The string \`"false"\` will store \`false\`
* Any other truthy string will store \`true\`
* Any truthy number will store \`true\`
* The boolean \`true\` will store \`true\`

There is no way to use \`updateItem\` to remove Boolean field values from the item.

### \`validateInput\`

Ensures the value can be interpreted using the rules above. Other complex values (objects that aren't null, dates, arrays, etc) are not valid input.

### \`validateRequiredInput\`

Ensures the value matches one of the rules above that will store \`true\`
`;

module.exports = ExplorerFieldType;
<commit_msg>add margin to domify;<commit_after>import Domify from 'react-domify';
import React from 'react';
import Markdown from 'react-markdown';

const Row = (props) => {
	const styles = {
		display: 'flex',
		flexWrap: 'wrap',
		marginLeft: -10,
		marginRight: -10,
		...props.style,
	};

	return (

	);
};

const Col = (props) => {
	const styles = {
		flex: props.width ? null : '1 1 0',
		minHeight: 1,
		paddingLeft: 10,
		paddingRight: 10,
		width: props.width || '100%',
		...props.style,
	};

	return (

	);
};

const ExplorerFieldType = React.createClass({
	getInitialState () {
		return {
			readmeIsVisible: !!this.props.readme,
			filter: this.props.FilterComponent.getDefaultValue(),
			value: this.props.value,
		};
	},
	componentWillReceiveProps (newProps) {
		if (this.props.params.type === newProps.params.type) return;

		this.setState({
			filter: newProps.FilterComponent.getDefaultValue(),
			readmeIsVisible: newProps.readme
				? this.state.readmeIsVisible
				: false,
			value: newProps.value,
		});
	},
	onFieldChange (e) {
		var logValue = typeof e.value === 'string' ? `"${e.value}"` : e.value;
		console.log(`${this.props.params.type} field value changed:`, logValue);
		this.setState({
			value: e.value,
		});
	},
	onFilterChange (value) {
		console.log(`${this.props.params.type} filter value changed:`, value);
		this.setState({
			filter: value,
		});
	},
	render () {
		const { FieldComponent, FilterComponent, readme, spec } = this.props;
		const { readmeIsVisible } = this.state;

		return (

{spec.label}

					{!!readme && (
						<button
							className="fx-page__header__button mega-octicon octicon-file-text"
							onClick={() => this.setState({ readmeIsVisible: !readmeIsVisible })}
							title={readmeIsVisible ? 'Hide Readme' : 'Show Readme'}
							type="button"
						/>
					)}

					<Row>
						<Col>

									<Row>
										<Col width={300}>
											<FieldComponent
												{...spec}
												onChange={this.onFieldChange}
												value={this.state.value}
											/>
										</Col>
										<Col>

												<Domify
													className="Domify"
													value={{ value: this.state.value }}
												/>

										</Col>
									</Row>

Filter

									<Row>
										<Col width={300}>
											<FilterComponent
												field={spec}
												filter={this.state.filter}
												onChange={this.onFilterChange}
											/>
										</Col>
										<Col>

												<Domify
													className="Domify"
													value={this.state.filter}
												/>

										</Col>
									</Row>

						</Col>
						{!!readmeIsVisible && (
							<Col width={380}>
								<Markdown
									className="Markdown"
									source={readme}
								/>
							</Col>
						)}
					</Row>

		);
	},
});

const STUB_README = `
# Boolean Field

Stores a \`Boolean\` in the model.

## Methods

### \`format\`

Returns the string \`"true"\` or \`"false"\`.

### \`updateItem\`

Because \`FormData\` can only submit \`String\` values, and HTML Forms won't submit fields at all when checkbox inputs are not checked, there are a few unusual behaviours when updating Boolean fields:

* The stored value is always updated, even when \`undefined\` is provided
* Any falsy value, including \`undefined\`, \`null\`, \`false\`, \`""\` and \`0\` will store \`false\`
* The string \`"false"\` will store \`false\`
* Any other truthy string will store \`true\`
* Any truthy number will store \`true\`
* The boolean \`true\` will store \`true\`

There is no way to use \`updateItem\` to remove Boolean field values from the item.

### \`validateInput\`

Ensures the value can be interpreted using the rules above. Other complex values (objects that aren't null, dates, arrays, etc) are not valid input.

### \`validateRequiredInput\`

Ensures the value matches one of the rules above that will store \`true\`
`;

module.exports = ExplorerFieldType;
<|endoftext|>


<commit_before>var app = require('cantina');

module.exports = function (req, res, next) {

  // Set messages on the response.
  res.setMessage = function(message, type) {
    type = type || 'warning';
    req.session.messages || (req.session.messages = {});
    req.session.messages[type] || (req.session.messages[type] = []);
    req.session.messages[type].push(message);
  };
  // Messages are stored in session for redirect persistence.
  if (req.session && req.session.messages) {
    res.vars.messages = req.session.messages;
    delete req.session.messages;
  }

  next();
};<commit_msg>Whitespace<commit_after>var app = require('cantina');

module.exports = function (req, res, next) {

  // Set messages on the response.
  res.setMessage = function(message, type) {
    type = type || 'warning';
    req.session.messages || (req.session.messages = {});
    req.session.messages[type] || (req.session.messages[type] = []);
    req.session.messages[type].push(message);
  };
  // Messages are stored in session for redirect persistence.
  if (req.session && req.session.messages) {
    res.vars.messages = req.session.messages;
    delete req.session.messages;
  }

  next();
};<|endoftext|>


<commit_before>
/**
 * migration.js
 *
 * Standalone script to migrate database schema
 */

var co = require('co')
var databaseUrl = 'mongodb://localhost:27017/animeshot?w=1'
var mongo = require('yieldb').connect;

function *migration() {
	console.log('migration started')

	var db = yield mongo(databaseUrl)

	var Shots = db.col('shots')

	yield Shots.index({
		sid: 1
	}, {
		unique: true
	})
};

co(migration).then(function() {
	console.log('migration done')
	process.exit()
}).catch(function(err) {
	console.error(err.stack)
	process.exit()
});
<commit_msg>new migration tool<commit_after>
/**
 * migration.js
 *
 * Standalone script to migrate database schema
 */

var co = require('co')
var databaseUrl = 'mongodb://localhost:27017/animeshot?w=1'
var mongo = require('yieldb').connect;

function *migration() {
	console.log('migration started')

	var db = yield mongo(databaseUrl)

	var Shots = db.col('shots')

	yield Shots.index({
		sid: 1
	}, {
		unique: true
	})

	yield Shots.index({
		created: -1
	})
};

co(migration).then(function() {
	console.log('migration done')
	process.exit()
}).catch(function(err) {
	console.error(err.stack)
	process.exit()
});
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 - 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 router from './router'
import {monitorLtiMessages} from '@canvas/lti/jquery/messages'

router.start()
monitorLtiMessages()
<commit_msg>Fix flakey selenium specs for files<commit_after>/*
 * Copyright (C) 2014 - 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 router from './router'
import {monitorLtiMessages} from '@canvas/lti/jquery/messages'
import ready from '@instructure/ready'

ready(() => {
  router.start()
  monitorLtiMessages()
})
<|endoftext|>


<commit_before>/*
 * jQuery UI Selectmenu @VERSION
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectmenu
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 *	jquery.ui.button.js
 */
(function( $, undefined ) {

$.widget( "ui.selectmenu", {
	version: "@VERSION",
	defaultElement: "<select>",
	options: {
		dropdown: true,
		iconWidth: 26,
		wrapperElement: "
",
		appendTo: "body",
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		source: null,
		value: null,
		// callbacks
		open: null,
		focus: null,
		select: null,
		close: null
		// change: null
	},

	_create: function() {
		var self = this,
			options = this.options,
			tabindex = this.element.attr( 'tabindex' )
			// set a default id value, generate a new random one if not set by developer
			selectmenuId = self.element.attr( 'id' ) || 'ui-selectmenu-' + Math.random().toString( 16 ).slice( 2, 10 );

		// quick array of button and menu id's
		self.ids = [ selectmenuId + '-button', selectmenuId + '-menu' ];

		// get options
		self.items = self.element.find( 'option' );

		// set options
		options.value = self.element[0].value;
		options.disabled = ( self.element.attr( 'disabled' ) ) ? true : false;

		// catch click event of the label
		self.element.bind( 'click.selectmenu',  function() {
			self.newelement.focus();
			return false;
		})
		.hide();

		// create button
		self.newelement = $( '', {
				href: '#' + selectmenuId,
				tabindex: ( tabindex ? tabindex : self.element.attr( 'disabled' ) ? 1 : 0 ),
				id: self.ids[ 0 ],
				css: {
					width: self.element.width()
				},
				'aria-disabled': options.disabled,
				'aria-owns': self.ids[ 1 ],
				'aria-haspopup': true
			})
			.addClass( self.widgetBaseClass + '-button' )
			.button({
				label: self.items.eq( this.element[0].selectedIndex ).text(),
				icons: {
					primary: ( options.dropdown ? 'ui-icon-triangle-1-s' : 'ui-icon-triangle-2-n-s' )
				}
			});

		self.newelementWrap = $( options.wrapperElement )
			.append( self.newelement )
			.insertAfter( self.element );

		self.newelement.bind({
			'mousedown.selectmenu': function( event ) {
				self._toggle( event );
				return false;
			},
			'click.selectmenu': function() {
				return false;
			},
			'keydown.selectmenu': function( event ) {
				var ret = true;
				switch (event.keyCode) {
					case $.ui.keyCode.TAB:
						if ( self.opened ) self.close();
						break;
					case $.ui.keyCode.ENTER:
						if ( self.opened ) self.list.menu( "select", self._getSelectedItem() );
						ret = false;
						break;
					case $.ui.keyCode.SPACE:
						self._toggle(event);
						ret = false;
						break;
					case $.ui.keyCode.UP:
						if ( event.altKey ) {
							self._toggle( event );
						} else {
							self._move( "previous", event );
						}
						ret = false;
						break;
					case $.ui.keyCode.DOWN:
						if ( event.altKey ) {
							self._toggle( event );
						} else {
							self._move( "next", event );
						}
						ret = false;
						break;
					case $.ui.keyCode.LEFT:
						self._move( "previous", event );
						ret = false;
						break;
					case $.ui.keyCode.RIGHT:
						self._move( "next", event );
						ret = false;
						break;
					default:
						self.list.trigger( event );
						ret = false;
				}
				return ret;
			}
		});

		// built menu
		self.refresh();

		// document click closes menu
		$( document ).bind( 'mousedown.selectmenu', function( event ) {
			if ( self.opened && !self.hover) {
				window.setTimeout( function() {
					self.close( event );
				}, 200 );
			}
		});
	},

	refresh: function() {
		var self = this,
			options = this.options;

		// create menu portion, append to body
		self.list = $( '
', {
			'class': 'ui-widget ui-widget-content',
			'aria-hidden': true,
			'aria-labelledby': self.ids[0],
			role: 'listbox',
			id: self.ids[1]
		});

		self.listWrap = $( options.wrapperElement )
			.addClass( self.widgetBaseClass + '-menu' )
			.css( "width", ( options.dropdown ) ? self.element.width() : self.element.width() - options.iconWidth )
			.append( self.list )
			.appendTo( options.appendTo );

		self._initSource();
		self._renderMenu( self.list, options.source );

		self.list
			.data( 'element.selectelemenu', self.element )
			.menu({
				select: function( event, ui ) {
					var item = ui.item.data( "item.selectmenu" );
					self._setSelected( event, item );
					item.element = $ ( self.items[ item.index ] );
					self._trigger( "select", event, { item: item } );
					self.close( event, true);
				},
				focus: function( event, ui ) {
					var item = ui.item.data( "item.selectmenu" );
					if ( !self.opened ) self._setSelected( event, item );
					self._trigger( "focus", event, { item: item } );
				}
			})
			.bind({
				'mouseenter.selectelemenu': function() {
					self.hover = true;
				},
				'mouseleave .selectelemenu': function() {
					self.hover = false;
				}
			});

		// adjust ARIA
		self.list.find( "li" ).not( '.ui-selectmenu-optgroup' ).find( 'a' ).attr( 'role', 'option' );

		if ( options.dropdown ) {
			self.list
				.addClass( 'ui-corner-bottom' )
				.removeClass( 'ui-corner-all' );
		}

		// transfer disabled state
		if ( self.element.attr( 'disabled' ) ) {
			self.disable();
		} else {
			self.enable()
		}
	},

	open: function( event ) {
		var self = this,
			options = this.options,
			currentItem = self._getSelectedItem();

		if ( !options.disabled ) {
			// close all other selectmenus
			$( '.' + self.widgetBaseClass + '-open' ).not( self.newelement ).each( function() {
				$( this ).children( 'ul.ui-menu' ).data( 'element.selectelemenu' ).selectmenu( 'close' );
			});

			if ( options.dropdown ) {
				self.newelement
					.addClass( 'ui-corner-top' )
					.removeClass( 'ui-corner-all' );
			}

			self.listWrap.addClass( self.widgetBaseClass + '-open' );
			self.list.menu( "focus", null, currentItem );

			if ( !options.dropdown ) {
				// center current item
				if ( self.list.css("overflow") == "auto" ) {
					self.list.scrollTop( self.list.scrollTop() + currentItem.position().top - self.list.outerHeight()/2 + currentItem.outerHeight()/2 );
				}
				// calculate offset
				var _offset = (self.list.offset().top  - currentItem.offset().top + (self.newelement.outerHeight() - currentItem.outerHeight()) / 2);
				$.extend( options.position, {
					my: "left top",
					at: "left top",
					offset: "0 " + _offset
				});
			}

			self.listWrap.position( $.extend({
				of: this.newelementWrap
			}, options.position ));

			this.opened = true;
			self._trigger( "open", event );
		}
	},

	close: function( event, focus ) {
		var self = this,
			options = this.options;

		if ( options.dropdown ) {
			self.newelement
				.addClass( 'ui-corner-all' )
				.removeClass( 'ui-corner-top' );
		}

		self.listWrap.removeClass( self.widgetBaseClass + '-open' );
		this.opened = false;

		if (focus) self.newelement.focus();

		self._trigger( "close", event );
	},

	_renderMenu: function( ul, items ) {
		var self = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			if ( item.optgroup != currentOptgroup ) {
				var optgroup = $( '
' + item.optgroup + '
' );
				if ( $( self.items[ item.index ] ).parent( "optgroup" ).attr( "disabled" ) ) optgroup.addClass( 'ui-state-disabled' );
				ul.append( optgroup );
				currentOptgroup = item.optgroup;
			}
			self._renderItem( ul, item );
		});
	},

	_renderItem: function( ul, item) {
		return $( "
" )
			.addClass( ( item.disabled ) ? 'ui-state-disabled' : '' )
			.data( "item.selectmenu", item )
			.append( $( "", {
					text: item.label,
					href: '#',
					click: function( event ) {
						event.preventDefault();
					}
				})
			).appendTo( ul );
	},

	_move: function( key, event ) {
		if ( !this.opened ) {
			this.list.menu( "focus", event, this._getSelectedItem() );
		}
		this.list.menu( key, event );
		if ( !this.opened ) {
			this.list.menu( "select", event  );
		}
	},

	_getSelectedItem: function() {
		return this.list.find( "li" ).not( '.ui-selectmenu-optgroup' ).eq( this.element[0].selectedIndex );
	},

	_setSelected: function( event, item ) {
		this.newelement.children( '.ui-button-text' ).text( item.label );
		this.element[0].selectedIndex = item.index;
		this.options.value = item.value;
	},

	_toggle: function( event ) {
		if ( this.opened ) {
			this.close( event );
		} else {
			this.open( event );
		}
	},

	_setOption: function( key, value ) {
		this._super( "_setOption", key, value );
		if ( key === "appendTo" ) {
			this.listWrap.appendTo( $( value || "body", this.element[0].ownerDocument )[0] );
		}
		if ( key === "value" && value) {
			this.element[0].value = value;
		}
		if ( key === "disabled" ) {
			this.newelement.button( "option", "disabled", value );
			this.list.attr( "aria-disabled", value );
			this.close();
		}
	},

	_initSource: function() {
		if ( !$.isArray( this.options.source ) ) {
			var data = [];
			$.each( this.items, function( index, item ) {
				var option = $( item ),
					optgroup = option.parent( "optgroup" );
				data.push({
					index: index,
					value: option.attr( 'value' ),
					label: option.text(),
					optgroup: optgroup.attr("label") || false,
					disabled: optgroup.attr( "disabled" ) || option.attr( "disabled" )
				});
			});
			this.options.source = data;
		}
	},

	_destroy: function() {
		this.listWrap.remove();
		this.newelementWrap.remove();
		this.element.show().unbind( '.selectmenu' );
		$( document ).unbind( '.selectmenu' );
	}
});

}( jQuery ));
<commit_msg>Selectmenu: “eats” too many key strokes when focused<commit_after>/*
 * jQuery UI Selectmenu @VERSION
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectmenu
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 *	jquery.ui.button.js
 */
(function( $, undefined ) {

$.widget( "ui.selectmenu", {
	version: "@VERSION",
	defaultElement: "<select>",
	options: {
		dropdown: true,
		iconWidth: 26,
		wrapperElement: "
",
		appendTo: "body",
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		source: null,
		value: null,
		// callbacks
		open: null,
		focus: null,
		select: null,
		close: null
		// change: null
	},

	_create: function() {
		var self = this,
			options = this.options,
			tabindex = this.element.attr( 'tabindex' )
			// set a default id value, generate a new random one if not set by developer
			selectmenuId = self.element.attr( 'id' ) || 'ui-selectmenu-' + Math.random().toString( 16 ).slice( 2, 10 );

		// quick array of button and menu id's
		self.ids = [ selectmenuId + '-button', selectmenuId + '-menu' ];

		// get options
		self.items = self.element.find( 'option' );

		// set options
		options.value = self.element[0].value;
		options.disabled = ( self.element.attr( 'disabled' ) ) ? true : false;

		// catch click event of the label
		self.element.bind( 'click.selectmenu',  function() {
			self.newelement.focus();
			return false;
		})
		.hide();

		// create button
		self.newelement = $( '', {
				href: '#' + selectmenuId,
				tabindex: ( tabindex ? tabindex : self.element.attr( 'disabled' ) ? 1 : 0 ),
				id: self.ids[ 0 ],
				css: {
					width: self.element.width()
				},
				'aria-disabled': options.disabled,
				'aria-owns': self.ids[ 1 ],
				'aria-haspopup': true
			})
			.addClass( self.widgetBaseClass + '-button' )
			.button({
				label: self.items.eq( this.element[0].selectedIndex ).text(),
				icons: {
					primary: ( options.dropdown ? 'ui-icon-triangle-1-s' : 'ui-icon-triangle-2-n-s' )
				}
			});

		self.newelementWrap = $( options.wrapperElement )
			.append( self.newelement )
			.insertAfter( self.element );

		self.newelement.bind({
			'mousedown.selectmenu': function( event ) {
				self._toggle( event );
				return false;
			},
			'click.selectmenu': function() {
				return false;
			},
			'keydown.selectmenu': function( event ) {
				var ret = true;
				switch (event.keyCode) {
					case $.ui.keyCode.TAB:
						if ( self.opened ) self.close();
						break;
					case $.ui.keyCode.ENTER:
						if ( self.opened ) self.list.menu( "select", self._getSelectedItem() );
						ret = false;
						break;
					case $.ui.keyCode.SPACE:
						self._toggle(event);
						ret = false;
						break;
					case $.ui.keyCode.UP:
						if ( event.altKey ) {
							self._toggle( event );
						} else {
							self._move( "previous", event );
						}
						ret = false;
						break;
					case $.ui.keyCode.DOWN:
						if ( event.altKey ) {
							self._toggle( event );
						} else {
							self._move( "next", event );
						}
						ret = false;
						break;
					case $.ui.keyCode.LEFT:
						self._move( "previous", event );
						ret = false;
						break;
					case $.ui.keyCode.RIGHT:
						self._move( "next", event );
						ret = false;
						break;
					default:
						self.list.trigger( event );
				}
				return ret;
			}
		});

		// built menu
		self.refresh();

		// document click closes menu
		$( document ).bind( 'mousedown.selectmenu', function( event ) {
			if ( self.opened && !self.hover) {
				window.setTimeout( function() {
					self.close( event );
				}, 200 );
			}
		});
	},

	refresh: function() {
		var self = this,
			options = this.options;

		// create menu portion, append to body
		self.list = $( '
', {
			'class': 'ui-widget ui-widget-content',
			'aria-hidden': true,
			'aria-labelledby': self.ids[0],
			role: 'listbox',
			id: self.ids[1]
		});

		self.listWrap = $( options.wrapperElement )
			.addClass( self.widgetBaseClass + '-menu' )
			.css( "width", ( options.dropdown ) ? self.element.width() : self.element.width() - options.iconWidth )
			.append( self.list )
			.appendTo( options.appendTo );

		self._initSource();
		self._renderMenu( self.list, options.source );

		self.list
			.data( 'element.selectelemenu', self.element )
			.menu({
				select: function( event, ui ) {
					var item = ui.item.data( "item.selectmenu" );
					self._setSelected( event, item );
					item.element = $ ( self.items[ item.index ] );
					self._trigger( "select", event, { item: item } );
					self.close( event, true);
				},
				focus: function( event, ui ) {
					var item = ui.item.data( "item.selectmenu" );
					if ( !self.opened ) self._setSelected( event, item );
					self._trigger( "focus", event, { item: item } );
				}
			})
			.bind({
				'mouseenter.selectelemenu': function() {
					self.hover = true;
				},
				'mouseleave .selectelemenu': function() {
					self.hover = false;
				}
			});

		// adjust ARIA
		self.list.find( "li" ).not( '.ui-selectmenu-optgroup' ).find( 'a' ).attr( 'role', 'option' );

		if ( options.dropdown ) {
			self.list
				.addClass( 'ui-corner-bottom' )
				.removeClass( 'ui-corner-all' );
		}

		// transfer disabled state
		if ( self.element.attr( 'disabled' ) ) {
			self.disable();
		} else {
			self.enable()
		}
	},

	open: function( event ) {
		var self = this,
			options = this.options,
			currentItem = self._getSelectedItem();

		if ( !options.disabled ) {
			// close all other selectmenus
			$( '.' + self.widgetBaseClass + '-open' ).not( self.newelement ).each( function() {
				$( this ).children( 'ul.ui-menu' ).data( 'element.selectelemenu' ).selectmenu( 'close' );
			});

			if ( options.dropdown ) {
				self.newelement
					.addClass( 'ui-corner-top' )
					.removeClass( 'ui-corner-all' );
			}

			self.listWrap.addClass( self.widgetBaseClass + '-open' );
			self.list.menu( "focus", null, currentItem );

			if ( !options.dropdown ) {
				// center current item
				if ( self.list.css("overflow") == "auto" ) {
					self.list.scrollTop( self.list.scrollTop() + currentItem.position().top - self.list.outerHeight()/2 + currentItem.outerHeight()/2 );
				}
				// calculate offset
				var _offset = (self.list.offset().top  - currentItem.offset().top + (self.newelement.outerHeight() - currentItem.outerHeight()) / 2);
				$.extend( options.position, {
					my: "left top",
					at: "left top",
					offset: "0 " + _offset
				});
			}

			self.listWrap.position( $.extend({
				of: this.newelementWrap
			}, options.position ));

			this.opened = true;
			self._trigger( "open", event );
		}
	},

	close: function( event, focus ) {
		var self = this,
			options = this.options;

		if ( options.dropdown ) {
			self.newelement
				.addClass( 'ui-corner-all' )
				.removeClass( 'ui-corner-top' );
		}

		self.listWrap.removeClass( self.widgetBaseClass + '-open' );
		this.opened = false;

		if (focus) self.newelement.focus();

		self._trigger( "close", event );
	},

	_renderMenu: function( ul, items ) {
		var self = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			if ( item.optgroup != currentOptgroup ) {
				var optgroup = $( '
' + item.optgroup + '
' );
				if ( $( self.items[ item.index ] ).parent( "optgroup" ).attr( "disabled" ) ) optgroup.addClass( 'ui-state-disabled' );
				ul.append( optgroup );
				currentOptgroup = item.optgroup;
			}
			self._renderItem( ul, item );
		});
	},

	_renderItem: function( ul, item) {
		return $( "
" )
			.addClass( ( item.disabled ) ? 'ui-state-disabled' : '' )
			.data( "item.selectmenu", item )
			.append( $( "", {
					text: item.label,
					href: '#',
					click: function( event ) {
						event.preventDefault();
					}
				})
			).appendTo( ul );
	},

	_move: function( key, event ) {
		if ( !this.opened ) {
			this.list.menu( "focus", event, this._getSelectedItem() );
		}
		this.list.menu( key, event );
		if ( !this.opened ) {
			this.list.menu( "select", event  );
		}
	},

	_getSelectedItem: function() {
		return this.list.find( "li" ).not( '.ui-selectmenu-optgroup' ).eq( this.element[0].selectedIndex );
	},

	_setSelected: function( event, item ) {
		this.newelement.children( '.ui-button-text' ).text( item.label );
		this.element[0].selectedIndex = item.index;
		this.options.value = item.value;
	},

	_toggle: function( event ) {
		if ( this.opened ) {
			this.close( event );
		} else {
			this.open( event );
		}
	},

	_setOption: function( key, value ) {
		this._super( "_setOption", key, value );
		if ( key === "appendTo" ) {
			this.listWrap.appendTo( $( value || "body", this.element[0].ownerDocument )[0] );
		}
		if ( key === "value" && value) {
			this.element[0].value = value;
		}
		if ( key === "disabled" ) {
			this.newelement.button( "option", "disabled", value );
			this.list.attr( "aria-disabled", value );
			this.close();
		}
	},

	_initSource: function() {
		if ( !$.isArray( this.options.source ) ) {
			var data = [];
			$.each( this.items, function( index, item ) {
				var option = $( item ),
					optgroup = option.parent( "optgroup" );
				data.push({
					index: index,
					value: option.attr( 'value' ),
					label: option.text(),
					optgroup: optgroup.attr( "label" ) || false,
					disabled: optgroup.attr( "disabled" ) || option.attr( "disabled" )
				});
			});
			this.options.source = data;
		}
	},

	_destroy: function() {
		this.listWrap.remove();
		this.newelementWrap.remove();
		this.element.show().unbind( '.selectmenu' );
		$( document ).unbind( '.selectmenu' );
	}
});

}( jQuery ));
<|endoftext|>