<commit_before>/**
 * MenuSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains options and
 * is used together with OO.ui.MenuOptionWidget. It is designed be used as part of another widget.
 * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget},
 * and {@link OO.ui.mixin.LookupElement LookupElement} for examples of widgets that contain menus.
 * MenuSelectWidgets themselves are not instantiated directly, rather subclassed
 * and customized to be opened, closed, and displayed as needed.
 *
 * By default, menus are clipped to the visible viewport and are not visible when a user presses the
 * mouse outside the menu.
 *
 * Menus also have support for keyboard interaction:
 *
 * - Enter/Return key: choose and select a menu option
 * - Up-arrow key: highlight the previous menu option
 * - Down-arrow key: highlight the next menu option
 * - Esc key: hide the menu
 *
 * Unlike most widgets, MenuSelectWidget is initially hidden and must be shown by calling #toggle.
 *
 * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
 * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
 *
 * @class
 * @extends OO.ui.SelectWidget
 * @mixins OO.ui.mixin.ClippableElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {OO.ui.TextInputWidget} [input] Text input used to implement option highlighting for menu items that match
 *  the text the user types. This config is used by {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget}
 *  and {@link OO.ui.mixin.LookupElement LookupElement}
 * @cfg {jQuery} [$input] Text input used to implement option highlighting for menu items that match
 *  the text the user types. This config is used by {@link OO.ui.CapsuleMultiselectWidget CapsuleMultiselectWidget}
 * @cfg {OO.ui.Widget} [widget] Widget associated with the menu's active state. If the user clicks the mouse
 *  anywhere on the page outside of this widget, the menu is hidden. For example, if there is a button
 *  that toggles the menu's visibility on click, the menu will be hidden then re-shown when the user clicks
 *  that button, unless the button (or its parent widget) is passed in here.
 * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu.
 * @cfg {boolean} [hideOnChoose=true] Hide the menu when the user chooses an option.
 * @cfg {boolean} [filterFromInput=false] Filter the displayed options from the input
 */
OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) {
	// Configuration initialization
	config = config || {};

	// Parent constructor
	OO.ui.MenuSelectWidget.parent.call( this, config );

	// Mixin constructors
	OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );

	// Properties
	this.autoHide = config.autoHide === undefined || !!config.autoHide;
	this.hideOnChoose = config.hideOnChoose === undefined || !!config.hideOnChoose;
	this.filterFromInput = !!config.filterFromInput;
	this.$input = config.$input ? config.$input : config.input ? config.input.$input : null;
	this.$widget = config.widget ? config.widget.$element : null;
	this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this );
	this.onInputEditHandler = OO.ui.debounce( this.updateItemVisibility.bind( this ), 100 );

	// Initialization
	this.$element
		.addClass( 'oo-ui-menuSelectWidget' )
		.attr( 'role', 'menu' );

	// Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
	// that reference properties not initialized at that time of parent class construction
	// TODO: Find a better way to handle post-constructor setup
	this.visible = false;
	this.$element.addClass( 'oo-ui-element-hidden' );
};

/* Setup */

OO.inheritClass( OO.ui.MenuSelectWidget, OO.ui.SelectWidget );
OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.mixin.ClippableElement );

/* Methods */

/**
 * Handles document mouse down events.
 *
 * @protected
 * @param {MouseEvent} e Mouse down event
 */
OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) {
	if (
		!OO.ui.contains( this.$element[ 0 ], e.target, true ) &&
		( !this.$widget || !OO.ui.contains( this.$widget[ 0 ], e.target, true ) )
	) {
		this.toggle( false );
	}
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
	var currentItem = this.getHighlightedItem() || this.getSelectedItem();

	if ( !this.isDisabled() && this.isVisible() ) {
		switch ( e.keyCode ) {
			case OO.ui.Keys.LEFT:
			case OO.ui.Keys.RIGHT:
				// Do nothing if a text field is associated, arrow keys will be handled natively
				if ( !this.$input ) {
					OO.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e );
				}
				break;
			case OO.ui.Keys.ESCAPE:
			case OO.ui.Keys.TAB:
				if ( currentItem ) {
					currentItem.setHighlighted( false );
				}
				this.toggle( false );
				// Don't prevent tabbing away, prevent defocusing
				if ( e.keyCode === OO.ui.Keys.ESCAPE ) {
					e.preventDefault();
					e.stopPropagation();
				}
				break;
			default:
				OO.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e );
				return;
		}
	}
};

/**
 * Update menu item visibility after input changes.
 *
 * @protected
 */
OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () {
	var i, item, visible, section, sectionEmpty,
		anyVisible = false,
		len = this.items.length,
		showAll = !this.isVisible(),
		filter = showAll ? null : this.getItemMatcher( this.$input.val() );

	// Hide non-matching options, and also hide section headers if all options
	// in their section are hidden.
	for ( i = 0; i < len; i++ ) {
		item = this.items[ i ];
		if ( item instanceof OO.ui.MenuSectionOptionWidget ) {
			if ( section ) {
				// If the previous section was empty, hide its header
				section.toggle( showAll || !sectionEmpty );
			}
			section = item;
			sectionEmpty = true;
		} else if ( item instanceof OO.ui.OptionWidget ) {
			visible = showAll || filter( item );
			anyVisible = anyVisible || visible;
			sectionEmpty = sectionEmpty && !visible;
			item.toggle( visible );
		}
	}
	// Process the final section
	if ( section ) {
		section.toggle( showAll || !sectionEmpty );
	}

	this.$element.toggleClass( 'oo-ui-menuSelectWidget-invisible', !anyVisible );

	// Reevaluate clipping
	this.clip();
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.bindKeyDownListener = function () {
	if ( this.$input ) {
		this.$input.on( 'keydown', this.onKeyDownHandler );
	} else {
		OO.ui.MenuSelectWidget.parent.prototype.bindKeyDownListener.call( this );
	}
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.unbindKeyDownListener = function () {
	if ( this.$input ) {
		this.$input.off( 'keydown', this.onKeyDownHandler );
	} else {
		OO.ui.MenuSelectWidget.parent.prototype.unbindKeyDownListener.call( this );
	}
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.bindKeyPressListener = function () {
	if ( this.$input ) {
		if ( this.filterFromInput ) {
			this.$input.on( 'keydown mouseup cut paste change input select', this.onInputEditHandler );
		}
	} else {
		OO.ui.MenuSelectWidget.parent.prototype.bindKeyPressListener.call( this );
	}
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.unbindKeyPressListener = function () {
	if ( this.$input ) {
		if ( this.filterFromInput ) {
			this.$input.off( 'keydown mouseup cut paste change input select', this.onInputEditHandler );
			this.updateItemVisibility();
		}
	} else {
		OO.ui.MenuSelectWidget.parent.prototype.unbindKeyPressListener.call( this );
	}
};

/**
 * Choose an item.
 *
 * When a user chooses an item, the menu is closed, unless the hideOnChoose config option is set to false.
 *
 * Note that ‘choose’ should never be modified programmatically. A user can choose an option with the keyboard
 * or mouse and it becomes selected. To select an item programmatically, use the #selectItem method.
 *
 * @param {OO.ui.OptionWidget} item Item to choose
 * @chainable
 */
OO.ui.MenuSelectWidget.prototype.chooseItem = function ( item ) {
	OO.ui.MenuSelectWidget.parent.prototype.chooseItem.call( this, item );
	if ( this.hideOnChoose ) {
		this.toggle( false );
	}
	return this;
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.addItems = function ( items, index ) {
	// Parent method
	OO.ui.MenuSelectWidget.parent.prototype.addItems.call( this, items, index );

	// Reevaluate clipping
	this.clip();

	return this;
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.removeItems = function ( items ) {
	// Parent method
	OO.ui.MenuSelectWidget.parent.prototype.removeItems.call( this, items );

	// Reevaluate clipping
	this.clip();

	return this;
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.clearItems = function () {
	// Parent method
	OO.ui.MenuSelectWidget.parent.prototype.clearItems.call( this );

	// Reevaluate clipping
	this.clip();

	return this;
};

/**
 * Toggle visibility of the menu. The menu is initially hidden and must be shown by calling
 * `.toggle( true )` after its #$element is attached to the DOM.
 *
 * Do not show the menu while it is not attached to the DOM. The calculations required to display
 * it in the right place and with the right dimensions only work correctly while it is attached.
 * Side-effects may include broken interface and exceptions being thrown. This wasn't always
 * strictly enforced, so currently it only generates a warning in the browser console.
 *
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
	var change;

	visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length;
	change = visible !== this.isVisible();

	if ( visible && !this.warnedUnattached && !this.isElementAttached() ) {
		OO.ui.warnDeprecation( 'MenuSelectWidget#toggle: Before calling this method, the menu must be attached to the DOM.' );
		this.warnedUnattached = true;
	}

	// Parent method
	OO.ui.MenuSelectWidget.parent.prototype.toggle.call( this, visible );

	if ( change ) {
		if ( visible ) {
			this.bindKeyDownListener();
			this.bindKeyPressListener();

			this.toggleClipping( true );

			if ( this.getSelectedItem() ) {
				this.getSelectedItem().scrollElementIntoView( { duration: 0 } );
			}

			// Auto-hide
			if ( this.autoHide ) {
				this.getElementDocument().addEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
			}
		} else {
			this.unbindKeyDownListener();
			this.unbindKeyPressListener();
			this.getElementDocument().removeEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
			this.toggleClipping( false );
		}
	}

	return this;
};
<commit_msg>MenuSelectWidget: Highlight the first result when searching<commit_after>/**
 * MenuSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains options and
 * is used together with OO.ui.MenuOptionWidget. It is designed be used as part of another widget.
 * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget},
 * and {@link OO.ui.mixin.LookupElement LookupElement} for examples of widgets that contain menus.
 * MenuSelectWidgets themselves are not instantiated directly, rather subclassed
 * and customized to be opened, closed, and displayed as needed.
 *
 * By default, menus are clipped to the visible viewport and are not visible when a user presses the
 * mouse outside the menu.
 *
 * Menus also have support for keyboard interaction:
 *
 * - Enter/Return key: choose and select a menu option
 * - Up-arrow key: highlight the previous menu option
 * - Down-arrow key: highlight the next menu option
 * - Esc key: hide the menu
 *
 * Unlike most widgets, MenuSelectWidget is initially hidden and must be shown by calling #toggle.
 *
 * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
 * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
 *
 * @class
 * @extends OO.ui.SelectWidget
 * @mixins OO.ui.mixin.ClippableElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {OO.ui.TextInputWidget} [input] Text input used to implement option highlighting for menu items that match
 *  the text the user types. This config is used by {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget}
 *  and {@link OO.ui.mixin.LookupElement LookupElement}
 * @cfg {jQuery} [$input] Text input used to implement option highlighting for menu items that match
 *  the text the user types. This config is used by {@link OO.ui.CapsuleMultiselectWidget CapsuleMultiselectWidget}
 * @cfg {OO.ui.Widget} [widget] Widget associated with the menu's active state. If the user clicks the mouse
 *  anywhere on the page outside of this widget, the menu is hidden. For example, if there is a button
 *  that toggles the menu's visibility on click, the menu will be hidden then re-shown when the user clicks
 *  that button, unless the button (or its parent widget) is passed in here.
 * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu.
 * @cfg {boolean} [hideOnChoose=true] Hide the menu when the user chooses an option.
 * @cfg {boolean} [filterFromInput=false] Filter the displayed options from the input
 * @cfg {boolean} [highlightOnFilter] Highlight the first result when filtering
 */
OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) {
	// Configuration initialization
	config = config || {};

	// Parent constructor
	OO.ui.MenuSelectWidget.parent.call( this, config );

	// Mixin constructors
	OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );

	// Properties
	this.autoHide = config.autoHide === undefined || !!config.autoHide;
	this.hideOnChoose = config.hideOnChoose === undefined || !!config.hideOnChoose;
	this.filterFromInput = !!config.filterFromInput;
	this.$input = config.$input ? config.$input : config.input ? config.input.$input : null;
	this.$widget = config.widget ? config.widget.$element : null;
	this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this );
	this.onInputEditHandler = OO.ui.debounce( this.updateItemVisibility.bind( this ), 100 );
	this.highlightOnFilter = !!config.highlightOnFilter;

	// Initialization
	this.$element
		.addClass( 'oo-ui-menuSelectWidget' )
		.attr( 'role', 'menu' );

	// Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
	// that reference properties not initialized at that time of parent class construction
	// TODO: Find a better way to handle post-constructor setup
	this.visible = false;
	this.$element.addClass( 'oo-ui-element-hidden' );
};

/* Setup */

OO.inheritClass( OO.ui.MenuSelectWidget, OO.ui.SelectWidget );
OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.mixin.ClippableElement );

/* Methods */

/**
 * Handles document mouse down events.
 *
 * @protected
 * @param {MouseEvent} e Mouse down event
 */
OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) {
	if (
		!OO.ui.contains( this.$element[ 0 ], e.target, true ) &&
		( !this.$widget || !OO.ui.contains( this.$widget[ 0 ], e.target, true ) )
	) {
		this.toggle( false );
	}
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
	var currentItem = this.getHighlightedItem() || this.getSelectedItem();

	if ( !this.isDisabled() && this.isVisible() ) {
		switch ( e.keyCode ) {
			case OO.ui.Keys.LEFT:
			case OO.ui.Keys.RIGHT:
				// Do nothing if a text field is associated, arrow keys will be handled natively
				if ( !this.$input ) {
					OO.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e );
				}
				break;
			case OO.ui.Keys.ESCAPE:
			case OO.ui.Keys.TAB:
				if ( currentItem ) {
					currentItem.setHighlighted( false );
				}
				this.toggle( false );
				// Don't prevent tabbing away, prevent defocusing
				if ( e.keyCode === OO.ui.Keys.ESCAPE ) {
					e.preventDefault();
					e.stopPropagation();
				}
				break;
			default:
				OO.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e );
				return;
		}
	}
};

