<commit_before>/* eslint-disable jsx-a11y/href-no-hash */
import React from 'react'
import PropTypes from 'prop-types'
import { get, has } from 'lodash/object'
import { isNil } from 'lodash/lang'
import moment from 'moment'
import {
  USER_BACKGROUND_OPTION_CUSTOM,
  USER_BACKGROUND_OPTION_COLOR,
  USER_BACKGROUND_OPTION_PHOTO,
  USER_BACKGROUND_OPTION_DAILY
} from '../../constants'
import {
  getUserBackgroundOption,
  getUserBackgroundCustomImage,
  getUserBackgroundColor,
  getUserBackgroundImageURL,
  setBackgroundSettings,
  setExtensionBackgroundSettings
} from 'utils/local-bkg-settings'
import SetBackgroundDailyImageMutation from 'mutations/SetBackgroundDailyImageMutation'
import FadeBackgroundAnimation from '../Background/FadeBackgroundAnimation'

/*
 * We want to load the background as quickly as possible,
 * so this component interacts with local storage and the
 * parent frame (the browser extension's local new tab page).
 * On mount, it tries to load background settings from
 * local storage; if settings exist in local storage, it posts
 * a message to the parent frame to make sure the background
 * settings used by the extension new tab page are up to date.
 * When the user data returns from the server, if the user's
 * background settings are different from those in local
 * storage, it updates the component state and messages the parent
 * frame. If the user's settings from the server are identical,
 * it should not update state or message the parent frame.
*/
class UserBackgroundImage extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      // The image we are currently showing (or fading in to show).
      currentlyDisplayedImgURL: null,
      // Whether the user's image has preloaded successfully.
      nextImgPreloaded: false,
      // Whether there was a problem preloading the user's image.
      imgPreloadError: false,
      // The user's background settings. They might not reflect what
      // background is currently displayed because we want to fully
      // preload images before displaying them.
      backgroundOption: getUserBackgroundOption(),
      customImage: getUserBackgroundCustomImage(),
      backgroundColor: getUserBackgroundColor(),
      backgroundImageURL: getUserBackgroundImageURL()
    }
  }

  componentDidMount () {
    // If the props contain valid settings for the background,
    // and they are different from what's already in state,
    // update the background settings values.
    if (this.arePropsReady(this.props) &&
      this.arePropsDifferentFromState(this.props)) {
      this.updateBackgroundSettings(this.props)
      this.getNewDailyImageIfNeeded(this.props)
    }

    // If there's background settings in local storage,
    // call the parent frame to update the extension's
    // background settings.
    if (this.stateHasBackgroundSettings()) {
      setExtensionBackgroundSettings(
        this.state.backgroundOption,
        this.state.customImage,
        this.state.backgroundColor,
        this.state.backgroundImageURL)
    }
  }

  componentWillReceiveProps (nextProps) {
    // Only update state and saved background settings if the
    // props contain different values.
    if (this.arePropsReady(nextProps) &&
      this.arePropsDifferentFromState(nextProps)) {
      this.updateBackgroundSettings(nextProps)
      this.getNewDailyImageIfNeeded(nextProps)
    }
  }

  // Determine if the state has values to render the background.
  stateHasBackgroundSettings (props) {
    return !isNil(this.state.backgroundOption)
  }

  // Determine if the props have the values we need to render
  // the background.
  arePropsReady (props) {
    return (
      has(props, ['user', 'backgroundOption']) &&
      has(props, ['user', 'customImage']) &&
      has(props, ['user', 'backgroundColor']) &&
      has(props, ['user', 'backgroundImage', 'imageURL']) &&
      has(props, ['user', 'backgroundImage', 'timestamp'])
    )
  }

  // Determine if the props hold background settings that are
  // different from the background settings currently in state.
  arePropsDifferentFromState (props) {
    return (
      get(props, ['user', 'backgroundOption']) !== this.state.backgroundOption ||
      get(props, ['user', 'customImage']) !== this.state.customImage ||
      get(props, ['user', 'backgroundColor']) !== this.state.backgroundColor ||
      get(props, ['user', 'backgroundImage', 'imageURL']) !== this.state.backgroundImageURL
    )
  }

  updateBackgroundSettings (props) {
    let backgroundOption = get(props, ['user', 'backgroundOption'])
    let customImage = get(props, ['user', 'customImage'])
    let backgroundColor = get(props, ['user', 'backgroundColor'])
    let backgroundImageURL = get(props, ['user', 'backgroundImage', 'imageURL'])
    this.setState({
      nextImgPreloaded: false, // reset because of new image
      backgroundOption: backgroundOption,
      customImage: customImage,
      backgroundColor: backgroundColor,
      backgroundImageURL: backgroundImageURL
    })
    setBackgroundSettings(backgroundOption,
      customImage, backgroundColor, backgroundImageURL)
  }

  getNewDailyImageIfNeeded (props) {
    if (props.user.backgroundOption !== USER_BACKGROUND_OPTION_DAILY) {
      return
    }

    const shouldChangeBackgroundImg = (
      // FIXME: daily
      moment().utc().diff(
        moment(props.user.backgroundImage.timestamp), 'seconds') > 2
    )

    // Fetch a new background image for today.
    if (shouldChangeBackgroundImg) {
      SetBackgroundDailyImageMutation(
        props.relay.environment,
        props.user.id
      )
    }
  }

  onImgLoad (e) {
    this.setState({
      nextImgPreloaded: true,
      currentlyDisplayedImgURL: this.getImgURL()
    })
  }

  onImgError (e) {
    this.setState({
      imgPreloadError: true
    })
    this.props.showError('We could not load your background image.')
  }

  /**
   * Get the image URL if the user has an image background.
   * @return {String|null} The image URL if one exists, or null
   *   if the user does not have an image background.
   */
  getImgURL () {
    const backgroundOption = this.state.backgroundOption
    switch (backgroundOption) {
      case USER_BACKGROUND_OPTION_CUSTOM:
        return this.state.customImage
      case USER_BACKGROUND_OPTION_PHOTO:
        return this.state.backgroundImageURL
      case USER_BACKGROUND_OPTION_DAILY:
        return this.state.backgroundImageURL
      default:
        return null
    }
  }

  isImgBackground () {
    return [
      USER_BACKGROUND_OPTION_CUSTOM,
      USER_BACKGROUND_OPTION_DAILY,
      USER_BACKGROUND_OPTION_PHOTO
    ].indexOf(this.state.backgroundOption) > -1
  }

  getBackgroundStyle () {
    const defaultStyle = {
      boxShadow: 'rgba(0, 0, 0, 0.5) 0px 0px 120px inset',
      backgroundRepeat: 'no-repeat',
      backgroundPosition: 'center',
      backgroundAttachment: 'fixed',
      WebkitBackgroundSize: 'cover',
      MozBackgroundSize: 'cover',
      backgroundSize: 'cover',
      position: 'absolute',
      top: 0,
      bottom: 0,
      right: 0,
      left: 0,
      width: '100%',
      height: '100%',
      boxSizing: 'border-box',
      zIndex: 'auto'
    }

    var backgroundStyle = {}

    // What we use if a property is missing.
    const styleOnError = {
      backgroundColor: '#4a90e2'
    }
    const backgroundOption = this.state.backgroundOption
    switch (backgroundOption) {
      case USER_BACKGROUND_OPTION_CUSTOM:
        if (this.state.customImage) {
          backgroundStyle = {
            backgroundImage: 'url(' + this.state.customImage + ')'
          }
        } else {
          backgroundStyle = styleOnError
        }
        break
      case USER_BACKGROUND_OPTION_COLOR:
        if (this.state.backgroundColor) {
          backgroundStyle = {
            backgroundColor: this.state.backgroundColor
          }
        } else {
          backgroundStyle = styleOnError
        }
        break
      case USER_BACKGROUND_OPTION_PHOTO:
        if (this.state.backgroundImageURL) {
          backgroundStyle = {
            backgroundImage: 'url(' + this.state.backgroundImageURL + ')'
          }
        } else {
          backgroundStyle = styleOnError
        }
        break
      case USER_BACKGROUND_OPTION_DAILY:
        if (this.state.backgroundImageURL) {
          backgroundStyle = {
            backgroundImage: 'url(' + this.state.backgroundImageURL + ')'
          }
        } else {
          backgroundStyle = styleOnError
        }
        break
      default:
        backgroundStyle = {
          backgroundImage: 'none',
          backgroundColor: 'transparent'
        }
        break
    }
    if (this.state.imgPreloadError) {
      backgroundStyle = styleOnError
    }
    return Object.assign({}, defaultStyle, backgroundStyle)
  }

  render () {
    const backgroundStyle = this.getBackgroundStyle()
    const isImgBackground = this.isImgBackground()
    const imgUrl = this.getImgURL()

    // FIXME: React key should not be img URL (need keys for non-img backgrounds too)
    // FIXME: first image can sometimes show before it's rendered; e.g. on a hard
    //   refresh. Need to wait for img load before running animation.
    // FIXME: image fade out delay does not work properly when changing background
    //   settings from a photo to color. Replace with new image fade in delay?

    // For image backgrounds, we use an img element to "preload"
    // the image before displaying the background.
    return (

        <FadeBackgroundAnimation>

            <div
              data-test-id={'background-tint-overlay'}
              style={{
                position: 'absolute',
                top: 0,
                bottom: 0,
                right: 0,
                left: 0,
                width: '100%',
                height: '100%',
                boxSizing: 'border-box',
                zIndex: 'auto',
                // Needs to match shading in extension new tab page.
                backgroundColor: `rgba(0, 0, 0, ${isImgBackground ? 0.15 : 0.03})`
              }} />

        </FadeBackgroundAnimation>
        { (isImgBackground && !this.state.nextImgPreloaded)
          ?
          : null
        }

    )
  }
}

