<commit_before>// @inheritedComponent ButtonBase

import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import { fade } from '../styles/colorManipulator';
import ButtonBase from '../ButtonBase';

export const styles = theme => ({
  root: {
    ...theme.typography.button,
    lineHeight: '1.4em', // Improve readability for multiline button.
    boxSizing: 'border-box',
    minWidth: 88,
    minHeight: 36,
    padding: `${theme.spacing.unit}px ${theme.spacing.unit * 2}px`,
    borderRadius: 2,
    color: theme.palette.text.primary,
    transition: theme.transitions.create(['background-color', 'box-shadow'], {
      duration: theme.transitions.duration.short,
    }),
    '&:hover': {
      textDecoration: 'none',
      // Reset on mouse devices
      backgroundColor: fade(theme.palette.text.primary, 0.12),
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
      '&$disabled': {
        backgroundColor: 'transparent',
      },
    },
  },
  dense: {
    padding: `${theme.spacing.unit - 1}px ${theme.spacing.unit}px`,
    minWidth: 64,
    minHeight: 32,
    fontSize: theme.typography.pxToRem(theme.typography.fontSize - 1),
  },
  label: {
    width: '100%',
    display: 'inherit',
    alignItems: 'inherit',
    justifyContent: 'inherit',
  },
  flatPrimary: {
    color: theme.palette.primary.main,
    '&:hover': {
      backgroundColor: fade(theme.palette.primary.main, 0.12),
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
    },
  },
  flatAccent: {
    color: theme.palette.secondary.light,
    '&:hover': {
      backgroundColor: fade(theme.palette.secondary.light, 0.12),
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
    },
  },
  flatContrast: {
    color: theme.palette.primary.contrastText,
    '&:hover': {
      backgroundColor: fade(theme.palette.primary.contrastText, 0.12),
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
    },
  },
  colorInherit: {
    color: 'inherit',
  },
  raised: {
    color: theme.palette.getContrastText(theme.palette.grey[300]),
    backgroundColor: theme.palette.grey[300],
    boxShadow: theme.shadows[2],
    '&$keyboardFocused': {
      boxShadow: theme.shadows[6],
    },
    '&:active': {
      boxShadow: theme.shadows[8],
    },
    '&$disabled': {
      boxShadow: theme.shadows[0],
      backgroundColor: theme.palette.text.divider,
    },
    '&:hover': {
      backgroundColor: theme.palette.grey.A100,
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: theme.palette.grey[300],
      },
      '&$disabled': {
        backgroundColor: theme.palette.text.divider,
        // Reset on mouse devices
        '@media (hover: none)': {
          backgroundColor: theme.palette.grey[300],
        },
      },
    },
  },
  keyboardFocused: {},
  raisedPrimary: {
    color: theme.palette.primary.contrastText,
    backgroundColor: theme.palette.primary.main,
    '&:hover': {
      backgroundColor: theme.palette.primary.dark,
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: theme.palette.primary.main,
      },
    },
  },
  raisedAccent: {
    color: theme.palette.getContrastText(theme.palette.secondary.light),
    backgroundColor: theme.palette.secondary.light,
    '&:hover': {
      backgroundColor: theme.palette.secondary.main,
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: theme.palette.secondary.light,
      },
    },
  },
  raisedContrast: {
    color: theme.palette.primary.contrastText,
  },
  disabled: {
    color: theme.palette.action.disabled,
  },
  fab: {
    borderRadius: '50%',
    padding: 0,
    minWidth: 0,
    width: 56,
    height: 56,
    boxShadow: theme.shadows[6],
    '&:active': {
      boxShadow: theme.shadows[12],
    },
  },
  mini: {
    width: 40,
    height: 40,
  },
  fullWidth: {
    width: '100%',
  },
});

function Button(props) {
  const {
    children,
    classes,
    className: classNameProp,
    color,
    dense,
    disabled,
    disableFocusRipple,
    fab,
    fullWidth,
    mini,
    raised,
    ...other
  } = props;

  const flat = !raised && !fab;
  const className = classNames(
    classes.root,
    {
      [classes.raised]: raised || fab,
      [classes.fab]: fab,
      [classes.mini]: fab && mini,
      [classes.colorInherit]: color === 'inherit',
      [classes.flatPrimary]: flat && color === 'primary',
      [classes.flatAccent]: flat && color === 'accent',
      [classes.flatContrast]: flat && color === 'contrast',
      [classes.raisedPrimary]: !flat && color === 'primary',
      [classes.raisedAccent]: !flat && color === 'accent',
      [classes.raisedContrast]: !flat && color === 'contrast',
      [classes.dense]: dense,
      [classes.disabled]: disabled,
      [classes.fullWidth]: fullWidth,
    },
    classNameProp,
  );

  return (
    <ButtonBase
      className={className}
      disabled={disabled}
      focusRipple={!disableFocusRipple}
      keyboardFocusedClassName={classes.keyboardFocused}
      {...other}
    >
      {children}
    </ButtonBase>
  );
}

Button.propTypes = {
  /**
   * The content of the button.
   */
  children: PropTypes.node.isRequired,
  /**
   * Useful to extend the style applied to components.
   */
  classes: PropTypes.object.isRequired,
  /**
   * @ignore
   */
  className: PropTypes.string,
  /**
   * The color of the component. It's using the theme palette when that makes sense.
   */
  color: PropTypes.oneOf(['default', 'inherit', 'primary', 'accent', 'contrast']),
  /**
   * The component used for the root node.
   * Either a string to use a DOM element or a component.
   * The default value is a `button`.
   */
  component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
  /**
   * Uses a smaller minWidth, ideal for things like card actions.
   */
  dense: PropTypes.bool,
  /**
   * If `true`, the button will be disabled.
   */
  disabled: PropTypes.bool,
  /**
   * If `true`, the  keyboard focus ripple will be disabled.
   * `disableRipple` must also be true.
   */
  disableFocusRipple: PropTypes.bool,
  /**
   * If `true`, the ripple effect will be disabled.
   */
  disableRipple: PropTypes.bool,
  /**
   * If `true`, will use floating action button styling.
   */
  fab: PropTypes.bool,
  /**
   * If `true`, the button will take up the full width of its container.
   */
  fullWidth: PropTypes.bool,
  /**
   * The URL to link to when the button is clicked.
   * If defined, an `a` element will be used as the root node.
   */
  href: PropTypes.string,
  /**
   * If `true`, and `fab` is `true`, will use mini floating action button styling.
   */
  mini: PropTypes.bool,
  /**
   * If `true`, the button will use raised styling.
   */
  raised: PropTypes.bool,
  /**
   * @ignore
   */
  type: PropTypes.string,
};

Button.defaultProps = {
  color: 'default',
  dense: false,
  disabled: false,
  disableFocusRipple: false,
  disableRipple: false,
  fab: false,
  fullWidth: false,
  mini: false,
  raised: false,
  type: 'button',
};

export default withStyles(styles, { name: 'MuiButton' })(Button);
<commit_msg>[Button] Fix secondary contrastText color (#9913)<commit_after>// @inheritedComponent ButtonBase

import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import { fade } from '../styles/colorManipulator';
import ButtonBase from '../ButtonBase';

export const styles = theme => ({
  root: {
    ...theme.typography.button,
    lineHeight: '1.4em', // Improve readability for multiline button.
    boxSizing: 'border-box',
    minWidth: 88,
    minHeight: 36,
    padding: `${theme.spacing.unit}px ${theme.spacing.unit * 2}px`,
    borderRadius: 2,
    color: theme.palette.text.primary,
    transition: theme.transitions.create(['background-color', 'box-shadow'], {
      duration: theme.transitions.duration.short,
    }),
    '&:hover': {
      textDecoration: 'none',
      // Reset on mouse devices
      backgroundColor: fade(theme.palette.text.primary, 0.12),
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
      '&$disabled': {
        backgroundColor: 'transparent',
      },
    },
  },
  dense: {
    padding: `${theme.spacing.unit - 1}px ${theme.spacing.unit}px`,
    minWidth: 64,
    minHeight: 32,
    fontSize: theme.typography.pxToRem(theme.typography.fontSize - 1),
  },
  label: {
    width: '100%',
    display: 'inherit',
    alignItems: 'inherit',
    justifyContent: 'inherit',
  },
  flatPrimary: {
    color: theme.palette.primary.main,
    '&:hover': {
      backgroundColor: fade(theme.palette.primary.main, 0.12),
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
    },
  },
  flatAccent: {
    color: theme.palette.secondary.light,
    '&:hover': {
      backgroundColor: fade(theme.palette.secondary.light, 0.12),
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
    },
  },
  flatContrast: {
    color: theme.palette.primary.contrastText,
    '&:hover': {
      backgroundColor: fade(theme.palette.primary.contrastText, 0.12),
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
    },
  },
  colorInherit: {
    color: 'inherit',
  },
  raised: {
    color: theme.palette.getContrastText(theme.palette.grey[300]),
    backgroundColor: theme.palette.grey[300],
    boxShadow: theme.shadows[2],
    '&$keyboardFocused': {
      boxShadow: theme.shadows[6],
    },
    '&:active': {
      boxShadow: theme.shadows[8],
    },
    '&$disabled': {
      boxShadow: theme.shadows[0],
      backgroundColor: theme.palette.text.divider,
    },
    '&:hover': {
      backgroundColor: theme.palette.grey.A100,
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: theme.palette.grey[300],
      },
      '&$disabled': {
        backgroundColor: theme.palette.text.divider,
        // Reset on mouse devices
        '@media (hover: none)': {
          backgroundColor: theme.palette.grey[300],
        },
      },
    },
  },
  keyboardFocused: {},
  raisedPrimary: {
    color: theme.palette.primary.contrastText,
    backgroundColor: theme.palette.primary.main,
    '&:hover': {
      backgroundColor: theme.palette.primary.dark,
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: theme.palette.primary.main,
      },
    },
  },
  raisedAccent: {
    color: theme.palette.secondary.contrastText,
    backgroundColor: theme.palette.secondary.light,
    '&:hover': {
      backgroundColor: theme.palette.secondary.main,
      // Reset on mouse devices
      '@media (hover: none)': {
        backgroundColor: theme.palette.secondary.light,
      },
    },
  },
  raisedContrast: {
    color: theme.palette.primary.contrastText,
  },
  disabled: {
    color: theme.palette.action.disabled,
  },
  fab: {
    borderRadius: '50%',
    padding: 0,
    minWidth: 0,
    width: 56,
    height: 56,
    boxShadow: theme.shadows[6],
    '&:active': {
      boxShadow: theme.shadows[12],
    },
  },
  mini: {
    width: 40,
    height: 40,
  },
  fullWidth: {
    width: '100%',
  },
});