/**
 * Update menu item visibility after input changes.
 *
 * @protected
 */
OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () {
	var i, item, visible, section, sectionEmpty,
		firstItemFound = false,
		anyVisible = false,
		len = this.items.length,
		showAll = !this.isVisible(),
		filter = showAll ? null : this.getItemMatcher( this.$input.val() );

	// Hide non-matching options, and also hide section headers if all options
	// in their section are hidden.
	for ( i = 0; i < len; i++ ) {
		item = this.items[ i ];
		if ( item instanceof OO.ui.MenuSectionOptionWidget ) {
			if ( section ) {
				// If the previous section was empty, hide its header
				section.toggle( showAll || !sectionEmpty );
			}
			section = item;
			sectionEmpty = true;
		} else if ( item instanceof OO.ui.OptionWidget ) {
			visible = showAll || filter( item );
			anyVisible = anyVisible || visible;
			sectionEmpty = sectionEmpty && !visible;
			item.toggle( visible );
			if ( this.highlightOnFilter && visible && !firstItemFound ) {
				// Highlight the first item in the list
				this.highlightItem( item );
				firstItemFound = true;
			}
		}
	}
	// Process the final section
	if ( section ) {
		section.toggle( showAll || !sectionEmpty );
	}

	this.$element.toggleClass( 'oo-ui-menuSelectWidget-invisible', !anyVisible );

	// Reevaluate clipping
	this.clip();
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.bindKeyDownListener = function () {
	if ( this.$input ) {
		this.$input.on( 'keydown', this.onKeyDownHandler );
	} else {
		OO.ui.MenuSelectWidget.parent.prototype.bindKeyDownListener.call( this );
	}
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.unbindKeyDownListener = function () {
	if ( this.$input ) {
		this.$input.off( 'keydown', this.onKeyDownHandler );
	} else {
		OO.ui.MenuSelectWidget.parent.prototype.unbindKeyDownListener.call( this );
	}
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.bindKeyPressListener = function () {
	if ( this.$input ) {
		if ( this.filterFromInput ) {
			this.$input.on( 'keydown mouseup cut paste change input select', this.onInputEditHandler );
		}
	} else {
		OO.ui.MenuSelectWidget.parent.prototype.bindKeyPressListener.call( this );
	}
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.unbindKeyPressListener = function () {
	if ( this.$input ) {
		if ( this.filterFromInput ) {
			this.$input.off( 'keydown mouseup cut paste change input select', this.onInputEditHandler );
			this.updateItemVisibility();
		}
	} else {
		OO.ui.MenuSelectWidget.parent.prototype.unbindKeyPressListener.call( this );
	}
};

/**
 * Choose an item.
 *
 * When a user chooses an item, the menu is closed, unless the hideOnChoose config option is set to false.
 *
 * Note that ‘choose’ should never be modified programmatically. A user can choose an option with the keyboard
 * or mouse and it becomes selected. To select an item programmatically, use the #selectItem method.
 *
 * @param {OO.ui.OptionWidget} item Item to choose
 * @chainable
 */
OO.ui.MenuSelectWidget.prototype.chooseItem = function ( item ) {
	OO.ui.MenuSelectWidget.parent.prototype.chooseItem.call( this, item );
	if ( this.hideOnChoose ) {
		this.toggle( false );
	}
	return this;
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.addItems = function ( items, index ) {
	// Parent method
	OO.ui.MenuSelectWidget.parent.prototype.addItems.call( this, items, index );

	// Reevaluate clipping
	this.clip();

	return this;
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.removeItems = function ( items ) {
	// Parent method
	OO.ui.MenuSelectWidget.parent.prototype.removeItems.call( this, items );

	// Reevaluate clipping
	this.clip();

	return this;
};

/**
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.clearItems = function () {
	// Parent method
	OO.ui.MenuSelectWidget.parent.prototype.clearItems.call( this );

	// Reevaluate clipping
	this.clip();

	return this;
};

/**
 * Toggle visibility of the menu. The menu is initially hidden and must be shown by calling
 * `.toggle( true )` after its #$element is attached to the DOM.
 *
 * Do not show the menu while it is not attached to the DOM. The calculations required to display
 * it in the right place and with the right dimensions only work correctly while it is attached.
 * Side-effects may include broken interface and exceptions being thrown. This wasn't always
 * strictly enforced, so currently it only generates a warning in the browser console.
 *
 * @inheritdoc
 */
OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
	var change;

	visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length;
	change = visible !== this.isVisible();

	if ( visible && !this.warnedUnattached && !this.isElementAttached() ) {
		OO.ui.warnDeprecation( 'MenuSelectWidget#toggle: Before calling this method, the menu must be attached to the DOM.' );
		this.warnedUnattached = true;
	}

	// Parent method
	OO.ui.MenuSelectWidget.parent.prototype.toggle.call( this, visible );

	if ( change ) {
		if ( visible ) {
			this.bindKeyDownListener();
			this.bindKeyPressListener();

			this.toggleClipping( true );

			if ( this.getSelectedItem() ) {
				this.getSelectedItem().scrollElementIntoView( { duration: 0 } );
			}

			// Auto-hide
			if ( this.autoHide ) {
				this.getElementDocument().addEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
			}
		} else {
			this.unbindKeyDownListener();
			this.unbindKeyPressListener();
			this.getElementDocument().removeEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
			this.toggleClipping( false );
		}
	}

	return this;
};
<|endoftext|>


<commit_before>import { BaseComponent } from '../base/Base';
import Flatpickr from 'flatpickr';
import _get from 'lodash/get';
import _each from 'lodash/each';
const momentModule = require('moment');
export class DateTimeComponent extends BaseComponent {
  constructor(component, options, data) {
    super(component, options, data);
    this.validators.push('date');
  }

  elementInfo() {
    let info = super.elementInfo();
    info.type = 'input';
    info.attr.type = 'text';
    info.changeEvent = 'input';
    this.component.suffix = true;
    return info;
  }

  build() {
    super.build();

    // See if a default date is set.
    if (this.component.defaultDate) {
      var defaultDate = new Date(this.component.defaultDate);
      if (!defaultDate || isNaN(defaultDate.getDate())) {
        try {
          let moment = momentModule;
          defaultDate = new Date(eval(this.component.defaultDate));
        }
        catch (e) {
          defaultDate = '';
        }
      }

      if (defaultDate && !isNaN(defaultDate.getDate())) {
        this.setValue(defaultDate);
      }
    }
  }

  // This select component can handle multiple items on its own.
  createWrapper() {
    return false;
  }

  convertFormat(format) {
    // Year conversion.
    format = format.replace(/y/g, 'Y');
    format = format.replace('YYYY', 'Y');
    format = format.replace('YY', 'y');

    // Month conversion.
    format = format.replace('MMMM', 'F');
    format = format.replace(/M/g, 'n');
    format = format.replace('nnn', 'M');
    format = format.replace('nn', 'm');

    // Day in month.
    format = format.replace(/d/g, 'j');
    format = format.replace('jj', 'd');

    // Day in week.
    format = format.replace('EEEE', 'l');
    format = format.replace('EEE', 'D');

    // Hours, minutes, seconds
    format = format.replace('HH', 'H');
    format = format.replace('hh', 'h');
    format = format.replace('mm', 'i');
    format = format.replace('ss', 'S');
    format = format.replace(/a/g, 'K');
    return format;
  }

  get config() {
    return {
      altInput: true,
      clickOpens: true,
      enableDate: true,
      mode: this.component.multiple ? 'multiple' : 'single',
      enableTime: _get(this.component, 'enableTime', true),
      noCalendar: !_get(this.component, 'enableDate', true),
      altFormat: this.convertFormat(_get(this.component, 'format', '')),
      dateFormat: 'U',
      defaultDate: _get(this.component, 'defaultDate', ''),
      hourIncrement: _get(this.component, 'timePicker.hourStep', 1),
      minuteIncrement: _get(this.component, 'timePicker.minuteStep', 5),
      onChange: () => this.onChange()
    };
  }

  set disabled(disabled) {
    super.disabled = disabled;
    _each(this.inputs, (input) => {
      if (input.calendar) {
        input.calendar.redraw();
      }
    });
  }

  addSuffix(input, inputGroup) {
    let suffix = this.ce('span', {
      class: 'input-group-addon'
    });
    suffix.appendChild(this.getIcon(this.component.enableDate ? 'calendar' : 'time'));
    inputGroup.appendChild(suffix);
    return suffix;
  }

  addInput(input, container, name) {
    super.addInput(input, container, name);
    input.calendar = new Flatpickr(input, this.config);
  }

  getDate(value) {
    let timestamp = parseInt(value, 10);
    if (!timestamp) {
      // Just default to today.
      return (new Date());
    }
    return (new Date(timestamp * 1000));
  }

  getRawValue() {
    let values = [];
    for (let i in this.inputs) {
      if (!this.component.multiple) {
        return this.getDate(this.inputs[i].value);
      }
      values.push(this.getDate(this.inputs[i].value));
    }
    return values;
  }

  getValueAt(index) {
    return this.getDate(this.inputs[index].value).toISOString();
  }

  setValueAt(index, value) {
    if (this.inputs[index].calendar) {
      let date = value ? new Date(value) : new Date();
      this.inputs[index].calendar.setDate(date);
    }
  }
}
<commit_msg>Adding mindate and max date to DateTime component.<commit_after>import { BaseComponent } from '../base/Base';
import Flatpickr from 'flatpickr';
import _get from 'lodash/get';
import _each from 'lodash/each';
const momentModule = require('moment');
export class DateTimeComponent extends BaseComponent {
  constructor(component, options, data) {
    super(component, options, data);
    this.validators.push('date');
  }

  elementInfo() {
    let info = super.elementInfo();
    info.type = 'input';
    info.attr.type = 'text';
    info.changeEvent = 'input';
    this.component.suffix = true;
    return info;
  }

  build() {
    super.build();

    // See if a default date is set.
    if (this.component.defaultDate) {
      var defaultDate = new Date(this.component.defaultDate);
      if (!defaultDate || isNaN(defaultDate.getDate())) {
        try {
          let moment = momentModule;
          defaultDate = new Date(eval(this.component.defaultDate));
        }
        catch (e) {
          defaultDate = '';
        }
      }

      if (defaultDate && !isNaN(defaultDate.getDate())) {
        this.setValue(defaultDate);
      }
    }
  }

  // This select component can handle multiple items on its own.
  createWrapper() {
    return false;
  }

  convertFormat(format) {
    // Year conversion.
    format = format.replace(/y/g, 'Y');
    format = format.replace('YYYY', 'Y');
    format = format.replace('YY', 'y');

    // Month conversion.
    format = format.replace('MMMM', 'F');
    format = format.replace(/M/g, 'n');
    format = format.replace('nnn', 'M');
    format = format.replace('nn', 'm');

    // Day in month.
    format = format.replace(/d/g, 'j');
    format = format.replace('jj', 'd');

    // Day in week.
    format = format.replace('EEEE', 'l');
    format = format.replace('EEE', 'D');

    // Hours, minutes, seconds
    format = format.replace('HH', 'H');
    format = format.replace('hh', 'h');
    format = format.replace('mm', 'i');
    format = format.replace('ss', 'S');
    format = format.replace(/a/g, 'K');
    return format;
  }

  get config() {
    return {
      altInput: true,
      clickOpens: true,
      enableDate: true,
      mode: this.component.multiple ? 'multiple' : 'single',
      enableTime: _get(this.component, 'enableTime', true),
      noCalendar: !_get(this.component, 'enableDate', true),
      altFormat: this.convertFormat(_get(this.component, 'format', '')),
      dateFormat: 'U',
      defaultDate: _get(this.component, 'defaultDate', ''),
      hourIncrement: _get(this.component, 'timePicker.hourStep', 1),
      minuteIncrement: _get(this.component, 'timePicker.minuteStep', 5),
      minDate: _get(this.component, 'datePicker.minDate'),
      maxDate: _get(this.component, 'datePicker.maxDate'),
      onChange: () => this.onChange()
    };
  }

  set disabled(disabled) {
    super.disabled = disabled;
    _each(this.inputs, (input) => {
      if (input.calendar) {
        input.calendar.redraw();
      }
    });
  }

  addSuffix(input, inputGroup) {
    let suffix = this.ce('span', {
      class: 'input-group-addon'
    });
    suffix.appendChild(this.getIcon(this.component.enableDate ? 'calendar' : 'time'));
    inputGroup.appendChild(suffix);
    return suffix;
  }

  addInput(input, container, name) {
    super.addInput(input, container, name);
    input.calendar = new Flatpickr(input, this.config);
  }

  getDate(value) {
    let timestamp = parseInt(value, 10);
    if (!timestamp) {
      // Just default to today.
      return (new Date());
    }
    return (new Date(timestamp * 1000));
  }

  getRawValue() {
    let values = [];
    for (let i in this.inputs) {
      if (!this.component.multiple) {
        return this.getDate(this.inputs[i].value);
      }
      values.push(this.getDate(this.inputs[i].value));
    }
    return values;
  }

  getValueAt(index) {
    return this.getDate(this.inputs[index].value).toISOString();
  }

  setValueAt(index, value) {
    if (this.inputs[index].calendar) {
      let date = value ? new Date(value) : new Date();
      this.inputs[index].calendar.setDate(date);
    }
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import styles from './Toast.css';
import Loading from '../Loading/Loading';

import dhIcon from '../../../assets/icons/hahoo-ui/dui.svg';
import warnIcon from '../../../assets/icons/hahoo-ui/warn.svg';
import wrongIcon from '../../../assets/icons/hahoo-ui/wrong.svg';

class Toast extends React.Component {

  static propTypes = {
    title: React.PropTypes.string,
    type: React.PropTypes.string,
    isAutoClose: React.PropTypes.bool,
    isBlock: React.PropTypes.bool,
    onClose: React.PropTypes.func,
    speed: React.PropTypes.number
  };

  static defaultProps = {
    type: 'success',
    isBlock: false,
    isAutoClose: true,
    speed: 1000
  };

  state = {
    isClose: false,
  };

  componentDidMount() {
    if (this.props.type !== 'loading' && this.props.isAutoClose) {
      setTimeout(this.handleClose.bind(this), this.props.speed);
    }
  }

  handleClose() {
    this.setState({ isClose: true });
    if (this.props.onClose) {
      this.props.onClose();
    }
  }

  render() {
    let icon = undefined;
    switch (this.props.type) {
      case 'success':
        icon = dhIcon;
        break;
      case 'warn':
        icon = warnIcon;
        break;
      case 'wrong':
        icon = wrongIcon;
        break;
      case 'loading':
        icon = <Loading className={styles.toastLoading} />;
        break;
      default:
        icon = dhIcon;
        break;
    }

    let iconbox = undefined;
    if (this.props.type === 'loading') {
      iconbox = icon;
    } else {
      iconbox =

;
    }

    let toast = undefined;
    if (!this.state.isClose) {
      toast =

        {this.props.isBlock ?

 : null}

					{iconbox}

{this.props.title}

;
    }

    return (

				{toast && toast}

    );
  }
}

export default Toast;
<commit_msg>更新Toast页面卸载时去掉setTimeout<commit_after>import React from 'react';
import styles from './Toast.css';
import Loading from '../Loading/Loading';

import dhIcon from '../../../assets/icons/hahoo-ui/dui.svg';
import warnIcon from '../../../assets/icons/hahoo-ui/warn.svg';
import wrongIcon from '../../../assets/icons/hahoo-ui/wrong.svg';

class Toast extends React.Component {

  static propTypes = {
    title: React.PropTypes.string,
    type: React.PropTypes.string,
    isAutoClose: React.PropTypes.bool,
    isBlock: React.PropTypes.bool,
    onClose: React.PropTypes.func,
    closeSpeed: React.PropTypes.number
  };

  static defaultProps = {
    type: 'success',
    isBlock: false,
    isAutoClose: true,
    closeSpeed: 1000
  };

  state = {
    isClose: false,
  };

  componentDidMount() {
    if (this.props.type !== 'loading' && this.props.isAutoClose) {
      this.closeTimeout = setTimeout(this.handleClose.bind(this), this.props.closeSpeed);
    }
  }

  componentWillUnmount() {
    if (this.props.isAutoClose && !this.state.isClose && this.closeTimeout) {
      clearTimeout(this.closeTimeout);
      this.closeTimeout = false;
    }
  }

  handleClose() {
    this.setState({ isClose: true });
    if (this.props.onClose) {
      this.props.onClose();
    }
  }

  render() {
    let icon = undefined;
    switch (this.props.type) {
      case 'success':
        icon = dhIcon;
        break;
      case 'warn':
        icon = warnIcon;
        break;
      case 'wrong':
        icon = wrongIcon;
        break;
      case 'loading':
        icon = <Loading className={styles.toastLoading} />;
        break;
      default:
        icon = dhIcon;
        break;
    }

    let iconbox = undefined;
    if (this.props.type === 'loading') {
      iconbox = icon;
    } else {
      iconbox =

;
    }

    let toast = undefined;
    if (!this.state.isClose) {
      toast =

        {this.props.isBlock ?

 : null}

					{iconbox}

{this.props.title}

;
    }

    return (

				{toast && toast}

    );
  }
}

export default Toast;
<|endoftext|>


<commit_before>import React from 'react'
import classNames from 'classnames'
// import Mousetrap from 'mousetrap'
import { connect } from 'react-redux'
import { SHORTCUT_KEYS } from '../../constants/action_types'
import NavbarLabel from './NavbarLabel'
import NavbarOmniButton from './NavbarOmniButton'
import NavbarLink from './NavbarLink'
import NavbarMark from './NavbarMark'
import NavbarProfile from './NavbarProfile'
import { BoltIcon, CircleIcon, SearchIcon, SparklesIcon, StarIcon } from '../iconography/Icons'
// import HelpDialog from '../dialogs/HelpDialog'
// import { openModal, closeModal } from '../../actions/modals'
import { addScrollObject, removeScrollObject } from '../scroll/ScrollComponent'

class Navbar extends React.Component {
  constructor(props, context) {
    super(props, context)
    this.scrollYAtDirectionChange = null
    this.state = {
      asFixed: false,
      asHidden: false,
      skipTransition: false,
    }
  }

  componentDidMount() {
    // Mousetrap.bind(Object.keys(this.props.shortcuts), (event, shortcut) => {
    //   const { router } = this.context
    //   router.transitionTo(this.props.shortcuts[shortcut])
    // })

    // Mousetrap.bind(SHORTCUT_KEYS.HELP, () => {
    //   const { dispatch, modals } = this.props
    //   if (modals.payload) {
    //     return dispatch(closeModal())
    //   }
    //   return dispatch(openModal(<HelpDialog/>))
    // })
    addScrollObject(this)
  }

  componentWillUnmount() {
    // Mousetrap.unbind(Object.keys(this.props.shortcuts))
    // Mousetrap.unbind(SHORTCUT_KEYS.HELP)
    removeScrollObject(this)
  }

  onScrollTop() {
    if (this.state.asFixed) {
      this.setState({ asFixed: false, asHidden: false, skipTransition: false })
    }
  }

  onScrollBottom() {
    if (this.state.asFixed) {
      this.setState({ asFixed: true, asHidden: false, skipTransition: false })
    }
  }

  onScrollDirectionChange(scrollProperties) {
    const { scrollY } = scrollProperties

    if (scrollY >= 600) {
      this.scrollYAtDirectionChange = scrollY
    }
  }

  onScroll(scrollProperties) {
    const { scrollY, scrollDirection } = scrollProperties

    // Going from absolute to fixed positioning
    if (scrollY >= 600 && !this.state.asFixed) {
      this.setState({ asFixed: true, asHidden: true, skipTransition: true })
    }

    // Scroll just changed directions so it's about to either be shown or hidden
    if (scrollY >= 600 && this.scrollYAtDirectionChange) {
      const distance = Math.abs(scrollY - this.scrollYAtDirectionChange)
      const delay = scrollDirection === 'down' ? 20 : 80

      if (distance >= delay ) {
        this.setState({ asHidden: scrollDirection === 'down', skipTransition: false })
        this.scrollYAtDirectionChange = null
      }
    }
  }

  omniButtonWasClicked() {
  }

  render() {
    const { profile } = this.props
    const showLabel = false
    const klassNames = classNames(
      'Navbar',
      { asFixed: this.state.asFixed },
      { asHidden: this.state.asHidden },
      { skipTransition: this.state.skipTransition },
    )

    return (

        <NavbarMark />
        { showLabel ? <NavbarLabel /> : <NavbarOmniButton callback={this.omniButtonWasClicked.bind(this)} />}

          <NavbarLink to="/following" label="Following" icon={ <CircleIcon/> } />
          <NavbarLink to="/starred" label="Starred" icon={ <StarIcon/> } />
          <NavbarLink to="/discover" label="Discover" icon={ <SparklesIcon/> } />
          <NavbarLink to="/notifications" label="Notifications" icon={ <BoltIcon/> } />
          <NavbarLink to="/search" label="Search" icon={ <SearchIcon/> } />

          <NavbarProfile { ...profile.payload } />

    )
  }
}

// This should be a selector: @see: https://github.com/faassen/reselect
function mapStateToProps(state) {
  return {
    modals: state.modals,
    profile: state.profile,
    router: state.router,
  }
}

Navbar.defaultProps = {
  shortcuts: {
    [SHORTCUT_KEYS.SEARCH]: '/search',
    [SHORTCUT_KEYS.DISCOVER]: '/discover',
    [SHORTCUT_KEYS.ONBOARDING]: '/onboarding/communities',
  },
}

Navbar.propTypes = {
  shortcuts: React.PropTypes.object.isRequired,
  dispatch: React.PropTypes.func.isRequired,
  modals: React.PropTypes.object,
  profile: React.PropTypes.object,
  router: React.PropTypes.object.isRequired,
}

export default connect(mapStateToProps)(Navbar)

<commit_msg>Don’t auto show the navbar on scroll bottom<commit_after>import React from 'react'
import classNames from 'classnames'
// import Mousetrap from 'mousetrap'
import { connect } from 'react-redux'
import { SHORTCUT_KEYS } from '../../constants/action_types'
import NavbarLabel from './NavbarLabel'
import NavbarOmniButton from './NavbarOmniButton'
import NavbarLink from './NavbarLink'
import NavbarMark from './NavbarMark'
import NavbarProfile from './NavbarProfile'
import { BoltIcon, CircleIcon, SearchIcon, SparklesIcon, StarIcon } from '../iconography/Icons'
// import HelpDialog from '../dialogs/HelpDialog'
// import { openModal, closeModal } from '../../actions/modals'
import { addScrollObject, removeScrollObject } from '../scroll/ScrollComponent'

class Navbar extends React.Component {
  constructor(props, context) {
    super(props, context)
    this.scrollYAtDirectionChange = null
    this.state = {
      asFixed: false,
      asHidden: false,
      skipTransition: false,
    }
  }

  componentDidMount() {
    // Mousetrap.bind(Object.keys(this.props.shortcuts), (event, shortcut) => {
    //   const { router } = this.context
    //   router.transitionTo(this.props.shortcuts[shortcut])
    // })

    // Mousetrap.bind(SHORTCUT_KEYS.HELP, () => {
    //   const { dispatch, modals } = this.props
    //   if (modals.payload) {
    //     return dispatch(closeModal())
    //   }
    //   return dispatch(openModal(<HelpDialog/>))
    // })
    addScrollObject(this)
  }

  componentWillUnmount() {
    // Mousetrap.unbind(Object.keys(this.props.shortcuts))
    // Mousetrap.unbind(SHORTCUT_KEYS.HELP)
    removeScrollObject(this)
  }

  onScrollTop() {
    if (this.state.asFixed) {
      this.setState({ asFixed: false, asHidden: false, skipTransition: false })
    }
  }

  onScrollDirectionChange(scrollProperties) {
    const { scrollY } = scrollProperties

    if (scrollY >= 600) {
      this.scrollYAtDirectionChange = scrollY
    }
  }

  onScroll(scrollProperties) {
    const { scrollY, scrollDirection } = scrollProperties

    // Going from absolute to fixed positioning
    if (scrollY >= 600 && !this.state.asFixed) {
      this.setState({ asFixed: true, asHidden: true, skipTransition: true })
    }

    // Scroll just changed directions so it's about to either be shown or hidden
    if (scrollY >= 600 && this.scrollYAtDirectionChange) {
      const distance = Math.abs(scrollY - this.scrollYAtDirectionChange)
      const delay = scrollDirection === 'down' ? 20 : 80

      if (distance >= delay ) {
        this.setState({ asHidden: scrollDirection === 'down', skipTransition: false })
        this.scrollYAtDirectionChange = null
      }
    }
  }

  omniButtonWasClicked() {
  }

  render() {
    const { profile } = this.props
    const showLabel = false
    const klassNames = classNames(
      'Navbar',
      { asFixed: this.state.asFixed },
      { asHidden: this.state.asHidden },
      { skipTransition: this.state.skipTransition },
    )

    return (

        <NavbarMark />
        { showLabel ? <NavbarLabel /> : <NavbarOmniButton callback={this.omniButtonWasClicked.bind(this)} />}

          <NavbarLink to="/following" label="Following" icon={ <CircleIcon/> } />
          <NavbarLink to="/starred" label="Starred" icon={ <StarIcon/> } />
          <NavbarLink to="/discover" label="Discover" icon={ <SparklesIcon/> } />
          <NavbarLink to="/notifications" label="Notifications" icon={ <BoltIcon/> } />
          <NavbarLink to="/search" label="Search" icon={ <SearchIcon/> } />

          <NavbarProfile { ...profile.payload } />

    )
  }
}

// This should be a selector: @see: https://github.com/faassen/reselect
function mapStateToProps(state) {
  return {
    modals: state.modals,
    profile: state.profile,
    router: state.router,
  }
}

Navbar.defaultProps = {
  shortcuts: {
    [SHORTCUT_KEYS.SEARCH]: '/search',
    [SHORTCUT_KEYS.DISCOVER]: '/discover',
    [SHORTCUT_KEYS.ONBOARDING]: '/onboarding/communities',
  },
}

Navbar.propTypes = {
  shortcuts: React.PropTypes.object.isRequired,
  dispatch: React.PropTypes.func.isRequired,
  modals: React.PropTypes.object,
  profile: React.PropTypes.object,
  router: React.PropTypes.object.isRequired,
}

export default connect(mapStateToProps)(Navbar)

<|endoftext|>


<commit_before>(function () {
  'use strict';

  angular.module('exceptionless.user', ['restangular'])
    .factory('userService', ['Restangular', function (Restangular) {
      function addAdminRole(id) {
        return Restangular.one('users', id).one('admin-role').post();
      }

      function getCurrentUser() {
        return Restangular.one('users', 'me').get();
      }

      function getById(id) {
        return Restangular.one('users', id).get();
      }

      function getByOrganizationId(id, options) {
        return Restangular.one('organizations', id).all('users').getList(options || {});
      }

      function hasAdminRole(user) {
        return hasRole(user, 'global');
      }

      function hasRole(user, role) {
        return !!user && !!user.roles && user.roles.indexOf(role) !== -1;
      }

      function removeAdminRole(id) {
        return Restangular.one('users', id).one('admin-role').remove();
      }

      function resendVerificationEmail(id) {
        return Restangular.one('users', id).one('resend-verification-email').get();
      }

      function update(id, project) {
        return Restangular.one('users', id).patch(project);
      }

      function updateEmailAddress(id, email) {
        return Restangular.one('users', id).one('email-address', email).post();
      }

      function verifyEmailAddress(token) {
        return Restangular.one('users', 'verify-email-address').one(token).get();
      }

      var service = {
        addAdminRole: addAdminRole,
        getCurrentUser: getCurrentUser,
        getById: getById,
        getByOrganizationId: getByOrganizationId,
        hasAdminRole: hasAdminRole,
        hasRole: hasRole,
        removeAdminRole: removeAdminRole,
        resendVerificationEmail: resendVerificationEmail,
        update: update,
        updateEmailAddress: updateEmailAddress,
        verifyEmailAddress: verifyEmailAddress
      };

      return service;
    }]);
}());
<commit_msg>Cache the user profile<commit_after>(function () {
  'use strict';

  angular.module('exceptionless.user', ['restangular'])
    .factory('userService', ['$cacheFactory', '$rootScope', 'Restangular', function ($cacheFactory, $rootScope, Restangular) {
      var _cache = $cacheFactory('http:user');
      $rootScope.$on('auth:logout', _cache.removeAll);
      $rootScope.$on('UserChanged', _cache.removeAll);

      var _cachedRestangular = Restangular.withConfig(function(RestangularConfigurer) {
        RestangularConfigurer.setDefaultHttpFields({ cache: _cache });
      });

      function addAdminRole(id) {
        return Restangular.one('users', id).one('admin-role').post();
      }

      function getCurrentUser(useCache) {
          if (useCache === undefined || useCache) {
            return _cachedRestangular.one('users', 'me').get();
          }

          return Restangular.one('users', 'me').get();
        }

      function getById(id) {
        return Restangular.one('users', id).get();
      }

      function getByOrganizationId(id, options) {
        return Restangular.one('organizations', id).all('users').getList(options || {});
      }

      function hasAdminRole(user) {
        return hasRole(user, 'global');
      }

      function hasRole(user, role) {
        return !!user && !!user.roles && user.roles.indexOf(role) !== -1;
      }

      function removeAdminRole(id) {
        return Restangular.one('users', id).one('admin-role').remove();
      }

      function resendVerificationEmail(id) {
        return Restangular.one('users', id).one('resend-verification-email').get();
      }

      function update(id, project) {
        return Restangular.one('users', id).patch(project);
      }

      function updateEmailAddress(id, email) {
        return Restangular.one('users', id).one('email-address', email).post();
      }

      function verifyEmailAddress(token) {
        return Restangular.one('users', 'verify-email-address').one(token).get();
      }

      var service = {
        addAdminRole: addAdminRole,
        getCurrentUser: getCurrentUser,
        getById: getById,
        getByOrganizationId: getByOrganizationId,
        hasAdminRole: hasAdminRole,
        hasRole: hasRole,
        removeAdminRole: removeAdminRole,
        resendVerificationEmail: resendVerificationEmail,
        update: update,
        updateEmailAddress: updateEmailAddress,
        verifyEmailAddress: verifyEmailAddress
      };

      return service;
    }]);
}());
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import classNames from 'classnames'
import { inviteUsers } from '../../actions/invitations'
import Avatar from '../assets/Avatar'
import RelationsGroup from '../relationships/RelationsGroup'
import { getLinkObject } from '../base/json_helper'

class UserInvitee extends Component {

  reInviteUser() {
    const { dispatch, invitation } = this.props
    const emails = [invitation.email]
    dispatch(inviteUsers(emails))
  }

  renderMailtoUserHeader(invitation) {
    const { email } = invitation
    return (

        <a className="UserInviteeUserLink" href={ `mailto: ${email}` }>
          <Avatar/>
          { email }

    )
  }

  renderSending(invitation) {
    return (

        { this.renderMailtoUserHeader(invitation) }
        Sending

    )
  }

  renderPending(invitation) {
    return (

        { this.renderMailtoUserHeader(invitation) }
        Pending

    )
  }

  renderReInvite(invitation) {
    return (

        { this.renderMailtoUserHeader(invitation) }
        <button className="UserInviteeAction" onClick={ ::this.reInviteUser }>Re-Invite</button>

    )
  }

  renderAccepted(invitation) {
    const { json } = this.props
    const user = getLinkObject(invitation, 'acceptedBy', json)
    return (

          <Link className="UserInviteeUserLink" to={`/${user.username}`}>
            <Avatar sources={user.avatar} />
            {`@${user.username}`}
          </Link>

        <RelationsGroup user={user} />

    )
  }

  // Need to actually define when these states are setup
  render() {
    const { invitation } = this.props
    if (invitation.acceptedAt) {
      return this.renderAccepted(invitation)
    } else if (invitation.email) {
      return this.renderReInvite(invitation)
    }
    return null
  }
}

UserInvitee.propTypes = {
  className: PropTypes.string,
  dispatch: PropTypes.func.isRequired,
  invitation: PropTypes.shape({}).isRequired,
  json: PropTypes.object.isRequired,
}

export default connect()(UserInvitee)

<commit_msg>Remove the pending state since we don’t care<commit_after>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import classNames from 'classnames'
import { inviteUsers } from '../../actions/invitations'
import Avatar from '../assets/Avatar'
import RelationsGroup from '../relationships/RelationsGroup'
import { getLinkObject } from '../base/json_helper'

class UserInvitee extends Component {

  reInviteUser() {
    const { dispatch, invitation } = this.props
    const emails = [invitation.email]
    dispatch(inviteUsers(emails))
  }

  renderMailtoUserHeader(invitation) {
    const { email } = invitation
    return (

        <a className="UserInviteeUserLink" href={ `mailto: ${email}` }>
          <Avatar/>
          { email }

    )
  }

  renderSending(invitation) {
    return (

        { this.renderMailtoUserHeader(invitation) }
        Sending

    )
  }

  renderReInvite(invitation) {
    return (

        { this.renderMailtoUserHeader(invitation) }
        <button className="UserInviteeAction" onClick={ ::this.reInviteUser }>Re-Invite</button>

    )
  }

  renderAccepted(invitation) {
    const { json } = this.props
    const user = getLinkObject(invitation, 'acceptedBy', json)
    return (

          <Link className="UserInviteeUserLink" to={`/${user.username}`}>
            <Avatar sources={user.avatar} />
            {`@${user.username}`}
          </Link>

        <RelationsGroup user={user} />

    )
  }

  // Need to actually define when these states are setup
  render() {
    const { invitation } = this.props
    if (invitation.acceptedAt) {
      return this.renderAccepted(invitation)
    } else if (invitation.email) {
      return this.renderReInvite(invitation)
    }
    return null
  }
}

UserInvitee.propTypes = {
  className: PropTypes.string,
  dispatch: PropTypes.func.isRequired,
  invitation: PropTypes.shape({}).isRequired,
  json: PropTypes.object.isRequired,
}

export default connect()(UserInvitee)

<|endoftext|>


<commit_before>function Game() {
  this.frameIndex = -1; // -1 as 0 needs to be first index
  this.currentFrameObject = null; // this will be refreshed every frame to the newly created Frame
  this.scoreSheet = []; //[Frame, Frame] stores individual frames
  this.totalScore = 0; // is this needed? if so -- does it belong in Frame?
  this.isGameOver = false;
  this.strikeCount = 0;
}

Game.prototype.rollBall = function(pinsKnocked) {
  this.logRoll(pinsKnocked);
};

Game.prototype.logRoll = function(pinsKnocked) {

  if (this.isGameOver == true) {
    fail('Game Over, please reset the game');
  }

  if (this.currentFrameObject == null) {
    this.currentFrameObject = new Frame(); //create the frame object for every new frame(round)
    this.frameIndex += 1;//
  }

  if (this.currentFrameObject.rollIndex == 0) { //if its on its first roll:
    this.currentFrameObject.firstRoll(pinsKnocked); //update the frame with firstRoll score
    this.checkStrike(); // checks if there was a strike
  } else { // if its on its second roll and there was no strike
    this.strikeCount = 0; // resets strike count to 0
    this.currentFrameObject.secondRoll(pinsKnocked); // updates frame with the results of the second roll
    this.scoreSheet.push(this.currentFrameObject); // frame finished add the frame to scoreSheet:
    this.calculateScore(); // calculates score at end of the frame
    this.gameOver(); // checks if the game is over
    this.currentFrameObject = null; //resets current frame
  }
};

Game.prototype.checkStrike = function() {
  if (this.currentFrameObject.strike == true) {
    this.scoreSheet.push(this.currentFrameObject);
    this.calculateScore();
    this.gameOver();
    this.currentFrameObject = null;
    this.strikeCount += 1;
    return;
  }
};

Game.prototype.calculateScore = function() {
  // if on first frame or if there were no strikes/spares
  // if ((this.frameIndex != 0 || this.frameIndex == 0) && (this.currentFrameObject.strike == false && this.currentFrameObject.spare == false)) {
  // }

  // if (this.scoreSheet[this.frameIndex - 1].strike == true && this.currentFrameObject.strike == false) { // checking if previous frame was a strike
  //   this.totalScore +=
  //
  // } else {
    this.totalScore += this.currentFrameObject.totalFrameScore;
  // }
  //
  // get a variable/property that stores the roll of strikes we're on like in old code. when strike happens + 1, when it doesnt reset it.
  // could there be an accumulating variable that stores the points
};

Game.prototype.gameOver = function() {
  if (this.frameIndex == 9 && this.currentFrameObject.strike == false) {
    this.isGameOver = true;
  }
};

// what happens when there is a half strike?

// game.prototype.resetGame = function() {
//
// }

// next thing to do check if at end of game
// scoresheet in a separate class? - not essential and might not need to.
// keep it neat and modular
<commit_msg>strike scoring system starting to shown signs of life, bit messy<commit_after>function Game() {
  this.frameIndex = -1; // -1 as 0 needs to be first index
  this.currentFrameObject = null; // this will be refreshed every frame to the newly created Frame
  this.scoreSheet = []; //[Frame, Frame] stores individual frames
  this.totalScore = 0; // is this needed? if so -- does it belong in Frame?
  this.isGameOver = false;
  this.strikeCount = 0;
}

Game.prototype.rollBall = function(pinsKnocked) {
  this.logRoll(pinsKnocked);
};

Game.prototype.logRoll = function(pinsKnocked) {

  if (this.isGameOver == true) {
    fail('Game Over, please reset the game');
  }

  if (this.currentFrameObject == null) {
    this.currentFrameObject = new Frame(); //create the frame object for every new frame(round)
    this.frameIndex += 1;//
  }

  if (this.currentFrameObject.rollIndex == 0) { //if its on its first roll:
    this.currentFrameObject.firstRoll(pinsKnocked); //update the frame with firstRoll score
    this.checkStrike(); // checks if there was a strike
  } else { // if its on its second roll and there was no strike
    this.currentFrameObject.secondRoll(pinsKnocked); // updates frame with the results of the second roll
    this.scoreSheet.push(this.currentFrameObject); // frame finished add the frame to scoreSheet:
    this.calculateScore(); // calculates score at end of the frame
    // this.strikeCount = 0; // resets strike count to 0
    this.gameOver(); // checks if the game is over
    this.currentFrameObject = null; //resets current frame
  }
};

Game.prototype.checkStrike = function() {
  if (this.currentFrameObject.strike == true) {
    this.scoreSheet.push(this.currentFrameObject);
    this.calculateScore();
    this.gameOver();
    this.currentFrameObject = null;
    this.strikeCount += 1;
    return;
  }
};

Game.prototype.calculateScore = function() {
if (this.frameIndex != 0 && this.scoreSheet[this.frameIndex - 1].strike == true && this.scoreSheet[this.frameIndex].strike == false) {
    console.log('wee') // checking if previous frame was a strike
    this.totalScore += ((this.strikeCount * 10 + this.currentFrameObject.totalFrameScore) + this.currentFrameObject.totalFrameScore);

  }  // if on first frame or if there were no strikes/spares
  else  if ((this.frameIndex != 0 || this.frameIndex == 0) && (this.scoreSheet[this.frameIndex].strike == false && this.scoreSheet[this.frameIndex].spare == false )) {
      this.totalScore += this.currentFrameObject.totalFrameScore;
    }
  // //
  // get a variable/property that stores the roll of strikes we're on like in old code. when strike happens + 1, when it doesnt reset it.
  // could there be an accumulating variable that stores the points
};

Game.prototype.gameOver = function() {
  if (this.frameIndex == 9 && this.currentFrameObject.strike == false) {
    this.isGameOver = true;
  }
};

// what happens when there is a half strike?

// game.prototype.resetGame = function() {
//
// }

// next thing to do check if at end of game
// scoresheet in a separate class? - not essential and might not need to.
// keep it neat and modular
<|endoftext|>


<commit_before>import dom from "../dom";
import lang from "wysihtml5/lang";
import quirks from "../quirks";
import { browser } from "../browser";
import { Constants } from "../constants";
import { Selection } from "../selection/selection";
import { Commands} from "../commands";
import { UndoManager } from "../undo_manager";
import { isNonPrintableKey } from "../helpers/is_non_printable_key";

var Composer = Base.extend({
  name: "composer",
  browser: browser,
  dom: dom,

  // Needed for firefox in order to display a proper caret in an empty contentEditable
  CARET_HACK: "
",

  _keyboardHandlers: [],
  _textSubstitutions: [],

  constructor: function(parent, editableElement, config) {
    this.parent = parent;
    this.element = editableElement;
    this.config = config;
    this.doc = document;
    this.editableArea = editableElement;
    this._create();
    this._setupFakeSelectionEvents();
  },

  clear: function() {
    this.element.innerHTML = browser.displaysCaretInEmptyContentEditableCorrectly() ? "" : this.CARET_HACK;
    this.undoManager.reset();
    this._checkForValueAndUpdateClass();
  },

  getValue: function(options) {
    var value;
    var element = this.element.cloneNode(true);

    if (!options) {
      options = {}
    }

    if (options.trim) {
      element = wysihtml5.dom.removeTrailingLineBreaks(element);
      element = wysihtml5.dom.removeEmptyNodes(element);
    }

    value = this.isEmpty() ? "" : quirks.getCorrectInnerHTML(element);

    if (options.parse) {
      var textNodes = this._textNodes(element);
      this._processNodesForBlockTextSubstitution(textNodes);
      value = this.parent.parse(value);
    }

    return value;
  },

  setValue: function(html, parse) {
    if (parse) {
      html = this.parent.parse(html);
    }
    this.element.innerHTML = html;
    this.undoManager.reset();
    this._checkForValueAndUpdateClass();
  },

  cleanUp: function() {
    this.parent.parse(this.element);
  },

  show: function() {
    this.editableArea.style.display = this._displayStyle || "";
  },

  hide: function() {
    this._displayStyle = dom.getStyle("display").from(this.editableArea);
    if (this._displayStyle === "none") {
      this._displayStyle = null;
    }
    this.editableArea.style.display = "none";
  },

  disable: function() {
    this.parent.fire("disable:composer");
    this.element.removeAttribute("contentEditable");
  },

  enable: function() {
    this.parent.fire("enable:composer");
    this.element.setAttribute("contentEditable", "true");
  },

  focus: function(setToEnd) {
    if (document.querySelector(":focus") === this.element) {
      return;
    }
    this.element.focus();

    var lastChild = this.element.lastChild;
    if (setToEnd && lastChild && this.selection) {
      if (lastChild.nodeName === "BR") {
        this.selection.setBefore(this.element.lastChild);
      } else {
        this.selection.setAfter(this.element.lastChild);
      }
    }
  },

  getTextContent: function() {
    return dom.getTextContent(this.element);
  },

  isEmpty: function() {
    return !this.element.textContent && !this.element.querySelector("img");
  },

  parentElement: function(node, matchingSet, options) {
    options = options || {};
    options.until = options.until || this.element;
    return dom.getParentElement(node, matchingSet, options);
  },

  _create: function() {
    var that = this;
    this.doc = document;
    this.element = this.editableArea;
    this.cleanUp(); // cleans contenteditable on initiation as it may contain html

    // Make sure our selection handler is ready
    this.selection = new Selection(this.parent, this, this.element, this.config.uneditableContainerClassname);

    // Make sure commands dispatcher is ready
    this.commands = new Commands(this.parent, this);

    // DEPRECATED
    dom.addClass(this.element, this.config.composerClassName);

    this.observe();

    // WTF
    var name = this.config.name;
    if (name) {
      dom.addClass(this.element, name);
    }

    this.enable();

    // Make sure that the browser avoids using inline styles whenever possible
    this.commands.exec("styleWithCSS", false);
    this.commands.exec("enableObjectResizing", false);
    this.commands.exec("insertBrOnReturn", false);

    this._initUndoManager();

    quirks.ensureProperClearing(this);

    // Fire global (before-)load event
    this.parent.fire("beforeload").fire("load");
    this._initLineBreaking();
  },

  _initUndoManager: function() {
    this.undoManager = new UndoManager(this);
  },

  _initLineBreaking: function() {
    var timeout;

    dom.observe(this.element, "blur", function(e) {
      clearTimeout(timeout);
    }.bind(this));

    dom.observe(this.element, ["focus", "keydown"], function(e) {
      if (e.type == "focus" || e.keyCode == Constants.BACKSPACE_KEY) {
        timeout = setTimeout(function() {
          this.ensureParagraph();
        }.bind(this), 0);
      }
    }.bind(this));

    dom.observe(this.element, "keydown", function(event) {
      this._handleKeyboardHandlers(event);
      this._lookForTextSubstitution(event);
    }.bind(this));
  },

  ensureParagraph: function(options) {
    options = options || {};
    var isEmpty = options.force ? !this.element.textContent.trim() : this.isEmpty();
    if (isEmpty) {
      var paragraph = document.createElement("P");
      this.element.innerHTML = "";
      this.element.appendChild(paragraph);
      if (!browser.displaysCaretInEmptyContentEditableCorrectly()) {
        paragraph.innerHTML = "
";
        this.selection.setBefore(paragraph.firstChild);
      } else {
        this.selection.selectNode(paragraph, true);
      }
      return paragraph;
    }
  },

  // Text Substitutions

  _lastInsertedBlock: function(range, e) {
    var nativeRange = range.nativeRange || range;
    var startContainer = nativeRange.startContainer;
    if (nativeRange.collapsed) {
      var blockElement = this.parentElement(startContainer, {
        nodeName: ["LI", "P", "H1", "H2", "H3", "H4", "H5", "H6", "PRE", "BLOCKQUOTE"]
      });
      if (blockElement) {
        var nodes = this._textNodes(blockElement);
        return nodes;
      }
    }
  },

  _processNodesForBlockTextSubstitution: function(nodes) {
    var _this = this;
    nodes.forEach(function(node) {
      _this._textSubstitutions.forEach(function(textSubstitution) {
        var textContent = node.textContent;
        if (textSubstitution.options.sentence !== false && textSubstitution.matcher(node.textContent, null, _this.parent, _this)) {
          var range = document.createRange();
          range.selectNodeContents(node);
          textSubstitution.callback(_this.parent, _this, range, textContent);
        }
      });
    });
  },

  _textNodes: function(node) {
    dom.normalize(node);
    return dom.all.textNodes(node);
  },

  _lastInsertedWordRange: function(range) {
    var nativeRange = range.nativeRange;
    var startContainer = nativeRange.startContainer;
    if (nativeRange.collapsed && startContainer.nodeType == Node.TEXT_NODE) {
      var wordRange = nativeRange.cloneRange();
      var textContent = startContainer.textContent.slice(0, nativeRange.startOffset);
      var lastIndex = textContent.lastIndexOf(" ");

      if (lastIndex != -1) {
        wordRange.setStart(nativeRange.startContainer, (lastIndex + 1));
      } else {
        wordRange.setStart(startContainer, 0);
      }
      return {
        range: wordRange,
        textContent: textContent.slice((lastIndex + 1))
      };
    }
  },

  _lookForTextSubstitution: function(e) {
    // Commit words on space and enter
    if (e.keyCode == Constants.SPACE_KEY || e.keyCode == Constants.ENTER_KEY) {
      var range = this.selection.getRange();
      var options = this._lastInsertedWordRange(range);
      if (options) {
        for (var index = 0; index < this._textSubstitutions.length; index++) {
          var textSubstitution = this._textSubstitutions[index];
          if (textSubstitution.options.word !== false && textSubstitution.matcher(options.textContent, e, this.parent, this, options.range)) {
            textSubstitution.callback(this.parent, this, options.range, options.textContent, e);
          }
        }
      }
    }

    // Also commit paragraphs on enter
    if (e.keyCode == Constants.ENTER_KEY) {
      var range = this.selection.getRange();
      var nodes = this._lastInsertedBlock(range, e);
      if (nodes && nodes.length) {
        this._processNodesForBlockTextSubstitution(nodes);
      }
    }
  },

  // Keyboard Processors

  _handleKeyboardHandlers: function(e) {
    for (var i = 0; i < this._keyboardHandlers.length; i++) {
      var keyboardHandler = this._keyboardHandlers[i];
      if (keyboardHandler.matcher(e)) {
        keyboardHandler.callback(this.parent, this, e);
      }
    };
  },

  _checkForPendingValueChangeAndUpdateClass: function(keyCode) {
    if (!isNonPrintableKey(keyCode)) {
      dom.addClass(this.element, "has-value");
    }
  },

  _checkForValueAndUpdateClass: function() {
    var VISUAL_BLOCKS, emptyText, containsVisualBlocks;
    VISUAL_BLOCKS = ["ul", "ol", "blockquote", "pre", "img"]

    for(var i = 0; i < VISUAL_BLOCKS.length; i++) {
      if(this.element.querySelectorAll(VISUAL_BLOCKS[i]).length > 0) {
        containsVisualBlocks = true;
        break;
      }
    }

    emptyText = !containsVisualBlocks && this.isEmpty() && this.element.querySelectorAll("p").length <= 1;

    if(emptyText) {
      dom.removeClass(this.element, "has-value");
    } else {
      dom.addClass(this.element, "has-value");
    }
  }
});

Composer.RegisterKeyboardHandler = function(matcher, callback) {
  Composer.prototype._keyboardHandlers.push({
    matcher: matcher,
    callback: callback
  });
};

Composer.RegisterTextSubstitution = function(matcher, callback, options) {
  Composer.prototype._textSubstitutions.push({
    matcher: matcher,
    callback: callback,
    options: options
  });
};

export { Composer };
<commit_msg>Use containsContent helper to determining if composer is empty<commit_after>import dom from "../dom";
import lang from "wysihtml5/lang";
import quirks from "../quirks";
import { browser } from "../browser";
import { Constants } from "../constants";
import { Selection } from "../selection/selection";
import { Commands} from "../commands";
import { UndoManager } from "../undo_manager";
import { isNonPrintableKey } from "../helpers/is_non_printable_key";
import { containsContent } from "../helpers/contains_content";

var Composer = Base.extend({
  name: "composer",
  browser: browser,
  dom: dom,

  // Needed for firefox in order to display a proper caret in an empty contentEditable
  CARET_HACK: "
",

  _keyboardHandlers: [],
  _textSubstitutions: [],

  constructor: function(parent, editableElement, config) {
    this.parent = parent;
    this.element = editableElement;
    this.config = config;
    this.doc = document;
    this.editableArea = editableElement;
    this._create();
    this._setupFakeSelectionEvents();
  },

  clear: function() {
    this.element.innerHTML = browser.displaysCaretInEmptyContentEditableCorrectly() ? "" : this.CARET_HACK;
    this.undoManager.reset();
    this._checkForValueAndUpdateClass();
  },

  getValue: function(options) {
    var value;
    var element = this.element.cloneNode(true);

    if (!options) {
      options = {}
    }

    if (options.trim) {
      element = wysihtml5.dom.removeTrailingLineBreaks(element);
      element = wysihtml5.dom.removeEmptyNodes(element);
    }

    value = !containsContent(element) ? "" : quirks.getCorrectInnerHTML(element);

    if (options.parse) {
      var textNodes = this._textNodes(element);
      this._processNodesForBlockTextSubstitution(textNodes);
      value = this.parent.parse(value);
    }

    return value;
  },

  setValue: function(html, parse) {
    if (parse) {
      html = this.parent.parse(html);
    }
    this.element.innerHTML = html;
    this.undoManager.reset();
    this._checkForValueAndUpdateClass();
  },

  cleanUp: function() {
    this.parent.parse(this.element);
  },

  show: function() {
    this.editableArea.style.display = this._displayStyle || "";
  },

  hide: function() {
    this._displayStyle = dom.getStyle("display").from(this.editableArea);
    if (this._displayStyle === "none") {
      this._displayStyle = null;
    }
    this.editableArea.style.display = "none";
  },

  disable: function() {
    this.parent.fire("disable:composer");
    this.element.removeAttribute("contentEditable");
  },

  enable: function() {
    this.parent.fire("enable:composer");
    this.element.setAttribute("contentEditable", "true");
  },

  focus: function(setToEnd) {
    if (document.querySelector(":focus") === this.element) {
      return;
    }
    this.element.focus();

    var lastChild = this.element.lastChild;
    if (setToEnd && lastChild && this.selection) {
      if (lastChild.nodeName === "BR") {
        this.selection.setBefore(this.element.lastChild);
      } else {
        this.selection.setAfter(this.element.lastChild);
      }
    }
  },

  getTextContent: function() {
    return dom.getTextContent(this.element);
  },

  isEmpty: function(element) {
    return !containsContent(this.element);
  },

  parentElement: function(node, matchingSet, options) {
    options = options || {};
    options.until = options.until || this.element;
    return dom.getParentElement(node, matchingSet, options);
  },

  _create: function() {
    var that = this;
    this.doc = document;
    this.element = this.editableArea;
    this.cleanUp(); // cleans contenteditable on initiation as it may contain html

    // Make sure our selection handler is ready
    this.selection = new Selection(this.parent, this, this.element, this.config.uneditableContainerClassname);

    // Make sure commands dispatcher is ready
    this.commands = new Commands(this.parent, this);

    // DEPRECATED
    dom.addClass(this.element, this.config.composerClassName);

    this.observe();

    // WTF
    var name = this.config.name;
    if (name) {
      dom.addClass(this.element, name);
    }

    this.enable();

    // Make sure that the browser avoids using inline styles whenever possible
    this.commands.exec("styleWithCSS", false);
    this.commands.exec("enableObjectResizing", false);
    this.commands.exec("insertBrOnReturn", false);

    this._initUndoManager();

    quirks.ensureProperClearing(this);

    // Fire global (before-)load event
    this.parent.fire("beforeload").fire("load");
    this._initLineBreaking();
  },

  _initUndoManager: function() {
    this.undoManager = new UndoManager(this);
  },

  _initLineBreaking: function() {
    var timeout;

    dom.observe(this.element, "blur", function(e) {
      clearTimeout(timeout);
    }.bind(this));

    dom.observe(this.element, ["focus", "keydown"], function(e) {
      if (e.type == "focus" || e.keyCode == Constants.BACKSPACE_KEY) {
        timeout = setTimeout(function() {
          this.ensureParagraph();
        }.bind(this), 0);
      }
    }.bind(this));

    dom.observe(this.element, "keydown", function(event) {
      this._handleKeyboardHandlers(event);
      this._lookForTextSubstitution(event);
    }.bind(this));
  },

  ensureParagraph: function(options) {
    options = options || {};
    var isEmpty = options.force ? !this.element.textContent.trim() : this.isEmpty();
    if (isEmpty) {
      var paragraph = document.createElement("P");
      this.element.innerHTML = "";
      this.element.appendChild(paragraph);
      if (!browser.displaysCaretInEmptyContentEditableCorrectly()) {
        paragraph.innerHTML = "
";
        this.selection.setBefore(paragraph.firstChild);
      } else {
        this.selection.selectNode(paragraph, true);
      }
      return paragraph;
    }
  },

  // Text Substitutions

  _lastInsertedBlock: function(range, e) {
    var nativeRange = range.nativeRange || range;
    var startContainer = nativeRange.startContainer;
    if (nativeRange.collapsed) {
      var blockElement = this.parentElement(startContainer, {
        nodeName: ["LI", "P", "H1", "H2", "H3", "H4", "H5", "H6", "PRE", "BLOCKQUOTE"]
      });
      if (blockElement) {
        var nodes = this._textNodes(blockElement);
        return nodes;
      }
    }
  },

  _processNodesForBlockTextSubstitution: function(nodes) {
    var _this = this;
    nodes.forEach(function(node) {
      _this._textSubstitutions.forEach(function(textSubstitution) {
        var textContent = node.textContent;
        if (textSubstitution.options.sentence !== false && textSubstitution.matcher(node.textContent, null, _this.parent, _this)) {
          var range = document.createRange();
          range.selectNodeContents(node);
          textSubstitution.callback(_this.parent, _this, range, textContent);
        }
      });
    });
  },

  _textNodes: function(node) {
    dom.normalize(node);
    return dom.all.textNodes(node);
  },

  _lastInsertedWordRange: function(range) {
    var nativeRange = range.nativeRange;
    var startContainer = nativeRange.startContainer;
    if (nativeRange.collapsed && startContainer.nodeType == Node.TEXT_NODE) {
      var wordRange = nativeRange.cloneRange();
      var textContent = startContainer.textContent.slice(0, nativeRange.startOffset);
      var lastIndex = textContent.lastIndexOf(" ");

      if (lastIndex != -1) {
        wordRange.setStart(nativeRange.startContainer, (lastIndex + 1));
      } else {
        wordRange.setStart(startContainer, 0);
      }
      return {
        range: wordRange,
        textContent: textContent.slice((lastIndex + 1))
      };
    }
  },

  _lookForTextSubstitution: function(e) {
    // Commit words on space and enter
    if (e.keyCode == Constants.SPACE_KEY || e.keyCode == Constants.ENTER_KEY) {
      var range = this.selection.getRange();
      var options = this._lastInsertedWordRange(range);
      if (options) {
        for (var index = 0; index < this._textSubstitutions.length; index++) {
          var textSubstitution = this._textSubstitutions[index];
          if (textSubstitution.options.word !== false && textSubstitution.matcher(options.textContent, e, this.parent, this, options.range)) {
            textSubstitution.callback(this.parent, this, options.range, options.textContent, e);
          }
        }
      }
    }

    // Also commit paragraphs on enter
    if (e.keyCode == Constants.ENTER_KEY) {
      var range = this.selection.getRange();
      var nodes = this._lastInsertedBlock(range, e);
      if (nodes && nodes.length) {
        this._processNodesForBlockTextSubstitution(nodes);
      }
    }
  },

  // Keyboard Processors

  _handleKeyboardHandlers: function(e) {
    for (var i = 0; i < this._keyboardHandlers.length; i++) {
      var keyboardHandler = this._keyboardHandlers[i];
      if (keyboardHandler.matcher(e)) {
        keyboardHandler.callback(this.parent, this, e);
      }
    };
  },

  _checkForPendingValueChangeAndUpdateClass: function(keyCode) {
    if (!isNonPrintableKey(keyCode)) {
      dom.addClass(this.element, "has-value");
    }
  },

  _checkForValueAndUpdateClass: function() {
    var VISUAL_BLOCKS, emptyText, containsVisualBlocks;
    VISUAL_BLOCKS = ["ul", "ol", "blockquote", "pre", "img"]

    for(var i = 0; i < VISUAL_BLOCKS.length; i++) {
      if(this.element.querySelectorAll(VISUAL_BLOCKS[i]).length > 0) {
        containsVisualBlocks = true;
        break;
      }
    }

    emptyText = !containsVisualBlocks && this.isEmpty() && this.element.querySelectorAll("p").length <= 1;

    if(emptyText) {
      dom.removeClass(this.element, "has-value");
    } else {
      dom.addClass(this.element, "has-value");
    }
  }
});

Composer.RegisterKeyboardHandler = function(matcher, callback) {
  Composer.prototype._keyboardHandlers.push({
    matcher: matcher,
    callback: callback
  });
};

Composer.RegisterTextSubstitution = function(matcher, callback, options) {
  Composer.prototype._textSubstitutions.push({
    matcher: matcher,
    callback: callback,
    options: options
  });
};

export { Composer };
<|endoftext|>


<commit_before>/**
 * Launch Screen
 *  - Shows a nice loading screen whilst:
 *    - Preloading any specified app content
 *    - Checking if user is logged in, and redirects from there
 *
 * React Native Starter App
 * https://github.com/mcnamee/react-native-starter-app
 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  View,
  Image,
  Alert,
  StatusBar,
  StyleSheet,
  ActivityIndicator,
} from 'react-native';
import { Actions } from 'react-native-router-flux';

// Consts and Libs
import { AppStyles, AppSizes } from '@theme/';

/* Styles ==================================================================== */
const styles = StyleSheet.create({
  launchImage: {
    width: AppSizes.screen.width,
    height: AppSizes.screen.height,
  },
});

/* Component ==================================================================== */
class AppLaunch extends Component {
  static componentName = 'AppLaunch';

  static propTypes = {
    login: PropTypes.func.isRequired,
    getRecipes: PropTypes.func.isRequired,
    getMeals: PropTypes.func.isRequired,
  }

  componentDidMount = () => {
    // Show status bar on app launch
    StatusBar.setHidden(false, true);

    // Preload content here
    Promise.all([
      this.props.getMeals(),
      this.props.getRecipes(),
    ]).then(() => {
      // Once we've preloaded basic content,
      // - Try to authenticate based on existing token
      this.props.login()
        // Logged in, show index screen
        .then(() => Actions.app({ type: 'reset' }))
        // Not Logged in, show Login screen
        .catch(() => Actions.authenticate({ type: 'reset' }));
    }).catch(err => Alert.alert(err.message));
  }

  render = () => (
    <View style={[AppStyles.container]}>
      <Image
        source={require('../../images/launch.jpg')}
        style={[styles.launchImage, AppStyles.containerCentered]}
      >
        <ActivityIndicator
          animating
          size={'large'}
          color={'#C1C5C8'}
        />
      </Image>
    </View>
  );
}

/* Export Component ==================================================================== */
export default AppLaunch;
<commit_msg>Ignore Android 'Setting a timer' yellow box - RE: Firebase SDK<commit_after>/**
 * Launch Screen
 *  - Shows a nice loading screen whilst:
 *    - Preloading any specified app content
 *    - Checking if user is logged in, and redirects from there
 *
 * React Native Starter App
 * https://github.com/mcnamee/react-native-starter-app
 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  View,
  Image,
  Alert,
  StatusBar,
  StyleSheet,
  ActivityIndicator,
} from 'react-native';
import { Actions } from 'react-native-router-flux';

// Consts and Libs
import { AppStyles, AppSizes } from '@theme/';

/* Styles ==================================================================== */
const styles = StyleSheet.create({
  launchImage: {
    width: AppSizes.screen.width,
    height: AppSizes.screen.height,
  },
});

/* Component ==================================================================== */
class AppLaunch extends Component {
  static componentName = 'AppLaunch';

  static propTypes = {
    login: PropTypes.func.isRequired,
    getRecipes: PropTypes.func.isRequired,
    getMeals: PropTypes.func.isRequired,
  }

  constructor() {
    super();
    console.ignoredYellowBox = ['Setting a timer'];
  }

  componentDidMount = () => {
    // Show status bar on app launch
    StatusBar.setHidden(false, true);

    // Preload content here
    Promise.all([
      this.props.getMeals(),
      this.props.getRecipes(),
    ]).then(() => {
      // Once we've preloaded basic content,
      // - Try to authenticate based on existing token
      this.props.login()
        // Logged in, show index screen
        .then(() => Actions.app({ type: 'reset' }))
        // Not Logged in, show Login screen
        .catch(() => Actions.authenticate({ type: 'reset' }));
    }).catch(err => Alert.alert(err.message));
  }

  render = () => (
    <View style={[AppStyles.container]}>
      <Image
        source={require('../../images/launch.jpg')}
        style={[styles.launchImage, AppStyles.containerCentered]}
      >
        <ActivityIndicator
          animating
          size={'large'}
          color={'#C1C5C8'}
        />
      </Image>
    </View>
  );
}

/* Export Component ==================================================================== */
export default AppLaunch;
<|endoftext|>


<commit_before>/*
 * gioc
 * https://github.com/goliatone/gioc
 *
 * Copyright (c) 2013 goliatone
 * Licensed under the MIT license.
 */
/*global define:true*/
/* jshint strict: false */
define('gioc', function() {

    /**
     * [ description]
     * @param  {[type]} config [description]
     * @return {[type]}        [description]
     */
    var Gioc = function(config){
        // this.services = {};
		this.instances = {};
		this.factories = {};
    };

    /**
     * [addFactory description]
     * @param {[type]} key     [description]
     * @param {[type]} factory [description]
     */
    Gioc.prototype.addFactory = function(key, factory){
        this.factories[key] = factory;
        return this;
    };

    /**
     * [ description]
     * @param  {[type]} key [description]
     * @return {[type]}     [description]
     */
    Gioc.prototype.hasInjector = function(key){
        return (key in this.instances || key in this.factories);
    };

    /**
     * Add instance to Gioc.
     * @param String key      Id of the instance.
     * @param Object instance Object instance.
     */
    Gioc.prototype.addInstance = function (key, instance) {
        this.instances[key] = instance;
        return this;
    };

    /**
     * Returns an instance by ID
     * @param  {[type]} key [description]
     * @return {[type]}     [description]
     */
    Gioc.prototype.solveKey = function (key) {
        var instance = this.instances[key];

        if (!instance) {
            var _Factory = this.factories[key];
            instance = _Factory();
            this.addInstance(key, instance);
        }

        return instance;
    };

    /**
     * [inject description]
     * @param  {[type]} scope  [description]
     * @param  {[type]} key    [description]
     * @param  {[type]} setter [description]
     * @param  {[type]} post   [description]
     * @return {[type]}        [description]
     */
    Gioc.prototype.inject = function (scope, key, setter, post, postArgs) {
        var value = this.solveKey(key);
        if(!value) return;

        if(typeof setter === 'function') setter.call(scope, value, key);
        else if( typeof setter === 'string') scope[setter] = value;

        if(post && typeof post === 'function')
            post.call(scope, postArgs);

        return this;
    };

    /**
     * [solveMappings description]
     * @param  {[type]} scope    [description]
     * @param  {[type]} mappings [description]
     * @param  {[type]} post     [description]
     * @return {[type]}          [description]
     */
    Gioc.prototype.solveMappings = function(scope, mappings, post){
        var mapping, args= Array.prototype.splice.call(arguments,3);
        for( var i = 0, t = mappings.length; i<t; i++){
            mapping = mappings[i];
            this.inject(scope, mapping.key, mapping.setter, mapping.post, mapping.postArgs);
        }
        post.apply(scope, args);

        return this;
    };

    return Gioc;
});<commit_msg>Refactoring injection cycle.<commit_after>/*
 * gioc
 * https://github.com/goliatone/gioc
 *
 * Copyright (c) 2013 goliatone
 * Licensed under the MIT license.
 */
/*global define:true*/
/* jshint strict: false */
define('gioc', function() {

////////////////////////////////////////
/// PRIVATE METHODS
////////////////////////////////////////

    var _slice = slice = Array.prototype.slice;

    /**
     * Extend method.
     * @param  {Object} target Source object
     * @return {Object}        Resulting object from
     *                         meging target to params.
     */
    var _extend = function(target) {
        var i = 1, length = arguments.length, source;
        for ( ; i < length; i++ ) {
            // Only deal with defined values
            if ((source = arguments[i]) != undefined ){
                Object.getOwnPropertyNames(source).forEach(function(k){
                    var d = Object.getOwnPropertyDescriptor(source, k) || {value:source[k]};
                    if (d.get) {
                        target.__defineGetter__(k, d.get);
                        if (d.set) target.__defineSetter__(k, d.set);
                    } else if (target !== d.value) target[k] = d.value;
                });
            }
        }
        return target;
    };

////////////////////////////////////////
/// CONSTRUCTOR
////////////////////////////////////////

    /**
     * [ description]
     * @param  {[type]} config [description]
     * @return {[type]}        [description]
     */
    var Gioc = function(config){
        //Store all bean info.
        this.beans = {};

        //TODO: This should be configurable.
        this.depsKey = 'deps';
        this.propKey = 'props';

        this.instances = {};
        this.factories = {};
    };

////////////////////////////////////////
/// PUBLIC METHODS
////////////////////////////////////////
    Gioc.prototype.map = function(key, payload, config){
        //Store basic information of our payload.
        var bean = {key:key, load:payload, config:(config || {})};
        //Is this a factory or a literal value?
        bean.construct =
        bean.isFactory = typeof payload === 'function';
        //TODO: How do we want to handle collision? We are overriding.
        this.beans[key] = bean;

        return this;
    };

    Gioc.prototype.solve = function(key, options){
        if (!this.mapped(key)) return undefined;

        var value  = null,
            bean   = this.beans[key],
            config = _extend({}, bean.config, options);
        console.log('==> solve, generated config ', config);
        //build our value.
        value = this.build(key, config);

        //configure our value
        value = this.wire(value, config);

        return value;
    };

    Gioc.prototype.build = function(key, options){
        var bean   = this.beans[key],
            value  = bean.load,
            config = _extend({}, bean.config, options);
            console.log('build, generated config ', config);
        //TODO: Do we want to cache? Or we handle that
        //on the factory?
        if(bean.construct){
            var args   = config.args,
                scope  = config.scope;

            //TODO: Make sure that undefined scope and args
            //does not break anything
            value = value.apply(scope, args);
        }

        return value;
    };

    //Meant to be overriden with use.
    Gioc.prototype.wire = function(target, config){
        config = config || {};
        console.log('Wiring: ', target, config, typeof target === 'object', 'modifier' in config);

        //We have a literal value. We might want to modify it?
        if(typeof target !== 'object' && 'modifier' in config)
            return config.modifier(target);

        //We need a collection of key to handler, and iterate over
        if(this.propKey in config) _extend(target, config[this.propKey]);

        //Solve dependencies: each individual dependency can be:
        // a)id => bean id
        // b)object {id:id, options:options}
        if(this.depsKey in config){
            config[this.depsKey].forEach((function(bean){
                //Normalize bean def, if string, we ride on defaults.
                if(typeof bean === 'string') bean = {id:bean, options:{setter:bean}};
                //We should try catch this.
                this.inject(target, bean.id, bean.options);
            }).bind(this));
            console.log(this.solve(config.deps[0]));
        }

        return target;
    };

    /**
     * [inject description]
     * @param  {[type]} scope  [description]
     * @param  {[type]} key    [description]
     * @param  {[type]} setter [description]
     * @param  {[type]} post   [description]
     * @return {[type]}        [description]
     */
    Gioc.prototype.inject = function (scope, key, options) {
        var setter = options.setter || key,
            value  = this.solve(key, options);

        if(typeof setter === 'function') scope.call(scope, value, key);
        else if( typeof setter === 'string') scope[setter] = value;

        if('post' in options) options.post.apply(scope, options.postArgs);

        return this;
    };

    /**
     * [ description]
     * @param  {[type]} key [description]
     * @return {[type]}     [description]
     */
    Gioc.prototype.mapped = function(key){
        return (key in this.beans);
    };

    /**
     * Returns an instance by ID
     * @param  {[type]} key [description]
     * @return {[type]}     [description]
     */
    Gioc.prototype.solveKey = function (key) {
        var instance = this.instances[key];

        if (!instance) {
            var _Factory = this.factories[key];
            instance = _Factory();
            this.addInstance(key, instance);
        }

        return instance;
    };

    /**
     * [solveMappings description]
     * @param  {[type]} scope    [description]
     * @param  {[type]} mappings [description]
     * @param  {[type]} post     [description]
     * @return {[type]}          [description]
     */
    Gioc.prototype.solveMappings = function(scope, mappings, post){
        var mapping, args= Array.prototype.splice.call(arguments,3);
        for( var i = 0, t = mappings.length; i<t; i++){
            mapping = mappings[i];
            this.inject(scope, mapping.key, mapping.setter, mapping.post, mapping.postArgs);
        }
        post.apply(scope, args);

        return this;
    };

    return Gioc;
});<|endoftext|>


<commit_before>import React, {Component} from 'react';

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

Doh! 404!

These are not the droids you are looking for!

    );
  }
}
<commit_msg>Make stateless components simple functions<commit_after>import React from 'react';

export default function NotFound() {
  return (

Doh! 404!

These are not the droids you are looking for!

  );
}
<|endoftext|>