UserBackgroundImage.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.string,
    backgroundOption: PropTypes.string,
    customImage: PropTypes.string,
    backgroundColor: PropTypes.string,
    backgroundImage: PropTypes.shape({
      imageURL: PropTypes.string,
      timestamp: PropTypes.string
    })
  }),
  showError: PropTypes.func,
  relay: PropTypes.shape({
    environment: PropTypes.object.isRequired
  })
}

UserBackgroundImage.defaultProps = {
  showError: () => {}
}

export default UserBackgroundImage
<commit_msg>Fix image fade on load<commit_after>/* eslint-disable jsx-a11y/href-no-hash */
import React from 'react'
import PropTypes from 'prop-types'
import { get, has } from 'lodash/object'
import { isNil } from 'lodash/lang'
import moment from 'moment'
import {
  USER_BACKGROUND_OPTION_CUSTOM,
  USER_BACKGROUND_OPTION_COLOR,
  USER_BACKGROUND_OPTION_PHOTO,
  USER_BACKGROUND_OPTION_DAILY
} from '../../constants'
import {
  getUserBackgroundOption,
  getUserBackgroundCustomImage,
  getUserBackgroundColor,
  getUserBackgroundImageURL,
  setBackgroundSettings,
  setExtensionBackgroundSettings
} from 'utils/local-bkg-settings'
import SetBackgroundDailyImageMutation from 'mutations/SetBackgroundDailyImageMutation'
import FadeBackgroundAnimation from '../Background/FadeBackgroundAnimation'

/*
 * We want to load the background as quickly as possible,
 * so this component interacts with local storage and the
 * parent frame (the browser extension's local new tab page).
 * On mount, it tries to load background settings from
 * local storage; if settings exist in local storage, it posts
 * a message to the parent frame to make sure the background
 * settings used by the extension new tab page are up to date.
 * When the user data returns from the server, if the user's
 * background settings are different from those in local
 * storage, it updates the component state and messages the parent
 * frame. If the user's settings from the server are identical,
 * it should not update state or message the parent frame.
*/
class UserBackgroundImage extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      // The image we are currently showing (or fading in to show).
      currentlyDisplayedImgURL: null,
      // Whether the user's image has preloaded successfully.
      nextImgPreloaded: false,
      // Whether there was a problem preloading the user's image.
      imgPreloadError: false,
      // The user's background settings. They might not reflect what
      // background is currently displayed because we want to fully
      // preload images before displaying them.
      backgroundOption: getUserBackgroundOption(),
      customImage: getUserBackgroundCustomImage(),
      backgroundColor: getUserBackgroundColor(),
      backgroundImageURL: getUserBackgroundImageURL()
    }
  }

  componentDidMount () {
    // If the props contain valid settings for the background,
    // and they are different from what's already in state,
    // update the background settings values.
    if (this.arePropsReady(this.props) &&
      this.arePropsDifferentFromState(this.props)) {
      this.updateBackgroundSettings(this.props)
      this.getNewDailyImageIfNeeded(this.props)
    }

    // If there's background settings in local storage,
    // call the parent frame to update the extension's
    // background settings.
    if (this.stateHasBackgroundSettings()) {
      setExtensionBackgroundSettings(
        this.state.backgroundOption,
        this.state.customImage,
        this.state.backgroundColor,
        this.state.backgroundImageURL)
    }
  }

  componentWillReceiveProps (nextProps) {
    // Only update state and saved background settings if the
    // props contain different values.
    if (this.arePropsReady(nextProps) &&
      this.arePropsDifferentFromState(nextProps)) {
      this.updateBackgroundSettings(nextProps)
      this.getNewDailyImageIfNeeded(nextProps)
    }
  }

  // Determine if the state has values to render the background.
  stateHasBackgroundSettings (props) {
    return !isNil(this.state.backgroundOption)
  }

  // Determine if the props have the values we need to render
  // the background.
  arePropsReady (props) {
    return (
      has(props, ['user', 'backgroundOption']) &&
      has(props, ['user', 'customImage']) &&
      has(props, ['user', 'backgroundColor']) &&
      has(props, ['user', 'backgroundImage', 'imageURL']) &&
      has(props, ['user', 'backgroundImage', 'timestamp'])
    )
  }

  // Determine if the props hold background settings that are
  // different from the background settings currently in state.
  arePropsDifferentFromState (props) {
    return (
      get(props, ['user', 'backgroundOption']) !== this.state.backgroundOption ||
      get(props, ['user', 'customImage']) !== this.state.customImage ||
      get(props, ['user', 'backgroundColor']) !== this.state.backgroundColor ||
      get(props, ['user', 'backgroundImage', 'imageURL']) !== this.state.backgroundImageURL
    )
  }

  updateBackgroundSettings (props) {
    let backgroundOption = get(props, ['user', 'backgroundOption'])
    let customImage = get(props, ['user', 'customImage'])
    let backgroundColor = get(props, ['user', 'backgroundColor'])
    let backgroundImageURL = get(props, ['user', 'backgroundImage', 'imageURL'])
    this.setState({
      nextImgPreloaded: false, // reset because of new image
      backgroundOption: backgroundOption,
      customImage: customImage,
      backgroundColor: backgroundColor,
      backgroundImageURL: backgroundImageURL
    })
    setBackgroundSettings(backgroundOption,
      customImage, backgroundColor, backgroundImageURL)
  }

  getNewDailyImageIfNeeded (props) {
    if (props.user.backgroundOption !== USER_BACKGROUND_OPTION_DAILY) {
      return
    }

    const shouldChangeBackgroundImg = (
      // FIXME: daily
      moment().utc().diff(
        moment(props.user.backgroundImage.timestamp), 'seconds') > 2
    )

    // Fetch a new background image for today.
    if (shouldChangeBackgroundImg) {
      SetBackgroundDailyImageMutation(
        props.relay.environment,
        props.user.id
      )
    }
  }

  onImgLoad (e) {
    this.setState({
      nextImgPreloaded: true,
      currentlyDisplayedImgURL: this.getImgURL()
    })
  }

  onImgError (e) {
    this.setState({
      imgPreloadError: true
    })
    this.props.showError('We could not load your background image.')
  }

  /**
   * Get the image URL if the user has an image background. Note:
   * this is the user's current setting but not necessarily the image
   * we are currently displaying, because we'll wait to preload the
   * image first.
   * @return {String|null} The image URL if one exists, or null
   *   if the user does not have an image background.
   */
  getImgURL () {
    const backgroundOption = this.state.backgroundOption
    switch (backgroundOption) {
      case USER_BACKGROUND_OPTION_CUSTOM:
        return this.state.customImage
      case USER_BACKGROUND_OPTION_PHOTO:
        return this.state.backgroundImageURL
      case USER_BACKGROUND_OPTION_DAILY:
        return this.state.backgroundImageURL
      default:
        return null
    }
  }

  isImgBackground () {
    return [
      USER_BACKGROUND_OPTION_CUSTOM,
      USER_BACKGROUND_OPTION_DAILY,
      USER_BACKGROUND_OPTION_PHOTO
    ].indexOf(this.state.backgroundOption) > -1
  }

  getBackgroundStyle () {
    const defaultStyle = {
      boxShadow: 'rgba(0, 0, 0, 0.5) 0px 0px 120px inset',
      backgroundRepeat: 'no-repeat',
      backgroundPosition: 'center',
      backgroundAttachment: 'fixed',
      WebkitBackgroundSize: 'cover',
      MozBackgroundSize: 'cover',
      backgroundSize: 'cover',
      position: 'absolute',
      top: 0,
      bottom: 0,
      right: 0,
      left: 0,
      width: '100%',
      height: '100%',
      boxSizing: 'border-box',
      zIndex: 'auto'
    }

    var backgroundStyle = {}

    // What we use if a property is missing.
    const styleOnError = {
      backgroundColor: '#4a90e2'
    }
    const backgroundOption = this.state.backgroundOption
    const currentlyDisplayedImgURL = this.state.currentlyDisplayedImgURL
    switch (backgroundOption) {
      case USER_BACKGROUND_OPTION_CUSTOM:
        if (this.state.customImage) {
          backgroundStyle = {
            backgroundImage: `url(${currentlyDisplayedImgURL})`
          }
        } else {
          backgroundStyle = styleOnError
        }
        break
      case USER_BACKGROUND_OPTION_COLOR:
        if (this.state.backgroundColor) {
          backgroundStyle = {
            backgroundColor: this.state.backgroundColor
          }
        } else {
          backgroundStyle = styleOnError
        }
        break
      case USER_BACKGROUND_OPTION_PHOTO:
        if (this.state.backgroundImageURL) {
          backgroundStyle = {
            backgroundImage: `url(${currentlyDisplayedImgURL})`
          }
        } else {
          backgroundStyle = styleOnError
        }
        break
      case USER_BACKGROUND_OPTION_DAILY:
        if (this.state.backgroundImageURL) {
          backgroundStyle = {
            backgroundImage: `url(${currentlyDisplayedImgURL})`
          }
        } else {
          backgroundStyle = styleOnError
        }
        break
      default:
        backgroundStyle = {
          backgroundImage: 'none',
          backgroundColor: 'transparent'
        }
        break
    }
    if (this.state.imgPreloadError) {
      backgroundStyle = styleOnError
    }
    return Object.assign({}, defaultStyle, backgroundStyle)
  }

  render () {
    const backgroundStyle = this.getBackgroundStyle()
    const isImgBackground = this.isImgBackground()
    const imgUrl = this.getImgURL()

    // FIXME: vertical scroll on page causes clock movement
    // FIXME: image fade out delay does not work properly when changing background
    //   settings from a photo to color. Replace with new image fade in delay?

    // React key for the background element
    var backgroundKey = ``
    if (isImgBackground) {
      backgroundKey = `img-${this.state.currentlyDisplayedImgURL}`
    } else {
      backgroundKey = `color-${this.state.backgroundColor}`
    }

    // Only initially show the background if the image has already loaded.
    const showBackground = isImgBackground ? this.state.currentlyDisplayedImgURL !== null : true

    // For image backgrounds, we use an img element to "preload"
    // the image before displaying the background.
    return (

        { showBackground ? (
          <FadeBackgroundAnimation>

              <div
                data-test-id={'background-tint-overlay'}
                style={{
                  position: 'absolute',
                  top: 0,
                  bottom: 0,
                  right: 0,
                  left: 0,
                  width: '100%',
                  height: '100%',
                  boxSizing: 'border-box',
                  zIndex: 'auto',
                  // Needs to match shading in extension new tab page.
                  backgroundColor: `rgba(0, 0, 0, ${isImgBackground ? 0.15 : 0.03})`
                }} />

          </FadeBackgroundAnimation>
        ) : null
        }
        { (isImgBackground && !this.state.nextImgPreloaded)
          ?
          : null
        }

    )
  }
}