function Button(props) {
  const {
    children,
    classes,
    className: classNameProp,
    color,
    dense,
    disabled,
    disableFocusRipple,
    fab,
    fullWidth,
    mini,
    raised,
    ...other
  } = props;

  const flat = !raised && !fab;
  const className = classNames(
    classes.root,
    {
      [classes.raised]: raised || fab,
      [classes.fab]: fab,
      [classes.mini]: fab && mini,
      [classes.colorInherit]: color === 'inherit',
      [classes.flatPrimary]: flat && color === 'primary',
      [classes.flatAccent]: flat && color === 'accent',
      [classes.flatContrast]: flat && color === 'contrast',
      [classes.raisedPrimary]: !flat && color === 'primary',
      [classes.raisedAccent]: !flat && color === 'accent',
      [classes.raisedContrast]: !flat && color === 'contrast',
      [classes.dense]: dense,
      [classes.disabled]: disabled,
      [classes.fullWidth]: fullWidth,
    },
    classNameProp,
  );

  return (
    <ButtonBase
      className={className}
      disabled={disabled}
      focusRipple={!disableFocusRipple}
      keyboardFocusedClassName={classes.keyboardFocused}
      {...other}
    >
      {children}
    </ButtonBase>
  );
}

Button.propTypes = {
  /**
   * The content of the button.
   */
  children: PropTypes.node.isRequired,
  /**
   * Useful to extend the style applied to components.
   */
  classes: PropTypes.object.isRequired,
  /**
   * @ignore
   */
  className: PropTypes.string,
  /**
   * The color of the component. It's using the theme palette when that makes sense.
   */
  color: PropTypes.oneOf(['default', 'inherit', 'primary', 'accent', 'contrast']),
  /**
   * The component used for the root node.
   * Either a string to use a DOM element or a component.
   * The default value is a `button`.
   */
  component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
  /**
   * Uses a smaller minWidth, ideal for things like card actions.
   */
  dense: PropTypes.bool,
  /**
   * If `true`, the button will be disabled.
   */
  disabled: PropTypes.bool,
  /**
   * If `true`, the  keyboard focus ripple will be disabled.
   * `disableRipple` must also be true.
   */
  disableFocusRipple: PropTypes.bool,
  /**
   * If `true`, the ripple effect will be disabled.
   */
  disableRipple: PropTypes.bool,
  /**
   * If `true`, will use floating action button styling.
   */
  fab: PropTypes.bool,
  /**
   * If `true`, the button will take up the full width of its container.
   */
  fullWidth: PropTypes.bool,
  /**
   * The URL to link to when the button is clicked.
   * If defined, an `a` element will be used as the root node.
   */
  href: PropTypes.string,
  /**
   * If `true`, and `fab` is `true`, will use mini floating action button styling.
   */
  mini: PropTypes.bool,
  /**
   * If `true`, the button will use raised styling.
   */
  raised: PropTypes.bool,
  /**
   * @ignore
   */
  type: PropTypes.string,
};

Button.defaultProps = {
  color: 'default',
  dense: false,
  disabled: false,
  disableFocusRipple: false,
  disableRipple: false,
  fab: false,
  fullWidth: false,
  mini: false,
  raised: false,
  type: 'button',
};

export default withStyles(styles, { name: 'MuiButton' })(Button);
<|endoftext|>


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

/**
 * The popup listbox for a ComboBox.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  const ComboBoxListItemNode = require( 'SUN/ComboBoxListItemNode' );
  const Emitter = require( 'AXON/Emitter' );
  const EmitterIO = require( 'AXON/EmitterIO' );
  const Event = require( 'SCENERY/input/Event' );
  const KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  const Panel = require( 'SUN/Panel' );
  const sun = require( 'SUN/sun' );
  const Tandem = require( 'TANDEM/Tandem' );
  const VBox = require( 'SCENERY/nodes/VBox' );
  const VoidIO = require( 'TANDEM/types/VoidIO' );

  class ComboBoxListBox extends Panel {

    /**
     * @param {Property} property
     * @param {ComboBoxItem[]} items
     * @param {ComboBoxButton} button  TODO sun#445 would be nice if listbox didn't need to know about button
     * @param {function} hideCallback  TODO sun#445 would be nice if we didn't need hideCallback
     * @param {Tandem} tandem
     * @param {Object} [options]
     */
    constructor( property, items, button, hideCallback, tandem, options ) {

      options = _.extend( {

        // fill for the highlight behind items in the list
        highlightFill: 'rgb( 245, 245, 245 )',

        // Panel options
        xMargin: 12,
        yMargin: 8,

        // a11y
        tagName: 'ul',
        ariaRole: 'listbox',
        groupFocusHighlight: true,
        focusable: true

        // Not instrumented for PhET-iO because the list's location isn't valid until it has been popped up.
        // See https://github.com/phetsims/phet-io/issues/1102

      }, options );

      // TODO sun#405 It seems it would be better to use FireListener on each ComboBoxListItemNode
      const firedEmitter = new Emitter( {
        argumentTypes: [ { validValue: Event } ],
        listener: event => {

          const listItemNode = event.currentTarget;
          assert && assert( listItemNode instanceof ComboBoxListItemNode, 'expected a ComboBoxListItemNode' );

          // a11y - keep this PDOM attribute in sync
          this.updateActiveDescendant( listItemNode );

          hideCallback();
          listItemNode.setHighlightVisible( false );
          event.abort(); // prevent nodes (eg, controls) behind the list from receiving the event

          property.value = listItemNode.item.value;
        },

        // phet-io
        tandem: tandem.createTandem( 'firedEmitter' ),
        phetioType: EmitterIO( [ { name: 'event', type: VoidIO } ] ) // TODO sun#405 Should type be EventIO or DOMEventIO?
      } );

      // listener that we'll attach to each item in the list
      const itemListener = {
        enter( event ) {
          event.currentTarget.setHighlightVisible( true );
        },
        exit( event ) {
          event.currentTarget.setHighlightVisible( false );
        },
        down( event ) {
          event.abort(); // prevent click-to-dismiss on the list
        },
        up( event ) {
          firedEmitter.emit( event );
        }
      };

      // Compute max item dimensions
      const maxItemWidth = _.maxBy( items, item => item.node.width ).node.width;
      const maxItemHeight = _.maxBy( items, item => item.node.height ).node.height;

      // Uniform dimensions for all highlighted items in the list, highlight overlaps margin by 50%
      const highlightWidth = maxItemWidth + options.xMargin;
      const highlightHeight = maxItemHeight + options.yMargin;

      // @private populate list with items
      const listItemNodes = [];
      items.forEach( ( item, index ) => {

        // Create the list item node
        const listItemNode = new ComboBoxListItemNode( item, highlightWidth, highlightHeight, {
          align: options.align,
          highlightFill: options.highlightFill,

          // highlight overlaps half of margins
          xMargin: 0.5 * options.xMargin,
          left: 0.5 * options.xMargin,
          top: ( 0.5 * options.yMargin ) + ( index * highlightHeight ),
          tandem: item.tandemName ? tandem.createTandem( item.tandemName ) : Tandem.optional,
          a11yLabel: item.a11yLabel
        } );
        listItemNodes.push( listItemNode );

        listItemNode.addInputListener( itemListener );

        //TODO sun#314 a11yClickListener should not be assigned here, it should be set via options or a setter method
        // a11y - select the property and close on a click event from assistive technology, must be removed in disposal
        // of combo box item. Keep track of it on the listItemNode for disposal.
        listItemNode.a11yClickListener = {
          keydown: event => {

            //TODO sun#445 KEY_ENTER is not working
            if ( KeyboardUtil.KEY_ENTER === event.domEvent.keyCode || KeyboardUtil.KEY_SPACE === event.domEvent.keyCode ) {

              //TODO address sun#447
              // fromA11yEnterKey = KeyboardUtil.KEY_ENTER === event.domEvent.keyCode; // only for the enter key
              property.value = item.value;
              hideCallback();
              button.focus();

              // a11y - keep this PDOM attribute in sync
              this.updateActiveDescendant( listItemNode );
            }
          }
        };
        listItemNode.addInputListener( listItemNode.a11yClickListener );
      } );

      const content = new VBox( {
        spacing: 0,
        children: listItemNodes
      } );

      // Adjust margins to account for highlight overlap
      options.xMargin = options.xMargin / 2;
      options.yMargin = options.yMargin / 2;

      super( content, options );

      // a11y - the list is labeled by the button's label
      this.addAriaLabelledbyAssociation( {
        otherNode: button,
        otherElementName: AccessiblePeer.LABEL_SIBLING,
        thisElementName: AccessiblePeer.PRIMARY_SIBLING
      } );

      // @public {ComboBoxListItemNode|null} the ComboBoxListItemNode that has focus
      this.focusedItemNode = null;

      // Handle keydown for a11y
      this.addInputListener( {
        keydown: event => {
          var keyCode = event.domEvent.keyCode;
          if ( keyCode === KeyboardUtil.KEY_ESCAPE ) {
            hideCallback();
            button.focus();
          }
          else if ( keyCode === KeyboardUtil.KEY_TAB ) {
            hideCallback();
          }
          else if ( keyCode === KeyboardUtil.KEY_DOWN_ARROW || keyCode === KeyboardUtil.KEY_UP_ARROW ) {

            // Up/down arrow keys move the focus between items in the listbox
            const direction = ( keyCode === KeyboardUtil.KEY_DOWN_ARROW ) ? 1 : -1;
            for ( let i = 0; i < listItemNodes.length; i++ ) {
              if ( this.focusedItemNode === listItemNodes[ i ] ) {
                const nextListItemNode = listItemNodes[ i + direction ];
                if ( nextListItemNode ) {

                  //TODO sun#314 say more here
                  // a11y - keep this PDOM attribute in sync
                  this.updateActiveDescendant( nextListItemNode );

                  // previous item should not be focusable
                  this.focusedItemNode.focusable = false;
                  this.focusedItemNode = nextListItemNode;
                  this.focusedItemNode.a11yFocusButton();
                  break;
                }
              }
            }
          }
        }
      } );

      // Update focus when the listbox's visibility changes.
      this.on( 'visibility', () => { this.updateFocus(); } );

      // @private
      this.disposeComboBoxListBox = () => {
        for ( let i = 0; i < listItemNodes; i++ ) {
          listItemNodes[ i ].dispose(); // to unregister tandem
        }
      };

      // @private needed by methods
      this.property = property;
      this.listItemNodes = listItemNodes;
    }

    /**
     * @public
     * @override
     */
    dispose() {
      this.disposeComboBoxListBox();
      super.dispose();
    }

    /**
     * Updates the focus to match the currently selected value.
     * @private
     */
    updateFocus() {
      if ( this.visible ) {

        // listbox is visible, focus on selected item
        for ( let i = 0; i < this.listItemNodes.length; i++ ) {
          const listItemNode = this.listItemNodes[ i ];
          if ( this.property.value === listItemNode.item.value ) {
            this.focusedItemNode = listItemNode;
            this.focusedItemNode.a11yFocusButton();
          }
        }
      }
      else if ( this.focusedItemNode ) {

        // listbox is invisible, clear focus
        this.focusedItemNode.focusable = false;
        this.focusedItemNode = null;
      }
    }

    // TODO: sun#314 we don't likely need this anymore
    /**
     * Updates this attribute on the listbox.
     * This changes as you interact with the comboBox, as well as when an item is selected.
     * @param {ComboBoxListItemNode} listItemNode
     * @private
     */
    updateActiveDescendant( listItemNode ) {

      // overwrite purposefully
      this.activeDescendantAssociations = [ {
        otherNode: listItemNode,
        thisElementName: AccessiblePeer.PRIMARY_SIBLING,
        otherElementName: AccessiblePeer.PRIMARY_SIBLING
      } ];
    }
  }

  return sun.register( 'ComboBoxListBox', ComboBoxListBox );
} );<commit_msg>consolidate all item input handling in one listener, #445<commit_after>// Copyright 2019, University of Colorado Boulder