UserBackgroundImage.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.string,
    backgroundOption: PropTypes.string,
    customImage: PropTypes.string,
    backgroundColor: PropTypes.string,
    backgroundImage: PropTypes.shape({
      imageURL: PropTypes.string,
      timestamp: PropTypes.string
    })
  }),
  showError: PropTypes.func,
  relay: PropTypes.shape({
    environment: PropTypes.object.isRequired
  })
}

UserBackgroundImage.defaultProps = {
  showError: () => {}
}

export default UserBackgroundImage
<|endoftext|>


<commit_before>'use strict';

angular.module('app').controller('queryCtrl', [
  '$scope',
  '$timeout',
  '$rootScope',
  'alertService',
  'modalService',
  function($scope, $timeout, $rootScope, alertService, modalService) {
    const queryModule = require('lib/modules/query');
    const mongoUtils = require('src/lib/utils/mongoUtils');

    if (!$scope.database) throw new Error('database is required for database query directive');
    if (!$scope.database.collections || !$scope.database.collections.length) throw new Error('database must have collections for database query directive');

    $scope.loading = false;
    $scope.queryTime = null;
    $scope.editorHandle = {};
    $scope.codeEditorOptions = {};
    $scope.codeEditorCustomData = {
      collectionNames: _.pluck($scope.database.collections, 'name')
    };

    $scope.results = [];
    $scope.keyValueResults = [];

    $scope.deleteResult = _deleteResult;
    $scope.getPropertyTypeIcon = _getPropertyTypeIcons;

    let defaultCollection = $scope.defaultCollection ? _.findWhere($scope.database.collections, {
      name: $scope.defaultCollection
    }) : null;

    defaultCollection = defaultCollection || $scope.database.collections[0];

    var defaultQuery = 'db.' + defaultCollection.name.toLowerCase() + '.find({\n  \n})';

    $scope.changeTabName = function(name) {
      if (!name || !$scope.databaseTab) return;
      $scope.databaseTab.name = name.length > 20 ? (name.substring(0, 20) + '...') : name;
    };

    $scope.changeTabName(defaultQuery);

    $scope.form = {
      query: defaultQuery,
      skip: 0,
      limit: 50
    };

    $scope.runQuery = function() {
      _runQuery($scope.form.query);
    };

    $scope.VIEWS = {
      RAW: 'LIST',
      KEYVALUE: 'KEYVALUE'
    };
    $scope.currentView = $scope.VIEWS.LIST;

    _runQuery(defaultQuery);

    $scope.autoformat = function() {
      if ($scope.editorHandle.autoformat) {
        $scope.editorHandle.autoformat();
      }
    };

    $scope.editorHasFocus = false;

    $scope.$watch('editorHasFocus', function(val) {
      if (val) {
        //make some functions available on the root scope when the editor gets focus,
        //used for keybindings
        $rootScope.currentQuery = {
          runQuery: $scope.runQuery,
          autoformat: $scope.autoformat
        };
      } else {
        $rootScope.currentQuery = null;
      }
    });

    $scope.$watch('results', function(val) {
      if (val && _.isArray(val)) {
        $scope.keyValueResults = _convertResultsToKeyValueResults(val);
      }
    });

    $scope.exportResults = function() {
      modalService.openQueryResultsExport($scope.currentCollection, $scope.exportQuery);
    };

    function _runQuery(rawQuery) {
      $scope.loading = true;
      $scope.error = null;

      $scope.changeTabName(rawQuery);

      if (!queryModule.isValidQuery(rawQuery)) {
        $scope.error = 'Sorry, ' + rawQuery + ' is not a valid query';
        $scope.loading = false;
        return;
      }

      var collectionName = queryModule.parseCollectionName(rawQuery);

      var collection = _getCollectionByNameFromRawQuery(collectionName, rawQuery);

      if (!collection) {
        $scope.error = 'Sorry, ' + collectionName + ' is not a valid collection name';
        $scope.loading = false;
        return;
      }

      $scope.currentCollection = collection;

      let query = null;

      queryModule.createQuery(rawQuery)
        .then((_query) => {
          query = _query;

          $scope.exportQuery = query.query; //used by the query-results-export directive

          return collection.execQuery(query);
        })
        .then((result) => {
          $timeout(() => {
            $scope.loading = false;
            $scope.queryTime = result.time;
            $scope.results = result.result;

            if (query.mongoMethod !== 'find') {
              alertService.success(query.mongoMethod + ' was successful');

              _runQuery('db.' + $scope.currentCollection.name + '.find()');
            }
          });
        })
        .catch((error) => {
          $timeout(() => {
            $scope.error = error && error.message ? error.message : error;
            $scope.loading = false;
          });
        });
    }

    function _deleteResult(result) {
      if (!result) return;

      modalService.confirm({
        message: 'Are you sure you want to delete this record?',
        confirmButtonMessage: 'Yes',
        cancelButtonMessage: 'No'
      }).result.then(function() {
        $scope.currentCollection.deleteById(result._id)
          .then(() => {
            $timeout(() => {
              alertService.success('Delete successful');

              _runQuery('db.' + $scope.currentCollection.name + '.find()');
            });
          })
          .catch((error) => {
            $timeout(() => {
              $scope.error = error && error.message ? error.message : error;
              $scope.loading = false;
            });
          });
      });
    }

    function _getCollectionByNameFromRawQuery(collectionName, rawQuery) {
      if (!collectionName) return null;

      return _.find($scope.database.collections, function(collection) {
        return collection.name && collection.name.toLowerCase && collection.name.toLowerCase() === collectionName.toLowerCase() ? true : false;
      });
    }

    function _getPropertyTypeIcons(propertyType) {
      var icon;

      switch (propertyType) {
        case 'number':
          icon = '';
          break;
        case 'string':
          icon = 'fa-quote-left';
          break;
        case 'boolean':
          icon = 'fa-calendar';
          break;
        case 'date':
          icon = 'fa-calendar';
          break;
        case 'array':
          icon = 'fa-calendar';
          break;
        case 'objectId':
          icon = 'fa-cog';
          break;
      }

      return icon;
    }

    function _convertResultsToKeyValueResults(results) {
      return results.map(function(result) {
        var props = [];
        props._id = result._id;

        for (var key in result) {
          //TODO: if it's a nested object then recurse and generate key/value for all of it's props
          props.push({
            _id: result[key] ? result[key]._id : result[key],
            key: key,
            value: result[key],
            type: _getPropertyType(result[key])
          });
        }

        return props;
      });
    }

    function _getPropertyType(property) {
      if (_.isNumber(property)) return 'number';
      if (_.isString(property)) return 'string';
      if (_.isArray(property)) return 'array';
      if (_.isDate(property)) return 'date';
      if (_.isBoolean(property)) return 'boolean';
      if (mongoUtils.isObjectId(property)) return 'objectId';
    }
  }
]);
<commit_msg>js hint<commit_after>'use strict';