/**
 * The popup listbox for a ComboBox.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  const ComboBoxListItemNode = require( 'SUN/ComboBoxListItemNode' );
  const Emitter = require( 'AXON/Emitter' );
  const EmitterIO = require( 'AXON/EmitterIO' );
  const Event = require( 'SCENERY/input/Event' );
  const KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  const Panel = require( 'SUN/Panel' );
  const sun = require( 'SUN/sun' );
  const Tandem = require( 'TANDEM/Tandem' );
  const VBox = require( 'SCENERY/nodes/VBox' );
  const VoidIO = require( 'TANDEM/types/VoidIO' );

  class ComboBoxListBox extends Panel {

    /**
     * @param {Property} property
     * @param {ComboBoxItem[]} items
     * @param {ComboBoxButton} button  TODO sun#445 would be nice if listbox didn't need to know about button
     * @param {function} hideCallback  TODO sun#445 would be nice if we didn't need hideCallback
     * @param {Tandem} tandem
     * @param {Object} [options]
     */
    constructor( property, items, button, hideCallback, tandem, options ) {

      options = _.extend( {

        // fill for the highlight behind items in the list
        highlightFill: 'rgb( 245, 245, 245 )',

        // Panel options
        xMargin: 12,
        yMargin: 8,

        // a11y
        tagName: 'ul',
        ariaRole: 'listbox',
        groupFocusHighlight: true,
        focusable: true

        // Not instrumented for PhET-iO because the list's location isn't valid until it has been popped up.
        // See https://github.com/phetsims/phet-io/issues/1102

      }, options );

      // TODO sun#405 It seems it would be better to use FireListener on each ComboBoxListItemNode
      const firedEmitter = new Emitter( {
        argumentTypes: [ { validValue: Event } ],
        listener: event => {

          const listItemNode = event.currentTarget;
          assert && assert( listItemNode instanceof ComboBoxListItemNode, 'expected a ComboBoxListItemNode' );

          // a11y - keep this PDOM attribute in sync
          this.updateActiveDescendant( listItemNode );

          hideCallback();
          listItemNode.setHighlightVisible( false );
          event.abort(); // prevent nodes (eg, controls) behind the list from receiving the event

          property.value = listItemNode.item.value;
        },

        // phet-io
        tandem: tandem.createTandem( 'firedEmitter' ),
        phetioType: EmitterIO( [ { name: 'event', type: VoidIO } ] ) // TODO sun#405 Should type be EventIO or DOMEventIO?
      } );

      // listener that we'll attach to each item in the list
      const itemListener = {

        enter( event ) {
          event.currentTarget.setHighlightVisible( true );
        },

        exit( event ) {
          event.currentTarget.setHighlightVisible( false );
        },

        down( event ) {
          event.abort(); // prevent click-to-dismiss on the list
        },

        up( event ) {
          firedEmitter.emit( event );
        },

        // Handle keydown on each item in the listbox, for a11y
        keydown: event => {

          //TODO sun#445 KEY_ENTER is not working
          if ( KeyboardUtil.KEY_ENTER === event.domEvent.keyCode || KeyboardUtil.KEY_SPACE === event.domEvent.keyCode ) {

            assert && assert( event.currentTarget instanceof ComboBoxListItemNode, 'currentTarget has wrong type' );
            const listItemNode = event.currentTarget;

            //TODO address sun#447
            // fromA11yEnterKey = KeyboardUtil.KEY_ENTER === event.domEvent.keyCode; // only for the enter key
            property.value = listItemNode.item.value;
            hideCallback();
            button.focus();

            // a11y - keep this PDOM attribute in sync
            this.updateActiveDescendant( listItemNode );
          }
        }
      };

      // Compute max item dimensions
      const maxItemWidth = _.maxBy( items, item => item.node.width ).node.width;
      const maxItemHeight = _.maxBy( items, item => item.node.height ).node.height;

      // Uniform dimensions for all highlighted items in the list, highlight overlaps margin by 50%
      const highlightWidth = maxItemWidth + options.xMargin;
      const highlightHeight = maxItemHeight + options.yMargin;

      // Create a node for each item in the list, and attach a listener.
      const listItemNodes = []; // {ComboBoxListItemNode[]}
      items.forEach( ( item, index ) => {

        // Create the list item node
        const listItemNode = new ComboBoxListItemNode( item, highlightWidth, highlightHeight, {
          align: options.align,
          highlightFill: options.highlightFill,

          // highlight overlaps half of margins
          xMargin: 0.5 * options.xMargin,
          left: 0.5 * options.xMargin,
          top: ( 0.5 * options.yMargin ) + ( index * highlightHeight ),
          tandem: item.tandemName ? tandem.createTandem( item.tandemName ) : Tandem.optional,
          a11yLabel: item.a11yLabel
        } );
        listItemNodes.push( listItemNode );

        listItemNode.addInputListener( itemListener );
      } );

      const content = new VBox( {
        spacing: 0,
        children: listItemNodes
      } );

      // Adjust margins to account for highlight overlap
      options.xMargin = options.xMargin / 2;
      options.yMargin = options.yMargin / 2;

      super( content, options );

      //TODO sun#314 say more here
      // a11y - the list is labeled by the button's label
      this.addAriaLabelledbyAssociation( {
        otherNode: button,
        otherElementName: AccessiblePeer.LABEL_SIBLING,
        thisElementName: AccessiblePeer.PRIMARY_SIBLING
      } );

      // @public {ComboBoxListItemNode|null} the ComboBoxListItemNode that has focus
      this.focusedItemNode = null;

      // Handle keydown on the entire listbox, for a11y
      this.addInputListener( {
        keydown: event => {
          var keyCode = event.domEvent.keyCode;
          if ( keyCode === KeyboardUtil.KEY_ESCAPE ) {
            hideCallback();
            button.focus();
          }
          else if ( keyCode === KeyboardUtil.KEY_TAB ) {
            hideCallback();
          }
          else if ( keyCode === KeyboardUtil.KEY_DOWN_ARROW || keyCode === KeyboardUtil.KEY_UP_ARROW ) {

            // Up/down arrow keys move the focus between items in the listbox
            const direction = ( keyCode === KeyboardUtil.KEY_DOWN_ARROW ) ? 1 : -1;
            for ( let i = 0; i < listItemNodes.length; i++ ) {
              if ( this.focusedItemNode === listItemNodes[ i ] ) {
                const nextListItemNode = listItemNodes[ i + direction ];
                if ( nextListItemNode ) {

                  //TODO sun#314 say more here
                  // a11y - keep this PDOM attribute in sync
                  this.updateActiveDescendant( nextListItemNode );

                  // previous item should not be focusable
                  this.focusedItemNode.focusable = false;
                  this.focusedItemNode = nextListItemNode;
                  this.focusedItemNode.a11yFocusButton();
                  break;
                }
              }
            }
          }
        }
      } );

      // Update focus when the listbox's visibility changes.
      this.on( 'visibility', () => { this.updateFocus(); } );

      // @private
      this.disposeComboBoxListBox = () => {
        for ( let i = 0; i < listItemNodes; i++ ) {
          listItemNodes[ i ].dispose(); // to unregister tandem
        }
      };

      // @private needed by methods
      this.property = property;
      this.listItemNodes = listItemNodes;
    }

    /**
     * @public
     * @override
     */
    dispose() {
      this.disposeComboBoxListBox();
      super.dispose();
    }

    /**
     * Updates the focus to match the currently selected value.
     * @private
     */
    updateFocus() {
      if ( this.visible ) {

        // listbox is visible, focus on selected item
        for ( let i = 0; i < this.listItemNodes.length; i++ ) {
          const listItemNode = this.listItemNodes[ i ];
          if ( this.property.value === listItemNode.item.value ) {
            this.focusedItemNode = listItemNode;
            this.focusedItemNode.a11yFocusButton();
          }
        }
      }
      else if ( this.focusedItemNode ) {

        // listbox is invisible, clear focus
        this.focusedItemNode.focusable = false;
        this.focusedItemNode = null;
      }
    }

    // TODO: sun#314 we don't likely need this anymore
    /**
     * Updates this attribute on the listbox.
     * This changes as you interact with the comboBox, as well as when an item is selected.
     * @param {ComboBoxListItemNode} listItemNode
     * @private
     */
    updateActiveDescendant( listItemNode ) {

      // overwrite purposefully
      this.activeDescendantAssociations = [ {
        otherNode: listItemNode,
        thisElementName: AccessiblePeer.PRIMARY_SIBLING,
        otherElementName: AccessiblePeer.PRIMARY_SIBLING
      } ];
    }
  }

  return sun.register( 'ComboBoxListBox', ComboBoxListBox );
} );<|endoftext|>


<commit_before>'use strict';

const test = require('tap').test;
const ecstatic = require('../lib/ecstatic');
const http = require('http');
const request = require('request');

const root = `${__dirname}/public`;

test('cors defaults to false', (t) => {
  t.plan(4);

  const server = http.createServer(
    ecstatic({
      root,
      autoIndex: true,
      defaultExt: 'html',
    })
  );

  server.listen(() => {
    const port = server.address().port;
    const uri = `http://localhost:${port}/subdir/index.html`;

    request.get({ uri }, (err, res) => {
      t.ifError(err);
      t.equal(res.statusCode, 200);
      t.type(res.headers['access-control-allow-origin'], 'undefined');
      t.type(res.headers['access-control-allow-headers'], 'undefined');
    });
  });
  t.once('end', () => {
    server.close();
  });
});

test('cors set to false', (t) => {
  t.plan(4);

  const server = http.createServer(
    ecstatic({
      root,
      cors: false,
      autoIndex: true,
      defaultExt: 'html',
    })
  );

  server.listen(() => {
    const port = server.address().port;
    const uri = `http://localhost:${port}/subdir/index.html`;

    request.get({ uri }, (err, res) => {
      t.ifError(err);
      t.equal(res.statusCode, 200);
      t.type(res.headers['access-control-allow-origin'], 'undefined');
      t.type(res.headers['access-control-allow-headers'], 'undefined');
    });
  });
  t.once('end', () => {
    server.close();
  });
});

test('cors set to true', (t) => {
  t.plan(4);

  const server = http.createServer(
    ecstatic({
      root,
      cors: true,
      autoIndex: true,
      defaultExt: 'html',
    })
  );

  server.listen(() => {
    const port = server.address().port;
    const uri = `http://localhost:${port}/subdir/index.html`;
    request.get({ uri }, (err, res) => {
      t.ifError(err);
      t.equal(res.statusCode, 200);
      t.equal(res.headers['access-control-allow-origin'], '*');
      t.equal(res.headers['access-control-allow-headers'], 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since');
    });
  });
  t.once('end', () => {
    server.close();
  });
});

test('CORS set to true', (t) => {
  t.plan(4);

  const server = http.createServer(
    ecstatic({
      root,
      CORS: true,
      autoIndex: true,
      defaultExt: 'html',
    })
  );

  server.listen(() => {
    const port = server.address().port;
    const uri = `http://localhost:${port}/subdir/index.html`;
    request.get({ uri }, (err, res) => {
      t.ifError(err);
      t.equal(res.statusCode, 200);
      t.equal(res.headers['access-control-allow-origin'], '*');
      t.equal(res.headers['access-control-allow-headers'], 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since');
    });
  });
  t.once('end', () => {
    server.close();
  });
});
<commit_msg>Minor tweaks/fixes to CORS tests<commit_after>'use strict';

const test = require('tap').test;
const ecstatic = require('../lib/ecstatic');
const http = require('http');
const path = require('path');
const request = require('request');

const root = path.join(__dirname, 'public');

test('cors defaults to false', (t) => {
  t.plan(4);

  const server = http.createServer(
    ecstatic({
      root,
      autoIndex: true,
      defaultExt: 'html',
    })
  );

  server.listen(() => {
    const port = server.address().port;
    const uri = `http://localhost:${port}/subdir/index.html`;

    request.get({ uri }, (err, res) => {
      t.ifError(err);
      t.equal(res.statusCode, 200);
      t.type(res.headers['access-control-allow-origin'], 'undefined');
      t.type(res.headers['access-control-allow-headers'], 'undefined');
    });
  });
  t.once('end', () => {
    server.close();
  });
});

test('cors set to false', (t) => {
  t.plan(4);

  const server = http.createServer(
    ecstatic({
      root,
      cors: false,
      autoIndex: true,
      defaultExt: 'html',
    })
  );

  server.listen(() => {
    const port = server.address().port;
    const uri = `http://localhost:${port}/subdir/index.html`;

    request.get({ uri }, (err, res) => {
      t.ifError(err);
      t.equal(res.statusCode, 200);
      t.type(res.headers['access-control-allow-origin'], 'undefined');
      t.type(res.headers['access-control-allow-headers'], 'undefined');
    });
  });
  t.once('end', () => {
    server.close();
  });
});

test('cors set to true', (t) => {
  t.plan(4);

  const server = http.createServer(
    ecstatic({
      root,
      cors: true,
      autoIndex: true,
      defaultExt: 'html',
    })
  );

  server.listen(() => {
    const port = server.address().port;
    const uri = `http://localhost:${port}/subdir/index.html`;
    request.get({ uri }, (err, res) => {
      t.ifError(err);
      t.equal(res.statusCode, 200);
      t.equal(res.headers['access-control-allow-origin'], '*');
      t.equal(res.headers['access-control-allow-headers'], 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since');
    });
  });
  t.once('end', () => {
    server.close();
  });
});

test('CORS set to true', (t) => {
  t.plan(4);

  const server = http.createServer(
    ecstatic({
      root,
      CORS: true,
      autoIndex: true,
      defaultExt: 'html',
    })
  );

  server.listen(() => {
    const port = server.address().port;
    const uri = `http://localhost:${port}/subdir/index.html`;
    request.get({ uri }, (err, res) => {
      t.ifError(err);
      t.equal(res.statusCode, 200);
      t.equal(res.headers['access-control-allow-origin'], '*');
      t.equal(res.headers['access-control-allow-headers'], 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since');
    });
  });
  t.once('end', () => {
    server.close();
  });
});
<|endoftext|>


<commit_before>// Copyright 2014 Endless Mobile, Inc.

const EosKnowledgePrivate = imports.gi.EosKnowledgePrivate;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;

const Module = imports.app.interfaces.module;
const ImagePreviewer = imports.app.widgets.imagePreviewer;
const Utils = imports.app.utils;

/**
 * Class: App
 *
 * A module for the application's logo.
 *
 * CSS classes:
 *   app-banner - on the module itself
 *   subtitle - on the label displaying the app's subtitle
 */
const App = new Module.Class({
    Name: 'AppBanner',
    CssName: 'EknAppBanner',
    Extends: Gtk.Grid,

    Properties: {
        /**
         * Property: image-uri
         * A URI to the title image. Defaults to an empty string.
         */
        'image-uri': GObject.ParamSpec.string('image-uri', 'Page Title Image URI',
            'URI to the title image',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, ''),
        /**
         * Property: show-subtitle
         * Whether to show an application subtitle underneath the image.
         * Subtitle will be taken from the desktop file description field.
         */
        'show-subtitle': GObject.ParamSpec.boolean('show-subtitle',
            'Show Subtitle', 'Show Subtitle',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            false),
        /**
         * Property: subtitle-capitalization
         * Manner in which the app's subtitle is formatted
         *
         * This property is a temporary stand-in for achieving this via the CSS
         * *text-transform* property.
         */
        'subtitle-capitalization': GObject.ParamSpec.enum('subtitle-capitalization',
            'Subtitle capitalization',
            'Manner in which the subtitle is formatted',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            EosKnowledgePrivate.TextTransformType,
            EosKnowledgePrivate.TextTransform.NONE),
        /**
         * Property: min-fraction
         * Delegates to <ImagePreviewer.min-fraction>
         *
         * Allows setting this property on the logo widget from the app.json.
         * It's construct only for simplicity.
         */
        'min-fraction': GObject.ParamSpec.float('min-fraction', 'Min fraction',
            'Min fraction of size to display the logo at',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            0.0, 1.0, 0.0),

        /**
         * Property: max-fraction
         * Delegates to <ImagePreviewer.max-fraction>
         *
         * Allows setting this property on the logo widget from the app.json.
         * It's construct only for simplicity.
         */
        'max-fraction': GObject.ParamSpec.float('max-fraction', 'Max fraction',
            'Max fraction of size to display the logo at',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            0.0, 1.0, 1.0),
    },

    Template: 'resource:///com/endlessm/knowledge/data/widgets/appBanner.ui',
    InternalChildren: [ 'subtitle-label' ],

    _init: function (props={}) {
        // We don't want the module to autoexpand, but it will unless explicitly
        // forced not to because logo child has expand=true set.
        props.expand = props.expand || false;
        this.parent(props);

        this._logo = new ImagePreviewer.ImagePreviewer({
            visible: true,
            min_fraction: this.min_fraction,
            max_fraction: this.max_fraction,
            expand: true,
            valign: Gtk.Align.END,
        });
        this.attach(this._logo, 0, 0, 1, 1);

        if (this.image_uri) {
            let stream = Gio.File.new_for_uri(this.image_uri).read(null);
            this._logo.set_content(stream);
        }

        let subtitle = Utils.get_desktop_app_info().get_description();
        if (this.show_subtitle && subtitle) {
            subtitle = Utils.format_capitals(subtitle, this.subtitle_capitalization);
            // 758 = 0.74 px * 1024 Pango units / px
            // FIXME: Should be achievable through CSS when we fix GTK
            this._subtitle_label.label = ('' +
                GLib.markup_escape_text(subtitle, -1) + '');
            this._subtitle_label.justify = Utils.alignment_to_justification(this.halign);
        }
        this._subtitle_label.visible = this.show_subtitle;
    },

    set subtitle(value) {
        if (this._subtitle === value)
            return;
        this._subtitle = value;
        this.notify('subtitle');
    },

    get subtitle() {
        return this._subtitle || '';
    },
});
<commit_msg>Check if desktop app info exists before extracting subtitle from it<commit_after>// Copyright 2014 Endless Mobile, Inc.

const EosKnowledgePrivate = imports.gi.EosKnowledgePrivate;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;

const Module = imports.app.interfaces.module;
const ImagePreviewer = imports.app.widgets.imagePreviewer;
const Utils = imports.app.utils;

/**
 * Class: App
 *
 * A module for the application's logo.
 *
 * CSS classes:
 *   app-banner - on the module itself
 *   subtitle - on the label displaying the app's subtitle
 */
const App = new Module.Class({
    Name: 'AppBanner',
    CssName: 'EknAppBanner',
    Extends: Gtk.Grid,

    Properties: {
        /**
         * Property: image-uri
         * A URI to the title image. Defaults to an empty string.
         */
        'image-uri': GObject.ParamSpec.string('image-uri', 'Page Title Image URI',
            'URI to the title image',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, ''),
        /**
         * Property: show-subtitle
         * Whether to show an application subtitle underneath the image.
         * Subtitle will be taken from the desktop file description field.
         */
        'show-subtitle': GObject.ParamSpec.boolean('show-subtitle',
            'Show Subtitle', 'Show Subtitle',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            false),
        /**
         * Property: subtitle-capitalization
         * Manner in which the app's subtitle is formatted
         *
         * This property is a temporary stand-in for achieving this via the CSS
         * *text-transform* property.
         */
        'subtitle-capitalization': GObject.ParamSpec.enum('subtitle-capitalization',
            'Subtitle capitalization',
            'Manner in which the subtitle is formatted',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            EosKnowledgePrivate.TextTransformType,
            EosKnowledgePrivate.TextTransform.NONE),
        /**
         * Property: min-fraction
         * Delegates to <ImagePreviewer.min-fraction>
         *
         * Allows setting this property on the logo widget from the app.json.
         * It's construct only for simplicity.
         */
        'min-fraction': GObject.ParamSpec.float('min-fraction', 'Min fraction',
            'Min fraction of size to display the logo at',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            0.0, 1.0, 0.0),

        /**
         * Property: max-fraction
         * Delegates to <ImagePreviewer.max-fraction>
         *
         * Allows setting this property on the logo widget from the app.json.
         * It's construct only for simplicity.
         */
        'max-fraction': GObject.ParamSpec.float('max-fraction', 'Max fraction',
            'Max fraction of size to display the logo at',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            0.0, 1.0, 1.0),
    },

    Template: 'resource:///com/endlessm/knowledge/data/widgets/appBanner.ui',
    InternalChildren: [ 'subtitle-label' ],

    _init: function (props={}) {
        // We don't want the module to autoexpand, but it will unless explicitly
        // forced not to because logo child has expand=true set.
        props.expand = props.expand || false;
        this.parent(props);

        this._logo = new ImagePreviewer.ImagePreviewer({
            visible: true,
            min_fraction: this.min_fraction,
            max_fraction: this.max_fraction,
            expand: true,
            valign: Gtk.Align.END,
        });
        this.attach(this._logo, 0, 0, 1, 1);

        if (this.image_uri) {
            let stream = Gio.File.new_for_uri(this.image_uri).read(null);
            this._logo.set_content(stream);
        }

        let subtitle = '';
        let app_info = Utils.get_desktop_app_info();
        if (app_info) {
            subtitle = app_info.get_description();
        }
        if (this.show_subtitle && subtitle) {
            subtitle = Utils.format_capitals(subtitle, this.subtitle_capitalization);
            // 758 = 0.74 px * 1024 Pango units / px
            // FIXME: Should be achievable through CSS when we fix GTK
            this._subtitle_label.label = ('' +
                GLib.markup_escape_text(subtitle, -1) + '');
            this._subtitle_label.justify = Utils.alignment_to_justification(this.halign);
        }
        this._subtitle_label.visible = this.show_subtitle;
    },

    set subtitle(value) {
        if (this._subtitle === value)
            return;
        this._subtitle = value;
        this.notify('subtitle');
    },

    get subtitle() {
        return this._subtitle || '';
    },
});
<|endoftext|>


<commit_before>const util = require('util');

module.exports = function (label = '') {
    const debug = util.debuglog(label);
    const enabled = debug.toString() !== (function () {}).toString();
    Object.defineProperty(debug, 'enabled', { get() { return enabled; } });
    return debug;
};<commit_msg>Refactor debug.js<commit_after>const util = require('util');

module.exports = function (label = '') {
    const debug = util.debuglog(label);
    const tr = (object) => object && object.toString().replace(/\s/g, '');
    const equalsIgnoreSpace = (a, b) => tr(a) === tr(b);
    const isDoNothingFunction = (fn) => equalsIgnoreSpace(fn, function () { });
    const enabled = !isDoNothingFunction(debug);
    Object.defineProperty(debug, 'enabled', { get() { return enabled; } });
    return debug;
};<|endoftext|>


<commit_before>var jsonfile = './browscap.preprocessed.json';

exports.setJson = function (filename) {
  jsonfile = filename;
};

exports.getBrowser = function (userAgent) {
  patterns = require(jsonfile).patterns;
  browsers = require(jsonfile).browsers;

  // Test user agent against each browser regex
  for (var pattern in patterns) {
    pattern = pattern.replace('@', '');
    re = new RegExp(pattern, 'i');

    if (re.test(userAgent)) {
      key     = patterns[pattern];
      found   = false;
      matches = userAgent.match(re);

      if (matches.length === 1) {
        browsersindex = key;
        found = true;
      } else {
        matchString = '@' + matches.join('|');

        if (key[matchString]) {
          browsersindex = key[matchString];
          found = true;
        }
      }

      if (found && browsers[browsersindex]) {
        browser = [userAgent, pattern.toLowerCase().trim()];
        browserData = JSON.parse(browsers[browsersindex]);

        for (var property in browserData) {
          browser[property] = browserData[property];
        }

        while (browserData['3']) {
          browserData = JSON.parse(browsers[browserData[3]]);

          for (var property in browserData) {
            if (!browser[property]) {
              browser[property] = browserData[property];
            }
          }
        }

        if (browser['3']) {
          userAgents = require(jsonfile).userAgents;
          browser['3'] = userAgents[browser['3']];
        }

        ua = {};
        properties = require(jsonfile).properties;

        for (var propertyNumber in browser) {
          propertyName = properties[propertyNumber];
          ua[propertyName] = browser[propertyNumber];
        }

        return ua;
      }
    }
  }

  return null;
};
<commit_msg>change parent detection<commit_after>var jsonfile = './browscap.preprocessed.json';

exports.setJson = function (filename) {
  jsonfile = filename;
};

exports.getBrowser = function (userAgent) {
  var browsers, patterns, re, key, found, matches, browsersindex;

  patterns = require(jsonfile).patterns;
  browsers = require(jsonfile).browsers;

  // Test user agent against each browser regex
  for (var pattern in patterns) {
    pattern = pattern.replace('@', '');
    re = new RegExp(pattern, 'i');

    if (re.test(userAgent)) {
      key = patterns[pattern];
      found = false;
      matches = userAgent.match(re);

      if (matches.length === 1) {
        browsersindex = key;
        found = true;
      } else {
        var matchString = '@' + matches.join('|');

        if (key[matchString]) {
          browsersindex = key[matchString];
          found = true;
        }
      }

      if (found && browsers[browsersindex]) {
        var browser = [userAgent, pattern.toLowerCase().trim()];
        var browserData = JSON.parse(browsers[browsersindex]);

        for (var property in browserData) {
          browser[property] = browserData[property];
        }

        while (browserData['Parent']) {
          browserData = JSON.parse(browsers[browserData['Parent']]);

          for (var property in browserData) {
            if (!browser[property]) {
              browser[property] = browserData[property];
            }
          }
        }

        if (browser['Parent']) {
          var userAgents = require(jsonfile).userAgents;
          browser['Parent'] = userAgents[browser['Parent']];
        }

        return browser;
      }
    }
  }

  return null;
};
<|endoftext|>


<commit_before>import Ember from 'ember';

const numbers = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];

export default Ember.Controller.extend({
  cities: ['Barcelona', 'London', 'New York', 'Porto', 'Coruña', 'Kracow', 'Siena', 'Portland', 'Springfield', 'Tokio'],
  destination: 'London',
  selectedCities: [],
  numbers,
  counter: 8,
  destroyed: Ember.computed.lte('counter', 0),

  actions: {
    chooseDestination(city) {
      this.set('destination', city);
    },

    handleKeydown(dropdown, e) {
      if (e.keyCode !== 13) {
        return;
      }
      let text = e.target.value;
      if (text.length > 0 && this.get('cities').indexOf(text) === -1) {
        this.get('selectedCities').pushObject(text);
      }
    },

    handleFocus(select, e) {
      let blurredEl = e.relatedTarget
      if (!blurredEl || !blurredEl.classList.contains('ember-power-select-search-input')) {
        select.actions.open();
      }
    },

    handleBlur() {
      /* eslint-disable */
      console.debug('EPS blurred!');
      /* eslint-enable */
    },

    startSelfDestroyCountdown() {
      let tick = () => {
        this.decrementProperty('counter');
        if (!this.get('destroyed')) {
          Ember.run.later(tick, 1000);
        }
      };
      this.set('countdown', Ember.run.later(tick, 1000));
    },

    verifyPresence(/* select, e */) {
      if (this.get('mandatoryNumber')) {
        this.set('selectClass', null);
      } else {
        this.set('selectClass', 'has-error');
        return false;
      }
    },

    checkLength(text, select) {
      if (select.searchText.length >= 3 && text.length < 3) {
        return '';
      } else {
        return text.length >= 3;
      }
    }
  }
});<commit_msg>Fix eslint error<commit_after>import Ember from 'ember';

const numbers = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];

export default Ember.Controller.extend({
  cities: ['Barcelona', 'London', 'New York', 'Porto', 'Coruña', 'Kracow', 'Siena', 'Portland', 'Springfield', 'Tokio'],
  destination: 'London',
  selectedCities: [],
  numbers,
  counter: 8,
  destroyed: Ember.computed.lte('counter', 0),

  actions: {
    chooseDestination(city) {
      this.set('destination', city);
    },

    handleKeydown(dropdown, e) {
      if (e.keyCode !== 13) {
        return;
      }
      let text = e.target.value;
      if (text.length > 0 && this.get('cities').indexOf(text) === -1) {
        this.get('selectedCities').pushObject(text);
      }
    },

    handleFocus(select, e) {
      let blurredEl = e.relatedTarget;
      if (!blurredEl || !blurredEl.classList.contains('ember-power-select-search-input')) {
        select.actions.open();
      }
    },

    handleBlur() {
      /* eslint-disable */
      console.debug('EPS blurred!');
      /* eslint-enable */
    },

    startSelfDestroyCountdown() {
      let tick = () => {
        this.decrementProperty('counter');
        if (!this.get('destroyed')) {
          Ember.run.later(tick, 1000);
        }
      };
      this.set('countdown', Ember.run.later(tick, 1000));
    },

    verifyPresence(/* select, e */) {
      if (this.get('mandatoryNumber')) {
        this.set('selectClass', null);
      } else {
        this.set('selectClass', 'has-error');
        return false;
      }
    },

    checkLength(text, select) {
      if (select.searchText.length >= 3 && text.length < 3) {
        return '';
      } else {
        return text.length >= 3;
      }
    }
  }
});<|endoftext|>


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

/**
 * Creates a Property that does synchronization of values with a swappable Property that itself can change.
 * Handles the case where you need a Property that can switch between acting like multiple other properties.
 *
 * With no other options specified, the value of this property is:
 * - null, if valuePropertyProperty.value === null
 * - valuePropertyProperty.value.value otherwise
 *
 * The value of this property (generalized, with the options available) is:
 * - derive( defaultValue ), if valuePropertyProperty.value === null
 * - map( derive( valuePropertyProperty.value ).value ) otherwise
 *
 * Generally, this DynamicProperty uses one-way synchronization (it only listens to the source), but if the
 * 'bidirectional' option is true, it will use two-way synchronization (changes to this property will change the active
 * source). Thus when this property changes value (when bidirectional is true), it will set:
 * - derive( valuePropertyProperty.value ).value = inverseMap( this.value ), if valuePropertyProperty.value !== null
 *
 *******************************
 * General example
 *******************************
 *   var firstProperty = new Property( Color.RED );
 *   var secondProperty = new Property( Color.BLUE );
 *   var currentProperty = new Property( firstProperty ); // {Property.<Property.<Color>>}
 *
 *   var backgroundFill = new DynamicProperty( currentProperty ) // Turns into a {Property.<Color>}
 *   backgroundFill.value; // Color.RED, since: currentProperty.value === firstProperty and
 *                                              firstProperty.value === Color.RED
 *   firstProperty.value = Color.YELLOW;
 *   backgroundFill.value; // Color.YELLOW - It's connected to firstProperty right now
 *
 *   currentProperty.value = secondProperty;
 *   backgroundFill.value; // Color.BLUE - It's the secondProperty's value
 *
 *   secondProperty.value = Color.MAGENTA;
 *   backgroundFill.value; // Color.MAGENTA - Yes, it's listening to the other property now.
 *
 * Also supports falling back to null if our main property is set to null:
 *   currentProperty.value = null;
 *   backgroundFill.value; // null
 *
 *******************************
 * 'derive' option
 *******************************
 * Additionally, DynamicProperty supports the ability to derive the property value from our main property's value.
 * For example, say you have multiple scenes each with the type:
 *   scene: {
 *     backgroundColorProperty: {Property.<Color>}
 *   }
 * and you have a currentSceneProperty: {Property.<Scene>}, you may want to create:
 *   var currentBackgroundColorProperty = new DynamicProperty( currentSceneProperty, {
 *     derive: 'backgroundColorProperty'
 *   } );
 * This would always report the current scene's current background color.
 * What if you sometimes don't have a scene active, e.g. {Property.<Scene|null>}? You can provide a default value:
 *  new DynamicProperty( currentSceneProperty, {
 *    derive: 'backgroundColorProperty',
 *    defaultValue: Color.BLACK
 *  } );
 * So that if the currentSceneProperty's value is null, the value of our DynamicProperty will be Color.BLACK.
 *
 *******************************
 * 'bidirectional' option
 *******************************
 * If you would like for direct changes to this property to change the original source (bidirectional synchronization),
 * then pass bidirectional:true:
 *   var firstProperty = new Property( 5 );
 *   var secondProperty = new Property( 10 );
 *   var numberPropertyProperty = new Property( firstProperty );
 *   var dynamicProperty = new DynamicProperty( numberPropertyProperty, { bidirectional: true } );
 *   dynamicProperty.value = 2; // allowed now that it is bidrectional, otherwise prohibited
 *   firstProperty.value; // 2
 *   numberPropertyProperty.value = secondProperty; // change which property is active
 *   dynamicProperty.value; // 10, from the new property
 *   dynamicProperty.value = 0;
 *   secondProperty.value; // 0, set above.
 *   firstProperty.value; // still 2 from above, since our dynamic property switched to the other property
 *
 *******************************
 * 'map' and 'inverseMap' options
 *******************************
 * DynamicProperty also supports mapping values to different types. For example, say we have a
 * numberPropertyProperty {Property.<Property.<number>>}, but want to have a {Property.<string>} as the output. Then:
 *   new DynamicProperty( numberPropertyProperty, {
 *     map: function( number ) { return '' + number; }
 *   } );
 * will do the trick. If this needs to be done with a bidirectional DynamicProperty, also include inverseMap:
 *   new DynamicProperty( numberPropertyProperty, {
 *     bidirectional: true,
 *     map: function( number ) { return '' + number; },
 *     inverseMap: function( string ) { return Number.parseFloat( string ); }
 *   } );
 * so that changes to the dynamic property will result in a change in the numberPropertyProperty's value.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
define( function( require ) {
  'use strict';

  // modules
  var axon = require( 'AXON/axon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Property = require( 'AXON/Property' );

  /**
   * @constructor
   * @extends {Property}
   *
   * @param {Property.<*|null>} valuePropertyProperty - If the value is null, it is considered disconnected. If the
   *                                                    'derive' option is not used, then this should always have the
   *                                                    type {Property.<Property.<*>|null>}
   * @param {Object} [options] - options
   */
  function DynamicProperty( valuePropertyProperty, options ) {

    options = _.extend( {
      // {boolean} - If set to true then changes to this property (if valuePropertyProperty.value is non-null at the
      //             time) will also be made to derive( valuePropertyProperty.value ).
      bidirectional: false,

      // {*} - If valuePropertyProperty.value === null, this dynamicProperty will act instead like
      //       derive( valuePropertyProperty.value ) === new Property( defaultValue ). Note that if a custom map
      //       function is provided, it will be applied to this defaultValue to determine our property's value.
      defaultValue: null,

      // {function|string} - Maps a non-null valuePropertyProperty.value into the Property to be used. See top-level
      //                     documentation for usage. Uses the Lodash path specification (if it's a string).
      derive: _.identity,

      // {function|string} - Maps our input property value to/from this property's value. See top-level documentation
      //                     for usage. Uses the Lodash path specification (if it's a string).
      map: _.identity,
      inverseMap: _.identity
    }, options );

    // @public {boolean} - Set to true when this Property's value is changing from an external source.
    this.isExternallyChanging = false;

    // @private {Property.<*|null>}
    this.valuePropertyProperty = valuePropertyProperty;

    // @private {boolean}
    this.bidirectional = options.bidirectional;

    // @private {*}
    this.defaultValue = options.defaultValue;

    // @private {function}
    this.derive = typeof options.derive === 'string' ? _.property( options.derive ) : options.derive;
    this.map = typeof options.map === 'string' ? _.property( options.map ) : options.map;
    this.inverseMap = typeof options.inverseMap === 'string' ? _.property( options.inverseMap ) : options.inverseMap;

    // Use the property's initial value
    var initialValue;
    if ( this.valuePropertyProperty.value === null ) {
      initialValue = this.map( this.defaultValue );
    }
    else {
      initialValue = this.map( this.derive( this.valuePropertyProperty.value ).value );
    }

    // Super call
    Property.call( this, initialValue, options );

    // @private {function}
    this.propertyPropertyListener = this.onPropertyPropertyChange.bind( this );

    // @private {function}
    this.propertyListener = this.onPropertyChange.bind( this );

    // Rehook our listener to whatever is the active property.
    valuePropertyProperty.link( this.propertyListener );

    // If we aren't bidirectional, we should never add this listener.
    if ( options.bidirectional ) {
      this.lazyLink( this.onSelfChange.bind( this ) );
    }
  }

  axon.register( 'DynamicProperty', DynamicProperty );

  return inherit( Property, DynamicProperty, {
    /**
     * Listener added to the active inner Property.
     * @private
     *
     * @param {*} value - Should be either our defaultValue (if valuePropertyProperty.value is null), or
     *                    derive( valuePropertyProperty.value ).value otherwise.
     */
    onPropertyPropertyChange: function( value ) {
      // Since we override the setter here, we need to call the version on the prototype
      Property.prototype.set.call( this, this.map( value ) );
    },

    /**
     * Listener added to the outer Property.
     * @private
     *
     * @param {*|null} newPropertyValue - If derive is not provided then it should be a {Property.<*>|null}
     * @param {*|null|undefined} oldPropertyValue - If derive is not provided then it should be a {Property.<*>|null}.
     *                                              We additionally handle the initial link() case where this is
     *                                              undefined.
     */
    onPropertyChange: function( newPropertyValue, oldPropertyValue ) {
      if ( oldPropertyValue ) {
        this.derive( oldPropertyValue ).unlink( this.propertyPropertyListener );
      }
      if ( newPropertyValue ) {
        this.derive( newPropertyValue ).link( this.propertyPropertyListener );
      }
      else {
        // Switch to null when our Property's value is null.
        this.onPropertyPropertyChange( this.defaultValue );
      }
    },

    /**
     * Listener added to ourself when we are bidirectional
     * @private
     *
     * @param {*} value
     */
    onSelfChange: function( value ) {
      if ( this.valuePropertyProperty.value !== null ) {
        this.derive( this.valuePropertyProperty.value ).value = this.inverseMap( value );
      }
    },

    /**
     * Disposes this property
     * @public
     */
    dispose: function() {
      this.valuePropertyProperty.unlink( this.propertyListener );

      if ( this.valuePropertyProperty.value !== null ) {
        this.derive( this.valuePropertyProperty.value ).unlink( this.propertyPropertyListener );
      }

      Property.prototype.dispose.call( this );
    },

    /**
     * Prevent setting this property manually
     * @public
     * @override
     *
     * TODO: DerivedProperty should only need to do this, not all of the other methods it's also doing
     *
     * @param {*} value
     */
    set: function( value ) {
      assert && assert( this.bidirectional, 'Cannot set values directly to a DynamicProperty, tried to set: ' + value );

      this.isExternallyChanging = true;

      Property.prototype.set.call( this, value );

      this.isExternallyChanging = false;
    }
  } );
} );
<commit_msg>property => Property documentation fixes<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * Creates a Property that does synchronization of values with a swappable Property that itself can change.
 * Handles the case where you need a Property that can switch between acting like multiple other properties.
 *
 * With no other options specified, the value of this Property is:
 * - null, if valuePropertyProperty.value === null
 * - valuePropertyProperty.value.value otherwise
 *
 * The value of this Property (generalized, with the options available) is:
 * - derive( defaultValue ), if valuePropertyProperty.value === null
 * - map( derive( valuePropertyProperty.value ).value ) otherwise
 *
 * Generally, this DynamicProperty uses one-way synchronization (it only listens to the source), but if the
 * 'bidirectional' option is true, it will use two-way synchronization (changes to this Property will change the active
 * source). Thus when this Property changes value (when bidirectional is true), it will set:
 * - derive( valuePropertyProperty.value ).value = inverseMap( this.value ), if valuePropertyProperty.value !== null
 *
 *******************************
 * General example
 *******************************
 *   var firstProperty = new Property( Color.RED );
 *   var secondProperty = new Property( Color.BLUE );
 *   var currentProperty = new Property( firstProperty ); // {Property.<Property.<Color>>}
 *
 *   var backgroundFill = new DynamicProperty( currentProperty ) // Turns into a {Property.<Color>}
 *   backgroundFill.value; // Color.RED, since: currentProperty.value === firstProperty and
 *                                              firstProperty.value === Color.RED
 *   firstProperty.value = Color.YELLOW;
 *   backgroundFill.value; // Color.YELLOW - It's connected to firstProperty right now
 *
 *   currentProperty.value = secondProperty;
 *   backgroundFill.value; // Color.BLUE - It's the secondProperty's value
 *
 *   secondProperty.value = Color.MAGENTA;
 *   backgroundFill.value; // Color.MAGENTA - Yes, it's listening to the other Property now.
 *
 * Also supports falling back to null if our main Property is set to null:
 *   currentProperty.value = null;
 *   backgroundFill.value; // null
 *
 *******************************
 * 'derive' option
 *******************************
 * Additionally, DynamicProperty supports the ability to derive the Property value from our main Property's value.
 * For example, say you have multiple scenes each with the type:
 *   scene: {
 *     backgroundColorProperty: {Property.<Color>}
 *   }
 * and you have a currentSceneProperty: {Property.<Scene>}, you may want to create:
 *   var currentBackgroundColorProperty = new DynamicProperty( currentSceneProperty, {
 *     derive: 'backgroundColorProperty'
 *   } );
 * This would always report the current scene's current background color.
 * What if you sometimes don't have a scene active, e.g. {Property.<Scene|null>}? You can provide a default value:
 *  new DynamicProperty( currentSceneProperty, {
 *    derive: 'backgroundColorProperty',
 *    defaultValue: Color.BLACK
 *  } );
 * So that if the currentSceneProperty's value is null, the value of our DynamicProperty will be Color.BLACK.
 *
 *******************************
 * 'bidirectional' option
 *******************************
 * If you would like for direct changes to this Property to change the original source (bidirectional synchronization),
 * then pass bidirectional:true:
 *   var firstProperty = new Property( 5 );
 *   var secondProperty = new Property( 10 );
 *   var numberPropertyProperty = new Property( firstProperty );
 *   var dynamicProperty = new DynamicProperty( numberPropertyProperty, { bidirectional: true } );
 *   dynamicProperty.value = 2; // allowed now that it is bidrectional, otherwise prohibited
 *   firstProperty.value; // 2
 *   numberPropertyProperty.value = secondProperty; // change which Property is active
 *   dynamicProperty.value; // 10, from the new Property
 *   dynamicProperty.value = 0;
 *   secondProperty.value; // 0, set above.
 *   firstProperty.value; // still 2 from above, since our dynamic Property switched to the other Property
 *
 *******************************
 * 'map' and 'inverseMap' options
 *******************************
 * DynamicProperty also supports mapping values to different types. For example, say we have a
 * numberPropertyProperty {Property.<Property.<number>>}, but want to have a {Property.<string>} as the output. Then:
 *   new DynamicProperty( numberPropertyProperty, {
 *     map: function( number ) { return '' + number; }
 *   } );
 * will do the trick. If this needs to be done with a bidirectional DynamicProperty, also include inverseMap:
 *   new DynamicProperty( numberPropertyProperty, {
 *     bidirectional: true,
 *     map: function( number ) { return '' + number; },
 *     inverseMap: function( string ) { return Number.parseFloat( string ); }
 *   } );
 * so that changes to the dynamic Property will result in a change in the numberPropertyProperty's value.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