angular.module('app').controller('queryCtrl', [
  '$scope',
  '$timeout',
  '$rootScope',
  'alertService',
  'modalService',
  function($scope, $timeout, $rootScope, alertService, modalService) {
    const queryModule = require('lib/modules/query');
    const mongoUtils = require('src/lib/utils/mongoUtils');

    if (!$scope.database) throw new Error('database is required for database query directive');
    if (!$scope.database.collections || !$scope.database.collections.length) throw new Error('database must have collections for database query directive');

    $scope.loading = false;
    $scope.queryTime = null;
    $scope.editorHandle = {};
    $scope.codeEditorOptions = {};
    $scope.codeEditorCustomData = {
      collectionNames: _.pluck($scope.database.collections, 'name')
    };

    $scope.results = [];
    $scope.keyValueResults = [];

    $scope.deleteResult = _deleteResult;
    $scope.getPropertyTypeIcon = _getPropertyTypeIcons;

    let defaultCollection = $scope.defaultCollection ? _.findWhere($scope.database.collections, {
      name: $scope.defaultCollection
    }) : null;

    defaultCollection = defaultCollection || $scope.database.collections[0];

    var defaultQuery = 'db.' + defaultCollection.name.toLowerCase() + '.find({\n  \n})';

    $scope.changeTabName = function(name) {
      if (!name || !$scope.databaseTab) return;
      $scope.databaseTab.name = name.length > 20 ? (name.substring(0, 20) + '...') : name;
    };

    $scope.changeTabName(defaultQuery);

    $scope.form = {
      query: defaultQuery,
      skip: 0,
      limit: 50
    };

    $scope.runQuery = function() {
      _runQuery($scope.form.query);
    };

    $scope.VIEWS = {
      RAW: 'LIST',
      KEYVALUE: 'KEYVALUE'
    };
    $scope.currentView = $scope.VIEWS.LIST;

    _runQuery(defaultQuery);

    $scope.autoformat = function() {
      if ($scope.editorHandle.autoformat) {
        $scope.editorHandle.autoformat();
      }
    };

    $scope.editorHasFocus = false;

    $scope.$watch('editorHasFocus', function(val) {
      if (val) {
        //make some functions available on the root scope when the editor gets focus,
        //used for keybindings
        $rootScope.currentQuery = {
          runQuery: $scope.runQuery,
          autoformat: $scope.autoformat
        };
      } else {
        $rootScope.currentQuery = null;
      }
    });

    $scope.$watch('results', function(val) {
      if (val && _.isArray(val)) {
        $scope.keyValueResults = _convertResultsToKeyValueResults(val);
      }
    });

    $scope.exportResults = function() {
      modalService.openQueryResultsExport($scope.currentCollection, $scope.exportQuery);
    };

    function _runQuery(rawQuery) {
      $scope.loading = true;
      $scope.error = null;

      $scope.changeTabName(rawQuery);

      if (!queryModule.isValidQuery(rawQuery)) {
        $scope.error = 'Sorry, ' + rawQuery + ' is not a valid query';
        $scope.loading = false;
        return;
      }

      var collectionName = queryModule.parseCollectionName(rawQuery);

      var collection = _getCollectionByNameFromRawQuery(collectionName);

      if (!collection) {
        $scope.error = 'Sorry, ' + collectionName + ' is not a valid collection name';
        $scope.loading = false;
        return;
      }

      $scope.currentCollection = collection;

      let query = null;

      queryModule.createQuery(rawQuery)
        .then((_query) => {
          query = _query;

          $scope.exportQuery = query.query; //used by the query-results-export directive

          return collection.execQuery(query);
        })
        .then((result) => {
          $timeout(() => {
            $scope.loading = false;
            $scope.queryTime = result.time;
            $scope.results = result.result;

            if (query.mongoMethod !== 'find') {
              alertService.success(query.mongoMethod + ' was successful');

              _runQuery('db.' + $scope.currentCollection.name + '.find()');
            }
          });
        })
        .catch((error) => {
          $timeout(() => {
            $scope.error = error && error.message ? error.message : error;
            $scope.loading = false;
          });
        });
    }

    function _deleteResult(result) {
      if (!result) return;

      modalService.confirm({
        message: 'Are you sure you want to delete this record?',
        confirmButtonMessage: 'Yes',
        cancelButtonMessage: 'No'
      }).result.then(function() {
        $scope.currentCollection.deleteById(result._id)
          .then(() => {
            $timeout(() => {
              alertService.success('Delete successful');

              _runQuery('db.' + $scope.currentCollection.name + '.find()');
            });
          })
          .catch((error) => {
            $timeout(() => {
              $scope.error = error && error.message ? error.message : error;
              $scope.loading = false;
            });
          });
      });
    }

    function _getCollectionByNameFromRawQuery(collectionName) {
      if (!collectionName) return null;

      return _.find($scope.database.collections, function(collection) {
        return collection.name && collection.name.toLowerCase && collection.name.toLowerCase() === collectionName.toLowerCase() ? true : false;
      });
    }

    function _getPropertyTypeIcons(propertyType) {
      var icon;

      switch (propertyType) {
        case 'number':
          icon = '';
          break;
        case 'string':
          icon = 'fa-quote-left';
          break;
        case 'boolean':
          icon = 'fa-calendar';
          break;
        case 'date':
          icon = 'fa-calendar';
          break;
        case 'array':
          icon = 'fa-calendar';
          break;
        case 'objectId':
          icon = 'fa-cog';
          break;
      }

      return icon;
    }

    function _convertResultsToKeyValueResults(results) {
      return results.map(function(result) {
        var props = [];
        props._id = result._id;

        for (var key in result) {
          //TODO: if it's a nested object then recurse and generate key/value for all of it's props
          props.push({
            _id: result[key] ? result[key]._id : result[key],
            key: key,
            value: result[key],
            type: _getPropertyType(result[key])
          });
        }

        return props;
      });
    }

    function _getPropertyType(property) {
      if (_.isNumber(property)) return 'number';
      if (_.isString(property)) return 'string';
      if (_.isArray(property)) return 'array';
      if (_.isDate(property)) return 'date';
      if (_.isBoolean(property)) return 'boolean';
      if (mongoUtils.isObjectId(property)) return 'objectId';
    }
  }
]);
<|endoftext|>


<commit_before>// TEMPORARY TEST SERVER - ISSUER SIMULATOR

'use strict'

let Hapi = require('hapi');
const Good = require('good');

// Create a server with a host and port
const server = new Hapi.Server();
server.connection({
	host: 'localhost',
	port: 8002
});

// Add the route for response (test)
server.route({
	method: 'POST',
	path:'/api/issuer/{id?}',
	handler: function (request, reply) {
		var data = JSON.parse('{"acquirerInfo": {"orderId": "1","timestamp": "25.01.2016 22:31:33"},"issuerInfo": {"id": "123456","orderId": "12","timestamp": "25.01.2016 22:31:36","transactionAmount": "5000.00"},"transactionStatus": {"code": "00","message": "TRANSACTION_COMPLETED"}}');
		console.log(data);
		reply(data);
	}
});

// Log
server.register({
	register: Good,
	options: {
		reporters: [{
			reporter: require('good-console'),
			events: {
				response: '*',
				log: '*',
				error: '*',
				info: '*'
			}
		}]
	}
}, (err) => {
	if (err) {
		throw err; // Failed loading "good" plugin!
	}

	// Start the server
	server.start((err) => {
		if (err) {
			throw err;
		}
		console.log('Issuer server running at:', server.info.uri);
	});
});
<commit_msg>Issuer response format fix<commit_after>// TEMPORARY TEST SERVER - ISSUER SIMULATOR

'use strict'

let Hapi = require('hapi');
const Good = require('good');

// Create a server with a host and port
const server = new Hapi.Server();
server.connection({
	host: 'localhost',
	port: 8002
});

// Add the route for response (test)
server.route({
	method: 'POST',
	path:'/api/issuer/{id?}',
	handler: function (request, reply) {
		var data = JSON.parse('{"acquirerInfo": {"orderId": "1","timestamp": "25.01.2016 22:31:33"},"issuerInfo": {"orderId": "12","timestamp": "25.01.2016 22:31:36"},"transactionStatus": {"code": "00","message": "TRANSACTION_COMPLETED"}}');
		console.log(data);
		reply(data);
	}
});

// Log
server.register({
	register: Good,
	options: {
		reporters: [{
			reporter: require('good-console'),
			events: {
				response: '*',
				log: '*',
				error: '*',
				info: '*'
			}
		}]
	}
}, (err) => {
	if (err) {
		throw err; // Failed loading "good" plugin!
	}

	// Start the server
	server.start((err) => {
		if (err) {
			throw err;
		}
		console.log('Issuer server running at:', server.info.uri);
	});
});
<|endoftext|>


<commit_before>import { get, getAll, update, search } from '../BooksAPI';

const example_books = {
  books: [
    {
      title: "Book 1",
      subtitle: "Subtitle 1",
      description: "lorem ipsum",
      authors: ["Guy One", "Guy Two"],
      imageLinks: { thumbnail: "pic.url" }
    },
    {
      title: "Book 2",
      subtitle: "Subtitle 2",
      description: "dolor sit amet",
      authors: ["Guy Three", "Guy Four"],
      imageLinks: { thumbnail: "pic2.url" }
    },
  ]
}

const headers = new Headers({"Accept": "application/json", "Content-Type":  "application/json"});

it('fetches correct data', () => {
  fetch.mockResponse(JSON.stringify(example_books), { headers });
  getAll().then(returnedBooks => {
    expect(returnedBooks).toBe(example_books.books)
  });
});
<commit_msg>refactor BooksAPI tests<commit_after>import { get, getAll, update, search } from '../BooksAPI';