define( function( require ) {
  'use strict';

  // modules
  var axon = require( 'AXON/axon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Property = require( 'AXON/Property' );

  /**
   * @constructor
   * @extends {Property}
   *
   * @param {Property.<*|null>} valuePropertyProperty - If the value is null, it is considered disconnected. If the
   *                                                    'derive' option is not used, then this should always have the
   *                                                    type {Property.<Property.<*>|null>}
   * @param {Object} [options] - options
   */
  function DynamicProperty( valuePropertyProperty, options ) {

    options = _.extend( {
      // {boolean} - If set to true then changes to this Property (if valuePropertyProperty.value is non-null at the
      //             time) will also be made to derive( valuePropertyProperty.value ).
      bidirectional: false,

      // {*} - If valuePropertyProperty.value === null, this dynamicProperty will act instead like
      //       derive( valuePropertyProperty.value ) === new Property( defaultValue ). Note that if a custom map
      //       function is provided, it will be applied to this defaultValue to determine our Property's value.
      defaultValue: null,

      // {function|string} - Maps a non-null valuePropertyProperty.value into the Property to be used. See top-level
      //                     documentation for usage. Uses the Lodash path specification (if it's a string).
      derive: _.identity,

      // {function|string} - Maps our input Property value to/from this Property's value. See top-level documentation
      //                     for usage. Uses the Lodash path specification (if it's a string).
      map: _.identity,
      inverseMap: _.identity
    }, options );

    // @public {boolean} - Set to true when this Property's value is changing from an external source.
    this.isExternallyChanging = false;

    // @private {Property.<*|null>}
    this.valuePropertyProperty = valuePropertyProperty;

    // @private {boolean}
    this.bidirectional = options.bidirectional;

    // @private {*}
    this.defaultValue = options.defaultValue;

    // @private {function}
    this.derive = typeof options.derive === 'string' ? _.property( options.derive ) : options.derive;
    this.map = typeof options.map === 'string' ? _.property( options.map ) : options.map;
    this.inverseMap = typeof options.inverseMap === 'string' ? _.property( options.inverseMap ) : options.inverseMap;

    // Use the Property's initial value
    var initialValue;
    if ( this.valuePropertyProperty.value === null ) {
      initialValue = this.map( this.defaultValue );
    }
    else {
      initialValue = this.map( this.derive( this.valuePropertyProperty.value ).value );
    }

    // Super call
    Property.call( this, initialValue, options );

    // @private {function}
    this.propertyPropertyListener = this.onPropertyPropertyChange.bind( this );

    // @private {function}
    this.propertyListener = this.onPropertyChange.bind( this );

    // Rehook our listener to whatever is the active Property.
    valuePropertyProperty.link( this.propertyListener );

    // If we aren't bidirectional, we should never add this listener.
    if ( options.bidirectional ) {
      this.lazyLink( this.onSelfChange.bind( this ) );
    }
  }

  axon.register( 'DynamicProperty', DynamicProperty );

  return inherit( Property, DynamicProperty, {
    /**
     * Listener added to the active inner Property.
     * @private
     *
     * @param {*} value - Should be either our defaultValue (if valuePropertyProperty.value is null), or
     *                    derive( valuePropertyProperty.value ).value otherwise.
     */
    onPropertyPropertyChange: function( value ) {
      // Since we override the setter here, we need to call the version on the prototype
      Property.prototype.set.call( this, this.map( value ) );
    },

    /**
     * Listener added to the outer Property.
     * @private
     *
     * @param {*|null} newPropertyValue - If derive is not provided then it should be a {Property.<*>|null}
     * @param {*|null|undefined} oldPropertyValue - If derive is not provided then it should be a {Property.<*>|null}.
     *                                              We additionally handle the initial link() case where this is
     *                                              undefined.
     */
    onPropertyChange: function( newPropertyValue, oldPropertyValue ) {
      if ( oldPropertyValue ) {
        this.derive( oldPropertyValue ).unlink( this.propertyPropertyListener );
      }
      if ( newPropertyValue ) {
        this.derive( newPropertyValue ).link( this.propertyPropertyListener );
      }
      else {
        // Switch to null when our Property's value is null.
        this.onPropertyPropertyChange( this.defaultValue );
      }
    },

    /**
     * Listener added to ourself when we are bidirectional
     * @private
     *
     * @param {*} value
     */
    onSelfChange: function( value ) {
      if ( this.valuePropertyProperty.value !== null ) {
        this.derive( this.valuePropertyProperty.value ).value = this.inverseMap( value );
      }
    },

    /**
     * Disposes this Property
     * @public
     */
    dispose: function() {
      this.valuePropertyProperty.unlink( this.propertyListener );

      if ( this.valuePropertyProperty.value !== null ) {
        this.derive( this.valuePropertyProperty.value ).unlink( this.propertyPropertyListener );
      }

      Property.prototype.dispose.call( this );
    },

    /**
     * Prevent setting this Property manually
     * @public
     * @override
     *
     * TODO: DerivedProperty should only need to do this, not all of the other methods it's also doing
     *
     * @param {*} value
     */
    set: function( value ) {
      assert && assert( this.bidirectional, 'Cannot set values directly to a DynamicProperty, tried to set: ' + value );

      this.isExternallyChanging = true;

      Property.prototype.set.call( this, value );

      this.isExternallyChanging = false;
    }
  } );
} );
<|endoftext|>


<commit_before>JSONEditor.defaults.templates.underscore = function() {
  if(!window._) return false;

  return {
    compile: function(template) {
      return function(context) {
        return window._.template(template, context);
      };
    }
  };
};
<commit_msg>Support underscore 1.7 and newer<commit_after>JSONEditor.defaults.templates.underscore = function() {
  if(!window._) return false;

  return {
    compile: function(template) {
      return function(context) {
        return window._.template(template)(context);
      };
    }
  };
};
<|endoftext|>


<commit_before>(function() {
  function applicationsModuleController(appConfig, dbHandler) {
    var applicationsModel = require('./model')(appConfig, dbHandler);

    function doListAll(requisition, response) {
      var query = {};

      applicationsModel.find(query, function(error, data) {
        if (error) {
          response.status(500).send(error).end();
        }

        return response.status(200).send(data).end();
      });
    };

    function generateABust() {
      var date = new Date();
      return date.getTime();
    };

    function generateClientId() {
      return 'THISISACLIENTID' + generateABust();
    };

    function generateClientSecret() {
      return 'THISISACLIENTSECRET' + generateABust();
    };

    function doCreate(requisition, response) {
      var data = requisition.body;

      data.clientId = generateClientId();
      data.clientSecret = generateClientSecret();

      var newApplication = new applicationsModel(data);

      newApplication.save(function(error, data) {
        if (error) {
          response.status(500).send(error).end();
        }

        return response.status(201).send(data).end();
      });
    };

    function doRetrieve() {

    };

    function doUpdate() {

    };

    function doDelete() {

    };

    var controller = {
      listAll: doListAll,
      create: doCreate,
      retrieve: doRetrieve,
      update: doUpdate,
      delete: doDelete
    };

    return controller;

  };

  module.exports = applicationsModuleController;
})();<commit_msg>Applications CRUD Api<commit_after>(function() {
  function applicationsModuleController(appConfig, dbHandler) {
    var applicationsModel = require('./model')(appConfig, dbHandler);

    function doListAll(requisition, response) {
      var query = {};

      applicationsModel.find(query, function(error, data) {
        if (error) {
          return response.status(500).send(error).end();
        }

        return response.status(200).send(data).end();
      });
    };

    function generateABust() {
      var date = new Date();
      return date.getTime();
    };

    function generateClientId() {
      return 'THISISACLIENTID' + generateABust();
    };

    function generateClientSecret() {
      return 'THISISACLIENTSECRET' + generateABust();
    };

    function doCreate(requisition, response) {
      var data = requisition.body;

      data.clientId = generateClientId();
      data.clientSecret = generateClientSecret();

      var newApplication = new applicationsModel(data);

      newApplication.save(function(error, data) {
        if (error) {
          return response.status(500).send(error).end();
        }

        return response.status(201).send(data).end();
      });
    };

    function doRetrieve(requisition, response) {
      var query = {_id: requisition.params.id};

      applicationsModel.findOne(query, function(error, data) {
        if (error) {
          return response.status(400).send(error).end();
        }

        return response.status(200).send(data).end();
      });
    };

    function doUpdate(requisition, response) {
      var query = {_id: requisition.params.id};

      var update = requisition.body;

      applicationsModel.update(query, update, function(error, data) {
        if (error) {
          return response.status(400).send(error).end();
        }

        return response.status(200).send(data).end();
      });
    };

    function doDelete(requisition, response) {
      var query = {_id: requisition.params.id};

      applicationsModel.remove(query, function(error, data) {
        if (error) {
          return response.status(400).send(error).end();
        }

        return response.status(200).send(data).end();
      });
    };

    var controller = {
      listAll: doListAll,
      create: doCreate,
      retrieve: doRetrieve,
      update: doUpdate,
      delete: doDelete
    };

    return controller;

  };

  module.exports = applicationsModuleController;
})();<|endoftext|>


<commit_before>function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.projectId = $("#projectHeader").data('value');

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);
    gvm.projectDescription = ko.observable("Description");

    gvm.firstNameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("firstNameTableTitle");}, gvm);
    gvm.lastNameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("lastNameTableTitle");}, gvm);
    gvm.actionTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ActionTableTitle");}, gvm);

    gvm.scoreTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("scoreTableTitle")});
    gvm.filesTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("filesTableTitle")});

    gvm.searchStudent = ko.observable("");

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + gvm.projectId, function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
            gvm.projectDescription(data[0].description);
        });
    };

    gvm.getStudentListBis = function() {
        $.getJSON('/api/project/' + gvm.projectId + '/students', function(data) {
            data.forEach(function(element){
                viewModel.addTableData(element.id, element.firstname, element.lastname, element.mail, element.assessCount);
            });
        });
    };

    gvm.getStudentList = function() {
        $.getJSON('/api/project/' + gvm.projectId + '/students/', function(data) {
            $.each(data, function(i, item) {

                $.getJSON('/api/project/' + gvm.projectid  + '/student/' + item.id + '/assessed', function(data)
                {
                    viewModel.users.removeAll();
                    data.forEach(function(element)
                    {
                        viewModel.users.push(element.firstname + " " + element.lastname);
                    });

                    viewModel.addTableData(item.id, item.firstname, item.lastname, item.mail, viewModel.users().length);
                });
            });
        });
    };

    gvm.users = ko.observableArray([]);

    gvm.assassedUsers = ko.observableArray([]);

    gvm.tabledata = ko.observableArray([]);

    gvm.tempTableData = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, firstname, lastname, email, countAssessed) {
        // Push data
        var data = countAssessed;
        var completed = false;

        console.log(data);
        console.log("test: " + getNrOfAssessing());
        if (data == getNrOfAssessing()){
            console.log("true");
            completed = true;
        }

        var tblOject = {tid: id, tfirstname: firstname, tlastname: lastname, tScoreTableBtn: gvm.scoreTableTitle, tFilesTableBtn: gvm.filesTableTitle, tpid: gvm.projectId, email: email, tcountAssessed: data, tAssessedCompleted: completed};
        gvm.tabledata.push(tblOject);
    };

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    };

    /*gvm.getAllData = function() {
        $.getJSON('/api/project/getAllData/' + $("#projectHeader").data('value'), function(data) {
            console.log(data);
        });
    }*/
}

function initPage() {
    viewModel.getProjectInfo();
    viewModel.getStudentListBis();
    //viewModel.getStudentList();

    $("#searchField").bind("keypress", {}, keypressInBox);

}

function keypressInBox(e) {
    var code = (e.keyCode ? e.keyCode : e.which);
    if (code == 13) { //Enter keycode
        $("#searchField").blur();
        getStudentByName();

    }
};

function createPDF(id,name,lastname,email, projectheader, projectdescription)
{
    /*$.getJSON('/api/finalscore/' + viewModel.projectId + '/' + id, function (data) {
        makePDF(id,name,lastname,email,projectheader,projectdescription,data);
        //console.log(data);
    });*/

    $.ajax({
        url: '/api/finalscore/' + viewModel.projectId + '/' + id,
        type: "GET",
        dataType: 'json',
        success: function(data) {
            makePDF(id,name,lastname,email,projectheader,projectdescription,data);
        },
        error: function()
        {
            alert("Please make sure points are given!");
        }
    });
}

function getStudentByName(){

    if(viewModel.tempTableData().length == 0) {
        viewModel.tempTableData(viewModel.tabledata.slice(0));
    }

    viewModel.tabledata([]);

    viewModel.tempTableData().forEach(function(item, element){
        var fullname = item.tfirstname + " " + item.tlastname;
        var fullnameReverse = item.tlastname + " " + item.tfirstname;

        if (item.tfirstname.toLowerCase().contains(viewModel.searchStudent().toLowerCase()) || item.tlastname.toLowerCase().contains(viewModel.searchStudent().toLowerCase()) || fullname.toLowerCase().contains(viewModel.searchStudent().toLowerCase()) || fullnameReverse.toLowerCase().contains(viewModel.searchStudent().toLowerCase())){
            viewModel.tabledata.push(item);
        }
    });
}