const example_books = {
  books: [
    {
      title: "Book 1",
      subtitle: "Subtitle 1",
      description: "lorem ipsum",
      authors: ["Guy One", "Guy Two"],
      imageLinks: { thumbnail: "pic.url" }
    },
    {
      title: "Book 2",
      subtitle: "Subtitle 2",
      description: "dolor sit amet",
      authors: ["Guy Three", "Guy Four"],
      imageLinks: { thumbnail: "pic2.url" }
    },
  ]
}

const headers = new Headers({"Accept": "application/json", "Content-Type":  "application/json"});

it('fetches correct data', () => {
  fetch.mockResponse(JSON.stringify(example_books), { headers });
  return getAll().then(returnedBooks => {
    expect(returnedBooks).toEqual(example_books.books)
  });
});
<|endoftext|>


<commit_before>import { nameIndex, traverse, traverseInit } from '../traverse';

jest.unmock('../traverse');
jest.unmock('ramda');

const part = 'people[1]';

const partLong = 'doges[1337]';

describe('#nameIndex', () => {
  it('should partition part correctly', () => {
    const { name, index } = nameIndex(part);

    expect(name).toBe('people');
    expect(index).toBe(1);
  });

  it('should partition part with big index correctly', () => {
    const { name, index } = nameIndex(partLong);

    expect(name).toBe('doges');
    expect(index).toBe(1337);
  });
});

const flat = {
  people: [{
    value: 'test',
  }, {
    value: 'test2',
  }],
};

const deep = {
  doges: [{
    value: 'doge',
    foods: [{
      value: 'bone',
      nutrition: [{
        value: '1337kcal',
      }, {
        value: '420kcal',
      }],
    }],
  }],
};

describe('#traverse', () => {
  it('should find a field in flat fields', () => {
    const result = traverse(flat, 'people[1]');

    expect(result).toEqual({ value: 'test2' });
  });

  it('should not find a field in flat fields', () => {
    const result = traverse(flat, 'people[2]');

    expect(result).toBeNull();
  });

  it('should find a field in deep fields', () => {
    const result = traverse(deep, 'doges[0].foods[0].nutrition[1]');

    expect(result).toEqual({ value: '420kcal' });
  });

  it('should not find a field in deep fields', () => {
    const result = traverse(deep, 'doges[0].people[0].nutrition[1]');

    expect(result).toBeNull();
  });
});

describe('#traverseTail', () => {
  it('should not find a field in flat fields', () => {
    const result = traverseInit(flat, 'people[1]');

    expect(result).toBeNull();
  });

  it('should find a field in fields', () => {
    const result = traverseInit(deep, 'doges[0].foods[0].nutrition[1]');

    expect(result).toEqual({
      value: 'bone',
      nutrition: [{
        value: '1337kcal',
      }, {
        value: '420kcal',
      }],
    });
  });

  it('should not find a field in fields', () => {
    const result = traverseInit(deep, 'doges[0].people[0].nutrition[1]');

    expect(result).toBeNull();
  });
});
<commit_msg>name tests<commit_after>import { nameIndex, traverse, traverseInit } from '../traverse';

jest.unmock('../traverse');
jest.unmock('ramda');

const part = 'people[1]';

const partLong = 'doges[1337]';

describe('#nameIndex', () => {
  it('should partition part correctly', () => {
    const { name, index } = nameIndex(part);

    expect(name).toBe('people');
    expect(index).toBe(1);
  });

  it('should partition part with big index correctly', () => {
    const { name, index } = nameIndex(partLong);

    expect(name).toBe('doges');
    expect(index).toBe(1337);
  });
});

const flat = {
  people: [{
    value: 'test',
  }, {
    value: 'test2',
  }],
};

const deep = {
  doges: [{
    value: 'doge',
    foods: [{
      value: 'bone',
      nutrition: [{
        value: '1337kcal',
      }, {
        value: '420kcal',
      }],
    }],
  }],
};

describe('#traverse', () => {
  it('should find a field in flat fields', () => {
    const result = traverse(flat, 'people[1]');

    expect(result).toEqual({ value: 'test2' });
  });

  it('should not find a field in flat fields', () => {
    const result = traverse(flat, 'people[2]');

    expect(result).toBeNull();
  });

  it('should find a field in deep fields', () => {
    const result = traverse(deep, 'doges[0].foods[0].nutrition[1]');

    expect(result).toEqual({ value: '420kcal' });
  });

  it('should not find a field in deep fields', () => {
    const result = traverse(deep, 'doges[0].people[0].nutrition[1]');

    expect(result).toBeNull();
  });
});

describe('#traverseInit', () => {
  it('should not find a field in flat fields', () => {
    const result = traverseInit(flat, 'people[1]');

    expect(result).toBeNull();
  });

  it('should find a field in deep fields', () => {
    const result = traverseInit(deep, 'doges[0].foods[0].nutrition[1]');

    expect(result).toEqual({
      value: 'bone',
      nutrition: [{
        value: '1337kcal',
      }, {
        value: '420kcal',
      }],
    });
  });

  it('should not find a field in deep fields', () => {
    const result = traverseInit(deep, 'doges[0].people[0].nutrition[1]');

    expect(result).toBeNull();
  });
});
<|endoftext|>


<commit_before>var fs = require('fs'),
	path = require('path'),
	U = require('../Alloy/utils'),
	_ = require('../Alloy/lib/alloy/underscore')._,
	CONST = require('../Alloy/common/constants'),
	logger = require('../Alloy/common/logger');

// Fix node warning
path.existsSync = fs.existsSync || path.existsSync;

var	wrench = require('wrench'),
	spawn = require('child_process').spawn,
	harnessTemplatePath = path.join(process.cwd(),'test','projects','HarnessTemplate'),
	harnessAppPath = path.join(process.cwd(),'test','projects','Harness'),
	targetAppPath = path.join(harnessAppPath,'app'),
	resourcesPath = path.join(harnessAppPath,'Resources');

function log(message) {
	if (!process.env.quiet) {
		console.log(message);
	}
}

//trim extra whitespace output
function filterLog(line) {
	line = U.trim(line);
	if (!line) return;

	var lines = line.split('\n');
	if (lines.length > 1) {
		_.each(lines,function(l) {
			filterLog(l);
		});
		return;
	}
	var idx = line.indexOf(' -- [');
	if (idx > 0) {
		var idx2 = line.indexOf(']', idx+7);
		line = line.substring(idx2+1);
	}
	if (line.charAt(0)=='[') {
		var i = line.indexOf(']');
		var label = line.substring(1,i);
		var rest = U.trim(line.substring(i+1));
		if (!rest) return;
		switch(label) {
			case 'INFO':
				logger.info(rest);
				return;
			case 'TRACE':
			case 'DEBUG':
				logger.debug(rest);
				return;
			case 'WARN':
				logger.warn(rest);
				return;
			case 'ERROR':
				logger.error(rest);
				return;
		}
	}
	logger.debug(line);
}

function runApp() {
	log('Running sample app "'+process.env.dir+'"...');

	// create array for `titanium build` args
	var e = process.env;
	var newArgs = ['build',
		'--project-dir', harnessAppPath,
		'--platform', e.platform || 'ios'
	];
	e.tiversion && (newArgs = newArgs.concat(['--sdk',e.tiversion]));
	e.simtype && (newArgs = newArgs.concat(['--sim-type',e.simtype]));

	//run stdout/stderr back through console.log
	var runcmd = spawn('titanium', newArgs);
	runcmd.stdout.on('data', function (data) {
		filterLog(data);
	});

	runcmd.stderr.on('data', function (data) {
		filterLog(data);
	});
}

namespace('app', function() {
	desc('remove the contents of the test harness\' "app" directory');
	task('clobber', function() {
		log('Reseting the Harness app from template...');
		wrench.rmdirSyncRecursive(harnessAppPath, true);
		wrench.mkdirSyncRecursive(harnessAppPath, 0777);
		wrench.copyDirSyncRecursive(harnessTemplatePath, harnessAppPath);
	});

	desc('compile the example app in the given directory name and stage for launch, e.g. "jake app:setup dir=masterdetail"');
	task('setup', ['app:clobber'], function() {
		log('Initializing Alloy project...');
		if (!path.existsSync(resourcesPath)) {
			wrench.mkdirSyncRecursive(resourcesPath, 0777);
		}
		require('child_process').exec('alloy new -f "' + harnessAppPath + '"', function(error, stdout, stderr) {
			if (error) {
				console.log(stdout);
				console.log(stderr);
				console.log(error);
				process.exit(1);
			} else {
				log('Staging sample app "'+process.env.dir+'" for launch...');
				wrench.copyDirSyncRecursive(path.join(process.cwd(), 'test', 'apps', process.env.dir), targetAppPath, {preserve:true});
			}
		});
	});

	desc('run an example, all but dir are optional: e.g. "jake app:run dir=masterdetail platform=android tiversion=2.0.2.GA tisdk=<path to sdk>"');
	task('run', ['app:setup'], function() {
		runApp();
	});

	desc('copy a sample app\'s "app" folder into the Harness');
	task('quickrun', function() {
		log('Quick-running sample app "' + process.env.dir + '"...');
		log('Staging sample app "' + process.env.dir + '" for launch...');
		wrench.copyDirSyncRecursive(path.join(process.cwd(), 'test', 'apps', process.env.dir), targetAppPath, {preserve:true});
		runApp();
	});
});
<commit_msg>copy testing libs to test apps when running<commit_after>var fs = require('fs'),
	path = require('path'),
	U = require('../Alloy/utils'),
	_ = require('../Alloy/lib/alloy/underscore')._,
	CONST = require('../Alloy/common/constants'),
	logger = require('../Alloy/common/logger');

// Fix node warning
path.existsSync = fs.existsSync || path.existsSync;

var	wrench = require('wrench'),
	spawn = require('child_process').spawn,
	harnessTemplatePath = path.join(process.cwd(),'test','projects','HarnessTemplate'),
	harnessAppPath = path.join(process.cwd(),'test','projects','Harness'),
	targetAppPath = path.join(harnessAppPath,'app'),
	resourcesPath = path.join(harnessAppPath,'Resources');

function log(message) {
	if (!process.env.quiet) {
		console.log(message);
	}
}

//trim extra whitespace output
function filterLog(line) {
	line = U.trim(line);
	if (!line) return;

	var lines = line.split('\n');
	if (lines.length > 1) {
		_.each(lines,function(l) {
			filterLog(l);
		});
		return;
	}
	var idx = line.indexOf(' -- [');
	if (idx > 0) {
		var idx2 = line.indexOf(']', idx+7);
		line = line.substring(idx2+1);
	}
	if (line.charAt(0)=='[') {
		var i = line.indexOf(']');
		var label = line.substring(1,i);
		var rest = U.trim(line.substring(i+1));
		if (!rest) return;
		switch(label) {
			case 'INFO':
				logger.info(rest);
				return;
			case 'TRACE':
			case 'DEBUG':
				logger.debug(rest);
				return;
			case 'WARN':
				logger.warn(rest);
				return;
			case 'ERROR':
				logger.error(rest);
				return;
		}
	}
	logger.debug(line);
}

function runApp() {
	log('Running sample app "'+process.env.dir+'"...');

	// create array for `titanium build` args
	var e = process.env;
	var newArgs = ['build',
		'--project-dir', harnessAppPath,
		'--platform', e.platform || 'ios'
	];
	e.tiversion && (newArgs = newArgs.concat(['--sdk',e.tiversion]));
	e.simtype && (newArgs = newArgs.concat(['--sim-type',e.simtype]));

	//run stdout/stderr back through console.log
	var runcmd = spawn('titanium', newArgs);
	runcmd.stdout.on('data', function (data) {
		filterLog(data);
	});

	runcmd.stderr.on('data', function (data) {
		filterLog(data);
	});
}

namespace('app', function() {
	desc('remove the contents of the test harness\' "app" directory');
	task('clobber', function() {
		log('Reseting the Harness app from template...');
		wrench.rmdirSyncRecursive(harnessAppPath, true);
		wrench.mkdirSyncRecursive(harnessAppPath, 0777);
		wrench.copyDirSyncRecursive(harnessTemplatePath, harnessAppPath);
	});

	desc('compile the example app in the given directory name and stage for launch, e.g. "jake app:setup dir=masterdetail"');
	task('setup', ['app:clobber'], function() {
		log('Initializing Alloy project...');
		if (!path.existsSync(resourcesPath)) {
			wrench.mkdirSyncRecursive(resourcesPath, 0777);
		}
		require('child_process').exec('alloy new -f "' + harnessAppPath + '"', function(error, stdout, stderr) {
			if (error) {
				console.log(stdout);
				console.log(stderr);
				console.log(error);
				process.exit(1);
			} else {
				log('Staging sample app "'+process.env.dir+'" for launch...');
				wrench.copyDirSyncRecursive(path.join(process.cwd(), 'test', 'apps', process.env.dir), targetAppPath, {preserve:true});
				wrench.mkdirSyncRecursive(path.join(targetAppPath,'lib'),0777);
				wrench.copyDirSyncRecursive(
					path.join('test','lib'),
					path.join(targetAppPath,'lib')
				);
			}
		});
	});

	desc('run an example, all but dir are optional: e.g. "jake app:run dir=masterdetail platform=android tiversion=2.0.2.GA tisdk=<path to sdk>"');
	task('run', ['app:setup'], function() {
		runApp();
	});

	desc('copy a sample app\'s "app" folder into the Harness');
	task('quickrun', function() {
		log('Quick-running sample app "' + process.env.dir + '"...');
		log('Staging sample app "' + process.env.dir + '" for launch...');
		wrench.copyDirSyncRecursive(path.join(process.cwd(), 'test', 'apps', process.env.dir), targetAppPath, {preserve:true});
		runApp();
	});
});
<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * @emails oncall+react_native
 * @flow
 */

const React = require('react');

const ShallowRenderer = require('react-shallow-renderer');
const TestRenderer = require('react-test-renderer');

/* $FlowFixMe[not-a-function] (>=0.125.1 site=react_native_fb) This comment
 * suppresses an error found when Flow v0.125.1 was deployed. To see the error,
 * delete this comment and run Flow. */
const renderer = new ShallowRenderer();

export const shallow = (Component: React.Element<any>): any => {
  const Wrapper = (): React.Element<any> => Component;

  return renderer.render(<Wrapper />);
};

export const shallowRender = (Component: React.Element<any>): any => {
  return renderer.render(Component);
};