function showFullList()
{
    viewModel.searchStudent = ko.observable("");
    getStudentByName();
}

function getData(id)
{
    $.getJSON('/api/project/' + viewModel.projectId  + '/student/' + id + '/assessed', function(data)
    {
        viewModel.users.removeAll();
        data.forEach(function(element)
        {
            viewModel.users.push(element.firstname + " " + element.lastname);
        });
    });

    return viewModel.users();
}

function getNrOfAssessing(){
    var nr;

    $.getJSON('/api/project/nrOfAssessing/' + viewModel.projectId , function(data)
    {
        data.forEach(function(item)
        {
            //console.log(item.nrOfAssessing);

            this.nr = item.nrOfAssessing;
        });
    });

    return nr;
}

function getDataCount(projectid, id)
{
    $.getJSON('/api/project/' + projectid  + '/student/' + id + '/assessed', function(data)
    {
        viewModel.users.removeAll();
        data.forEach(function(element)
        {
            viewModel.users.push(element.firstname + " " + element.lastname);
        });

        return viewModel.users().length;
    });
}
<commit_msg>nr of assessing color balls<commit_after>function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.projectId = $("#projectHeader").data('value');

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);
    gvm.projectDescription = ko.observable("Description");

    gvm.firstNameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("firstNameTableTitle");}, gvm);
    gvm.lastNameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("lastNameTableTitle");}, gvm);
    gvm.actionTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ActionTableTitle");}, gvm);

    gvm.scoreTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("scoreTableTitle")});
    gvm.filesTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("filesTableTitle")});

    gvm.searchStudent = ko.observable("");

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + gvm.projectId, function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
            gvm.projectDescription(data[0].description);
        });
    };

    gvm.getStudentListBis = function() {
        $.getJSON('/api/project/' + gvm.projectId + '/students', function(data) {
            data.forEach(function(element){
                viewModel.addTableData(element.id, element.firstname, element.lastname, element.mail, element.assessCount);
            });
        });
    };

    gvm.getStudentList = function() {
        $.getJSON('/api/project/' + gvm.projectId + '/students/', function(data) {
            $.each(data, function(i, item) {

                $.getJSON('/api/project/' + gvm.projectid  + '/student/' + item.id + '/assessed', function(data)
                {
                    viewModel.users.removeAll();
                    data.forEach(function(element)
                    {
                        viewModel.users.push(element.firstname + " " + element.lastname);
                    });

                    viewModel.addTableData(item.id, item.firstname, item.lastname, item.mail, viewModel.users().length);
                });
            });
        });
    };

    gvm.users = ko.observableArray([]);

    gvm.assassedUsers = ko.observableArray([]);

    gvm.tabledata = ko.observableArray([]);

    gvm.tempTableData = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, firstname, lastname, email, countAssessed) {
        // Push data
        var data = countAssessed;
        var completed = false;

        console.log(data);
        console.log("test: " + getNrOfAssessing());
        if (data == getNrOfAssessing()){
            console.log("true");
            completed = true;
        }

        var tblOject = {tid: id, tfirstname: firstname, tlastname: lastname, tScoreTableBtn: gvm.scoreTableTitle, tFilesTableBtn: gvm.filesTableTitle, tpid: gvm.projectId, email: email, tcountAssessed: data, tAssessedCompleted: completed};
        gvm.tabledata.push(tblOject);
    };

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    };

    /*gvm.getAllData = function() {
        $.getJSON('/api/project/getAllData/' + $("#projectHeader").data('value'), function(data) {
            console.log(data);
        });
    }*/
}