export const create = (Component: React.Element<any>): any => {
  return TestRenderer.create(Component);
};
<commit_msg>chore: remove FlowFixMe (#29468)<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * @emails oncall+react_native
 * @flow
 */

import * as React from 'react';
import TestRenderer from 'react-test-renderer';
import ShallowRenderer from 'react-shallow-renderer';

const renderer = new ShallowRenderer();

export const shallow = (Component: React.Element<any>): any => {
  const Wrapper = (): React.Element<any> => Component;

  return renderer.render(<Wrapper />);
};

export const shallowRender = (Component: React.Element<any>): any => {
  return renderer.render(Component);
};

export const create = (Component: React.Element<any>): any => {
  return TestRenderer.create(Component);
};
<|endoftext|>


<commit_before>// @flow
// @jsx glam

import glam from 'glam';
import React from 'react';
import { withRouter } from 'react-router-dom';

import Select from '../../src';
const smallDevice = '@media (max-width: 769px)';
const largeDevice = '@media (min-width: 770px)';

const changes = [
  {
    path: '/guides/styles',
    icon: '🎨',
    text: 'CSS-in-JS styling API',
  },
  {
    path: '/guides/components',
    icon: '🏗',
    text: 'Replacable component architecture',
  },
  {
    path: 'extensibility',
    icon: '🤖',
    text: 'Simpler and more extensible; fewer props',
  },
  { path: 'perf', icon: '⚡', text: 'Attention to detail and performance' },
];

function getLabel({ icon, text }) {
  return (

      {icon}
      {text}

  );
}

const Gradient = props => (

);
const Container = ({ css, ...props }) => (

);

function Header({ children, location, history }) {
  const isHome = location.pathname === '/' || location.pathname === '/examples';
  return !isHome ? (
    <Gradient>
      <Container
        css={{
          alignItems: 'center',
          display: 'flex ',
          justifyContent: 'space-between',
        }}
      >

          React Select

            v2

        <GithubButton />
      </Container>
      {children}
    </Gradient>
  ) : (
    <Gradient>
      <GithubCorner />
      <Container>

          React Select

            v2

              A flexible and beautiful Select Input control for ReactJS with
              multiselect, autocomplete, async and creatable support.

            <GithubButton />

            <Select
              getOptionLabel={getLabel}
              isSearchable={false}
              options={changes}
              onChange={opt => {
                console.log(opt);
                history.push(opt.path);
              }}
              placeholder="🎉 What's new in V2"
              styles={{
                control: (css, { isFocused }) => ({
                  ...css,
                  backgroundClip: 'padding-box',
                  borderColor: 'rgba(0,0,0,0.1)',
                  boxShadow: isFocused ? '0 0 0 1px #4C9AFF' : null,

                  ':hover': {
                    borderColor: 'rgba(0,0,0,0.2)',
                  },
                }),
                option: css => ({
                  ...css,
                  padding: '4px 12px',
                }),
              }}
            />

      </Container>
      {children}
    </Gradient>
  );
}

const GithubButton = () => (

    Star

);

const GithubCorner = () => (

    <svg width="100" height="100" viewBox="0 0 250 250" aria-hidden="true">
      <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
      <path
        d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
        fill="currentColor"
        style={{ transformOrigin: '130px 106px' }}
        className="octo-arm"
      />
      <path
        d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
        fill="currentColor"
        className="octo-body"
      />
    </svg>

);

export default withRouter(Header);
<commit_msg>update header options<commit_after>// @flow
// @jsx glam

import glam from 'glam';
import React from 'react';
import { withRouter } from 'react-router-dom';

import Select from '../../src';
const smallDevice = '@media (max-width: 769px)';
const largeDevice = '@media (min-width: 770px)';

const changes = [
  {
    path: '/guides/styles',
    icon: '🎨',
    text: 'CSS-in-JS styling API',
  },
  {
    path: '/guides/components',
    icon: '🏗',
    text: 'Replacable component architecture',
  },
  {
    path: '/api',
    icon: '🤖',
    text: 'Simpler and more extensible',
  },
  { path: '/examples/animated', icon: '🚀', text: 'Animation built in' },
];

function getLabel({ icon, text }) {
  return (

      {icon}
      {text}

  );
}

const Gradient = props => (

);
const Container = ({ css, ...props }) => (

);

function Header({ children, location, history }) {
  const isHome = location.pathname === '/' || location.pathname === '/examples';
  return !isHome ? (
    <Gradient>
      <Container
        css={{
          alignItems: 'center',
          display: 'flex ',
          justifyContent: 'space-between',
        }}
      >

          React Select

            v2

        <GithubButton />
      </Container>
      {children}
    </Gradient>
  ) : (
    <Gradient>
      <GithubCorner />
      <Container>

          React Select

            v2

              A flexible and beautiful Select Input control for ReactJS with
              multiselect, autocomplete, async and creatable support.

            <GithubButton />

            <Select
              getOptionLabel={getLabel}
              isSearchable={false}
              options={changes}
              onChange={opt => {
                console.log(opt);
                history.push(opt.path);
              }}
              placeholder="🎉 What's new in V2"
              styles={{
                control: (css, { isFocused }) => ({
                  ...css,
                  backgroundClip: 'padding-box',
                  borderColor: 'rgba(0,0,0,0.1)',
                  boxShadow: isFocused ? '0 0 0 1px #4C9AFF' : null,

                  ':hover': {
                    borderColor: 'rgba(0,0,0,0.2)',
                  },
                }),
                option: css => ({
                  ...css,
                  padding: '4px 12px',
                }),
                placeholder: css => ({
                  ...css,
                  color: 'black',
                }),
              }}
            />

      </Container>
      {children}
    </Gradient>
  );
}

const GithubButton = () => (

    Star

);

const GithubCorner = () => (

    <svg width="100" height="100" viewBox="0 0 250 250" aria-hidden="true">
      <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
      <path
        d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
        fill="currentColor"
        style={{ transformOrigin: '130px 106px' }}
        className="octo-arm"
      />
      <path
        d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
        fill="currentColor"
        className="octo-body"
      />
    </svg>

);

export default withRouter(Header);
<|endoftext|>


<commit_before>/** @jsx React.DOM */
var React = require("react/addons");
var {update} = React.addons;

function noop () {}

var ToastMessageSpec = {
  displayName: "ToastMessage",

  getDefaultProps () {
    var iconClassNames = {
      error: "toast-error",
      info: "toast-info",
      success: "toast-success",
      warning: "toast-warning"
    };

    return {
      className: "toast",
      iconClassNames: iconClassNames,
      titleClassName: "toast-title",
      messageClassName: "toast-message",
      closeButton: false
    };
  },

  _render_close_button (props) {
    return props.closeButton ? (
      <button className="toast-close-button" role="button">×</button>
    ) : false;
  },

  _render_title_element (props) {
    return props.title ? (

        {props.title}

    ) : false;
  },

  _render_message_element (props) {
    return props.message ? (

        {props.message}

    ) : false;
  },

  handleOnClick (event) {
    this.props.handleOnClick(event);
    this._hideToast();
  },

  _hideToast: noop,//tapToDismiss

  render () {
    var cx = React.addons.classSet;
    var {props} = this;
    var iconClassName = props.iconClassName || props.iconClassNames[props.type];

    var toastClass = {};
    toastClass[props.className] = true;
    toastClass[iconClassName] = true;

    return (

        {this._render_close_button(props)}
        {this._render_title_element(props)}
        {this._render_message_element(props)}

    );
  }
};

var jQuery = React.createClass(update(ToastMessageSpec, {
  displayName: { $set: "ToastMessage.jQuery" },
  mixins: { $set: [require("./jQueryMixin")] }
}));

var ToastMessage = module.exports = React.createClass(ToastMessageSpec);
ToastMessage.jQuery = jQuery;
<commit_msg>feat(ToastMessage): add handleMouse{Enter/Leave} for internal api<commit_after>/** @jsx React.DOM */
var React = require("react/addons");
var {update} = React.addons;

function noop () {}

var ToastMessageSpec = {
  displayName: "ToastMessage",

  getDefaultProps () {
    var iconClassNames = {
      error: "toast-error",
      info: "toast-info",
      success: "toast-success",
      warning: "toast-warning"
    };

    return {
      className: "toast",
      iconClassNames: iconClassNames,
      titleClassName: "toast-title",
      messageClassName: "toast-message",
      closeButton: false
    };
  },

  _render_close_button (props) {
    return props.closeButton ? (
      <button className="toast-close-button" role="button">×</button>
    ) : false;
  },

  _render_title_element (props) {
    return props.title ? (

        {props.title}

    ) : false;
  },

  _render_message_element (props) {
    return props.message ? (

        {props.message}

    ) : false;
  },

  handleOnClick (event) {
    this.props.handleOnClick(event);
    this._hideToast();
  },

  _hideToast: noop,//tapToDismiss

  render () {
    var cx = React.addons.classSet;
    var {props} = this;
    var iconClassName = props.iconClassName || props.iconClassNames[props.type];

    var toastClass = {};
    toastClass[props.className] = true;
    toastClass[iconClassName] = true;

    return (

        {this._render_close_button(props)}
        {this._render_title_element(props)}
        {this._render_message_element(props)}

    );
  }
};

var jQuery = React.createClass(update(ToastMessageSpec, {
  displayName: { $set: "ToastMessage.jQuery" },
  mixins: { $set: [require("./jQueryMixin")] }
}));

/*
 * assign default noop functions
 */
ToastMessageSpec.handleMouseEnter = noop;
ToastMessageSpec.handleMouseLeave = noop;

var ToastMessage = module.exports = React.createClass(ToastMessageSpec);
ToastMessage.jQuery = jQuery;
<|endoftext|>


<commit_before>function parseStateRef(ref, current) {
  var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
  if (preparsed) ref = current + '(' + preparsed[1] + ')';
  parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
  if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
  return { state: parsed[1], paramExpr: parsed[3] || null };
}

function stateContext(el) {
  var stateData = el.parent().inheritedData('$uiView');

  if (stateData && stateData.state && stateData.state.name) {
    return stateData.state;
  }
}

/**
 * @ngdoc directive
 * @name ui.router.state.directive:ui-sref
 *
 * @requires ui.router.state.$state
 * @requires $timeout
 *
 * @restrict A
 *
 * @description
 * A directive that binds a link (`<a>` tag) to a state. If the state has an associated
 * URL, the directive will automatically generate & update the `href` attribute via
 * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
 * the link will trigger a state transition with optional parameters.
 *
 * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
 * handled natively by the browser.
 *
 * You can also use relative state paths within ui-sref, just like the relative
 * paths passed to `$state.go()`. You just need to be aware that the path is relative
 * to the state that the link lives in, in other words the state that loaded the
 * template containing the link.
 *
 * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
 * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
 * and `reload`.
 *
 * @example
 * Here's an example of how you'd use ui-sref and how it would compile. If you have the
 * following template:
 *

 * Home | About | Next page
 *
 *

 *

 *         {{ contact.name }}
 *

 *

 *

 *
 * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
 *

 * Home | About | Next page
 *
 *

 *

 *         Joe
 *

 *

 *         Alice
 *

 *

 *         Bob
 *

 *

 *
 * Home
 *

 *
 * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
 * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
 */
$StateRefDirective.$inject = ['$state', '$timeout'];
function $StateRefDirective($state, $timeout) {
  var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];

  return {
    restrict: 'A',
    require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
    link: function(scope, element, attrs, uiSrefActive) {
      var ref = parseStateRef(attrs.uiSref, $state.current.name);
      var params = null, url = null, base = stateContext(element) || $state.$current;
      // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
      var hrefKind = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
                 'xlink:href' : 'href';
      var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
      var isForm = element[0].nodeName === "FORM";
      var attr = isForm ? "action" : hrefKind, nav = true;

      var options = { relative: base, inherit: true };
      var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};

      angular.forEach(allowedOptions, function(option) {
        if (option in optionsOverride) {
          options[option] = optionsOverride[option];
        }
      });

      var update = function(newVal) {
        if (newVal) params = angular.copy(newVal);
        if (!nav) return;

        newHref = $state.href(ref.state, params, options);

        var activeDirective = uiSrefActive[1] || uiSrefActive[0];
        if (activeDirective) {
          activeDirective.$$addStateInfo(ref.state, params);
        }
        if (newHref === null) {
          nav = false;
          return false;
        }
        attrs.$set(attr, newHref);
      };

      if (ref.paramExpr) {
        scope.$watch(ref.paramExpr, function(newVal, oldVal) {
          if (newVal !== params) update(newVal);
        }, true);
        params = angular.copy(scope.$eval(ref.paramExpr));
      }
      update();

      if (isForm) return;

      element.bind("click", function(e) {
        var button = e.which || e.button;
        if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
          // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
          var transition = $timeout(function() {
            $state.go(ref.state, params, options);
          });
          e.preventDefault();

          // if the state has no URL, ignore one preventDefault from the  directive.
          var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
          e.preventDefault = function() {
            if (ignorePreventDefaultCount-- <= 0)
              $timeout.cancel(transition);
          };
        }
      });
    }
  };
}

/**
 * @ngdoc directive
 * @name ui.router.state.directive:ui-sref-active
 *
 * @requires ui.router.state.$state
 * @requires ui.router.state.$stateParams
 * @requires $interpolate
 *
 * @restrict A
 *
 * @description
 * A directive working alongside ui-sref to add classes to an element when the
 * related ui-sref directive's state is active, and removing them when it is inactive.
 * The primary use-case is to simplify the special appearance of navigation menus
 * relying on `ui-sref`, by having the "active" state's menu button appear different,
 * distinguishing it from the inactive menu items.
 *
 * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
 * ui-sref-active found at the same level or above the ui-sref will be used.
 *
 * Will activate when the ui-sref's target state or any child state is active. If you
 * need to activate only when the ui-sref target state is active and *not* any of
 * it's children, then you will use
 * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
 *
 * @example
 * Given the following template:
 *

 *

 *

 *     @bilbobaggins
 *

 *

 *

 *
 *
 * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
 * the resulting HTML will appear as (note the 'active' class):
 *

 *

 *

 *     @bilbobaggins
 *

 *

 *

 *
 * The class name is interpolated **once** during the directives link time (any further changes to the
 * interpolated value are ignored).
 *
 * Multiple classes may be specified in a space-separated format:
 *

 *

 *

 *     link
 *

 *

 *

 */

/**
 * @ngdoc directive
 * @name ui.router.state.directive:ui-sref-active-eq
 *
 * @requires ui.router.state.$state
 * @requires ui.router.state.$stateParams
 * @requires $interpolate
 *
 * @restrict A
 *
 * @description
 * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
 * when the exact target state used in the `ui-sref` is active; no child states.
 *
 */
$StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
function $StateRefActiveDirective($state, $stateParams, $interpolate) {
  return  {
    restrict: "A",
    controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
      var states = [], activeClass;

      // There probably isn't much point in $observing this
      // uiSrefActive and uiSrefActiveEq share the same directive object with some
      // slight difference in logic routing
      activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);

      // Allow uiSref to communicate with uiSrefActive[Equals]
      this.$$addStateInfo = function (newState, newParams) {
        var state = $state.get(newState, stateContext($element));
        if (state) {
          states.push({
            state: state,
            params: newParams
          });
          update();
        }
      };

      $scope.$on('$stateChangeSuccess', update);

      // Update route state
      function update() {
        if (anyMatch()) {
          $element.addClass(activeClass);
        } else {
          $element.removeClass(activeClass);
        }
      }

      function anyMatch() {
        for (var i = 0; i < states.length; i++) {
          if (isMatch(states[i].state, states[i].params)) {
            return true;
          }
        }
        return false;
      }

      function isMatch(state, params) {
        if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
          return $state.is(state.name, params);
        } else {
          return $state.includes(state.name, params);
        }
      }
    }]
  };
}

angular.module('ui.router.state')
  .directive('uiSref', $StateRefDirective)
  .directive('uiSrefActive', $StateRefActiveDirective)
  .directive('uiSrefActiveEq', $StateRefActiveDirective);
<commit_msg>fix(uiSref): use Object's toString instead of Window's toString<commit_after>function parseStateRef(ref, current) {
  var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
  if (preparsed) ref = current + '(' + preparsed[1] + ')';
  parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
  if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
  return { state: parsed[1], paramExpr: parsed[3] || null };
}

function stateContext(el) {
  var stateData = el.parent().inheritedData('$uiView');

  if (stateData && stateData.state && stateData.state.name) {
    return stateData.state;
  }
}

/**
 * @ngdoc directive
 * @name ui.router.state.directive:ui-sref
 *
 * @requires ui.router.state.$state
 * @requires $timeout
 *
 * @restrict A
 *
 * @description
 * A directive that binds a link (`<a>` tag) to a state. If the state has an associated
 * URL, the directive will automatically generate & update the `href` attribute via
 * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
 * the link will trigger a state transition with optional parameters.
 *
 * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
 * handled natively by the browser.
 *
 * You can also use relative state paths within ui-sref, just like the relative
 * paths passed to `$state.go()`. You just need to be aware that the path is relative
 * to the state that the link lives in, in other words the state that loaded the
 * template containing the link.
 *
 * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
 * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
 * and `reload`.
 *
 * @example
 * Here's an example of how you'd use ui-sref and how it would compile. If you have the
 * following template:
 *

 * Home | About | Next page
 *
 *

 *

 *         {{ contact.name }}
 *

 *

 *

 *
 * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
 *

 * Home | About | Next page
 *
 *

 *

 *         Joe
 *

 *

 *         Alice
 *

 *

 *         Bob
 *

 *

 *
 * Home
 *

 *
 * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
 * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
 */
$StateRefDirective.$inject = ['$state', '$timeout'];
function $StateRefDirective($state, $timeout) {
  var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];

  return {
    restrict: 'A',
    require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
    link: function(scope, element, attrs, uiSrefActive) {
      var ref = parseStateRef(attrs.uiSref, $state.current.name);
      var params = null, url = null, base = stateContext(element) || $state.$current;
      // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
      var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
                 'xlink:href' : 'href';
      var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
      var isForm = element[0].nodeName === "FORM";
      var attr = isForm ? "action" : hrefKind, nav = true;

      var options = { relative: base, inherit: true };
      var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};

      angular.forEach(allowedOptions, function(option) {
        if (option in optionsOverride) {
          options[option] = optionsOverride[option];
        }
      });

      var update = function(newVal) {
        if (newVal) params = angular.copy(newVal);
        if (!nav) return;

        newHref = $state.href(ref.state, params, options);

        var activeDirective = uiSrefActive[1] || uiSrefActive[0];
        if (activeDirective) {
          activeDirective.$$addStateInfo(ref.state, params);
        }
        if (newHref === null) {
          nav = false;
          return false;
        }
        attrs.$set(attr, newHref);
      };

      if (ref.paramExpr) {
        scope.$watch(ref.paramExpr, function(newVal, oldVal) {
          if (newVal !== params) update(newVal);
        }, true);
        params = angular.copy(scope.$eval(ref.paramExpr));
      }
      update();

      if (isForm) return;

      element.bind("click", function(e) {
        var button = e.which || e.button;
        if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
          // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
          var transition = $timeout(function() {
            $state.go(ref.state, params, options);
          });
          e.preventDefault();

          // if the state has no URL, ignore one preventDefault from the  directive.
          var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
          e.preventDefault = function() {
            if (ignorePreventDefaultCount-- <= 0)
              $timeout.cancel(transition);
          };
        }
      });
    }
  };
}

/**
 * @ngdoc directive
 * @name ui.router.state.directive:ui-sref-active
 *
 * @requires ui.router.state.$state
 * @requires ui.router.state.$stateParams
 * @requires $interpolate
 *
 * @restrict A
 *
 * @description
 * A directive working alongside ui-sref to add classes to an element when the
 * related ui-sref directive's state is active, and removing them when it is inactive.
 * The primary use-case is to simplify the special appearance of navigation menus
 * relying on `ui-sref`, by having the "active" state's menu button appear different,
 * distinguishing it from the inactive menu items.
 *
 * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
 * ui-sref-active found at the same level or above the ui-sref will be used.
 *
 * Will activate when the ui-sref's target state or any child state is active. If you
 * need to activate only when the ui-sref target state is active and *not* any of
 * it's children, then you will use
 * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
 *
 * @example
 * Given the following template:
 *

 *

 *

 *     @bilbobaggins
 *

 *

 *

 *
 *
 * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
 * the resulting HTML will appear as (note the 'active' class):
 *

 *

 *

 *     @bilbobaggins
 *

 *

 *

 *
 * The class name is interpolated **once** during the directives link time (any further changes to the
 * interpolated value are ignored).
 *
 * Multiple classes may be specified in a space-separated format:
 *

 *

 *

 *     link
 *

 *

 *

 */

/**
 * @ngdoc directive
 * @name ui.router.state.directive:ui-sref-active-eq
 *
 * @requires ui.router.state.$state
 * @requires ui.router.state.$stateParams
 * @requires $interpolate
 *
 * @restrict A
 *
 * @description
 * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
 * when the exact target state used in the `ui-sref` is active; no child states.
 *
 */
$StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
function $StateRefActiveDirective($state, $stateParams, $interpolate) {
  return  {
    restrict: "A",
    controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
      var states = [], activeClass;

      // There probably isn't much point in $observing this
      // uiSrefActive and uiSrefActiveEq share the same directive object with some
      // slight difference in logic routing
      activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);

      // Allow uiSref to communicate with uiSrefActive[Equals]
      this.$$addStateInfo = function (newState, newParams) {
        var state = $state.get(newState, stateContext($element));
        if (state) {
          states.push({
            state: state,
            params: newParams
          });
          update();
        }
      };

      $scope.$on('$stateChangeSuccess', update);

      // Update route state
      function update() {
        if (anyMatch()) {
          $element.addClass(activeClass);
        } else {
          $element.removeClass(activeClass);
        }
      }

      function anyMatch() {
        for (var i = 0; i < states.length; i++) {
          if (isMatch(states[i].state, states[i].params)) {
            return true;
          }
        }
        return false;
      }

      function isMatch(state, params) {
        if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
          return $state.is(state.name, params);
        } else {
          return $state.includes(state.name, params);
        }
      }
    }]
  };
}

angular.module('ui.router.state')
  .directive('uiSref', $StateRefDirective)
  .directive('uiSrefActive', $StateRefActiveDirective)
  .directive('uiSrefActiveEq', $StateRefActiveDirective);
<|endoftext|>