function initPage() {
    viewModel.getProjectInfo();
    viewModel.getStudentListBis();
    //viewModel.getStudentList();

    $("#searchField").bind("keypress", {}, keypressInBox);

}

function keypressInBox(e) {
    var code = (e.keyCode ? e.keyCode : e.which);
    if (code == 13) { //Enter keycode
        $("#searchField").blur();
        getStudentByName();

    }
};

function createPDF(id,name,lastname,email, projectheader, projectdescription)
{
    /*$.getJSON('/api/finalscore/' + viewModel.projectId + '/' + id, function (data) {
        makePDF(id,name,lastname,email,projectheader,projectdescription,data);
        //console.log(data);
    });*/

    $.ajax({
        url: '/api/finalscore/' + viewModel.projectId + '/' + id,
        type: "GET",
        dataType: 'json',
        success: function(data) {
            makePDF(id,name,lastname,email,projectheader,projectdescription,data);
        },
        error: function()
        {
            alert("Please make sure points are given!");
        }
    });
}

function getStudentByName(){

    if(viewModel.tempTableData().length == 0) {
        viewModel.tempTableData(viewModel.tabledata.slice(0));
    }

    viewModel.tabledata([]);

    viewModel.tempTableData().forEach(function(item, element){
        var fullname = item.tfirstname + " " + item.tlastname;
        var fullnameReverse = item.tlastname + " " + item.tfirstname;

        if (item.tfirstname.toLowerCase().contains(viewModel.searchStudent().toLowerCase()) || item.tlastname.toLowerCase().contains(viewModel.searchStudent().toLowerCase()) || fullname.toLowerCase().contains(viewModel.searchStudent().toLowerCase()) || fullnameReverse.toLowerCase().contains(viewModel.searchStudent().toLowerCase())){
            viewModel.tabledata.push(item);
        }
    });
}

function showFullList()
{
    viewModel.searchStudent = ko.observable("");
    getStudentByName();
}

function getData(id)
{
    $.getJSON('/api/project/' + viewModel.projectId  + '/student/' + id + '/assessed', function(data)
    {
        viewModel.users.removeAll();
        data.forEach(function(element)
        {
            viewModel.users.push(element.firstname + " " + element.lastname);
        });
    });

    return viewModel.users();
}

function getNrOfAssessing(){
    $.getJSON('/api/project/nrOfAssessing/' + viewModel.projectId , function(data)
    {
        data.forEach(function(item)
        {
            //console.log(item.nrOfAssessing);

            var nr = item.nrOfAssessing;
        });
    });

    return nr;
}

function getDataCount(projectid, id)
{
    $.getJSON('/api/project/' + projectid  + '/student/' + id + '/assessed', function(data)
    {
        viewModel.users.removeAll();
        data.forEach(function(element)
        {
            viewModel.users.push(element.firstname + " " + element.lastname);
        });

        return viewModel.users().length;
    });
}
<|endoftext|>