<commit_before>require('moonstone');

/**
* Contains the declaration for the {@link module:moonstone/IconButton~IconButton} kind.
* @module moonstone/IconButton
*/

var
	kind = require('enyo/kind');

var
	Icon = require('../Icon');

/**
* {@link module:moonstone/IconButton~IconButton} is a {@link module:moonstone/Icon~Icon} that acts like a button. Specify
* the icon image by setting the [src]{@link module:moonstone/Icon~Icon#src} property to a URL
* indicating the image file's location.
*
* ```
* {kind: 'moon.IconButton', src: 'images/search.png'}
* ```
*
* If you want to combine an icon with text inside of a button, use a
* `moon.Icon` inside a {@link module:moonstone/Button~Button}.
*
* Moonstone supports two methods for displaying icons; in addition to specifying
* traditional image assets in `src`, you may use icons that are stored as single
* characters in a special symbol font. To do this, set the value of the
* [icon]{@link module:moonstone/Icon~Icon#icon} property to a string representing an icon name,
* e.g.:
*
* ```
* {kind: 'moon.IconButton', icon: 'closex'}
* ```
*
* See {@link module:moonstone/Icon~Icon} for more information on the available font-based icons,
* as well as specifications for icon image assets.
*
* @class IconButton
* @extends module:moonstone/Icon~Icon
* @ui
* @public
*/
module.exports = kind(
	/** @lends module:moonstone/IconButton~IconButton.prototype */ {

	/**
	* @private
	*/
	name: 'moon.IconButton',

	/**
	* @private
	*/
	kind: Icon,

	/**
	* @private
	* @lends module:moonstone/IconButton~IconButton.prototype
	*/
	published: {

		/**
		* Used when the IconButton is part of an {@link module:enyo/Group~Group}. A value of `true`
		* indicates that this is the active button of the group; `false`, that it is not
		* the active button.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		active: false,

		/**
		* A boolean parameter affecting the size of the button.
		* If `true`, the button will have a diameter of 60px.
		* However, the button's tap target will still have a diameter of 78px, with
		* invisible DOM wrapping the small button to provide the larger tap zone.
		*
		* @type {Boolean}
		* @default true
		* @public
		*/
		small: true,

		/**
		* @deprecated Replaced by [backgroundOpacity]{@link moon.IconButton#backgroundOpacity}.
		*
		* If `true`, the button will have no rounded background color/border.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		noBackground: false,

		/**
		* The background-color opacity of this button; valid alues are `'opaque'`, `'translucent'`,
		* and `'transparent'`.
		*
		* @type {String}
		* @default opaque
		* @public
		*/
		backgroundOpacity: 'opaque'
	},

	/**
	* @private
	*/
	classes: 'moon-icon-button',

	/**
	* @private
	*/
	spotlight: true,

	/**
	* @private
	*/
	handlers: {

		/**
		* Simulates mousedown.
		*/
		onSpotlightKeyDown: 'depress',

		/**
		* Simulates mouseup.
		*/
		onSpotlightKeyUp: 'undepress',

		/**
		* Used to request it is in view in scrollers.
		*/
		onSpotlightFocused: 'spotlightFocused',

		onSpotlightBlur: 'spotlightBlurred'
	},

	/**
	* @private
	*/
	create: function () {
		Icon.prototype.create.apply(this, arguments);
		if (this.noBackground) {
			this.noBackgroundChanged();
		}
		this.backgroundOpacityChanged();
	},

	/**
	* @private
	*/
	rendered: function () {
		Icon.prototype.rendered.apply(this, arguments);
		this.activeChanged();
	},

	/**
	* @private
	*/
	noBackgroundChanged: function () {
		this.set('backgroundOpacity', this.noBackground ? 'transparent' : 'opaque');
	},

	/**
	* @private
	*/
	tap: function () {
		if (this.disabled) {
			return true;
		}
		this.setActive(true);
	},

	/**
	* @fires module:enyo/GroupItem~GroupItem#onActivate
	* @private
	*/
	activeChanged: function () {
		this.bubble('onActivate');
	},

	/**
	* Adds `pressed` CSS class.
	* @private
	*/
	depress: function (inSender, inEvent) {
		if (inEvent.keyCode === 13) {
			this.addClass('pressed');
		}
	},

	/**
	* Removes `pressed` CSS class.
	* @private
	*/
	undepress: function () {
		this.removeClass('pressed');
	},

	/**
	* @fires module:moonstone/Scroller~Scroller#onRequestScrollIntoView
	* @private
	*/
	spotlightFocused: function (inSender, inEvent) {
		if (inEvent.originator === this) {
			this.bubble('onRequestScrollIntoView');
		}
	},

	/**
	* @private
	*/
	spotlightBlurred: function (sender, event) {
		this.removeClass('pressed');
	},

	/**
	* @private
	*/
	backgroundOpacityChanged: function (old) {
		var opacity = this.backgroundOpacity;
		if (old) this.removeClass(old);
		if (opacity == 'translucent' || opacity == 'transparent') {
			this.addClass(opacity);
		}
	},

	// Accessibility

	/**
	* @default button
	* @type {String}
	* @see enyo/AccessibilitySupport~AccessibilitySupport#accessibilityRole
	* @public
	*/
	accessibilityRole: 'button'
});
<commit_msg>JSDoc cleanup Enyo-DCO-1.1-Signed-Off-By: Jim Tang (jim.tang@lge.com)<commit_after>require('moonstone');

/**
* Contains the declaration for the {@link module:moonstone/IconButton~IconButton} kind.
* @module moonstone/IconButton
*/

var
	kind = require('enyo/kind');

var
	Icon = require('../Icon');

/**
* {@link module:moonstone/IconButton~IconButton} is a {@link module:moonstone/Icon~Icon} that acts like a button. Specify
* the icon image by setting the [src]{@link module:moonstone/Icon~Icon#src} property to a URL
* indicating the image file's location.
*
* ```
* var
* 	kind = require('enyo/kind'),
* 	IconButton = require('moonstone/IconButton');
*
* {kind: IconButton, src: 'images/search.png'}
* ```
*
* If you want to combine an icon with text inside of a button, use a
* {@link module:moonstone/Icon~Icon} inside a
* {@link module:moonstone/Button~Button}.
*
* Moonstone supports two methods for displaying icons; in addition to specifying
* traditional image assets in `src`, you may use icons that are stored as single
* characters in a special symbol font. To do this, set the value of the
* [icon]{@link module:moonstone/Icon~Icon#icon} property to a string representing
* an icon name, e.g.:
*
* ```
* {kind: IconButton, icon: 'closex'}
* ```
*
* See {@link module:moonstone/Icon~Icon} for more information on the available font-based icons,
* as well as specifications for icon image assets.
*
* @class IconButton
* @extends module:moonstone/Icon~Icon
* @ui
* @public
*/
module.exports = kind(
	/** @lends module:moonstone/IconButton~IconButton.prototype */ {

	/**
	* @private
	*/
	name: 'moon.IconButton',

	/**
	* @private
	*/
	kind: Icon,

	/**
	* @private
	* @lends module:moonstone/IconButton~IconButton.prototype
	*/
	published: {

		/**
		* Used when the IconButton is part of an {@link module:enyo/Group~Group}. A value of `true`
		* indicates that this is the active button of the group; `false`, that it is not
		* the active button.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		active: false,

		/**
		* A boolean parameter affecting the size of the button.
		* If `true`, the button will have a diameter of 60px.
		* However, the button's tap target will still have a diameter of 78px, with
		* invisible DOM wrapping the small button to provide the larger tap zone.
		*
		* @type {Boolean}
		* @default true
		* @public
		*/
		small: true,

		/**
		* @deprecated Replaced by [backgroundOpacity]{@link moon/IconButton~IconButton#backgroundOpacity}.
		*
		* If `true`, the button will have no rounded background color/border.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		noBackground: false,

		/**
		* The background-color opacity of this button; valid values are `'opaque'`, `'translucent'`,
		* and `'transparent'`.
		*
		* @type {String}
		* @default opaque
		* @public
		*/
		backgroundOpacity: 'opaque'
	},

	/**
	* @private
	*/
	classes: 'moon-icon-button',

	/**
	* @private
	*/
	spotlight: true,

	/**
	* @private
	*/
	handlers: {

		/**
		* Simulates mousedown.
		*/
		onSpotlightKeyDown: 'depress',

		/**
		* Simulates mouseup.
		*/
		onSpotlightKeyUp: 'undepress',

		/**
		* Used to request it is in view in scrollers.
		*/
		onSpotlightFocused: 'spotlightFocused',

		onSpotlightBlur: 'spotlightBlurred'
	},

	/**
	* @private
	*/
	create: function () {
		Icon.prototype.create.apply(this, arguments);
		if (this.noBackground) {
			this.noBackgroundChanged();
		}
		this.backgroundOpacityChanged();
	},

	/**
	* @private
	*/
	rendered: function () {
		Icon.prototype.rendered.apply(this, arguments);
		this.activeChanged();
	},

	/**
	* @private
	*/
	noBackgroundChanged: function () {
		this.set('backgroundOpacity', this.noBackground ? 'transparent' : 'opaque');
	},

	/**
	* @private
	*/
	tap: function () {
		if (this.disabled) {
			return true;
		}
		this.setActive(true);
	},

	/**
	* @fires module:enyo/GroupItem~GroupItem#onActivate
	* @private
	*/
	activeChanged: function () {
		this.bubble('onActivate');
	},

	/**
	* Adds `pressed` CSS class.
	* @private
	*/
	depress: function (inSender, inEvent) {
		if (inEvent.keyCode === 13) {
			this.addClass('pressed');
		}
	},

	/**
	* Removes `pressed` CSS class.
	* @private
	*/
	undepress: function () {
		this.removeClass('pressed');
	},

	/**
	* @fires module:moonstone/Scroller~Scroller#onRequestScrollIntoView
	* @private
	*/
	spotlightFocused: function (inSender, inEvent) {
		if (inEvent.originator === this) {
			this.bubble('onRequestScrollIntoView');
		}
	},

	/**
	* @private
	*/
	spotlightBlurred: function (sender, event) {
		this.removeClass('pressed');
	},

	/**
	* @private
	*/
	backgroundOpacityChanged: function (old) {
		var opacity = this.backgroundOpacity;
		if (old) this.removeClass(old);
		if (opacity == 'translucent' || opacity == 'transparent') {
			this.addClass(opacity);
		}
	},

	// Accessibility

	/**
	* @default 'button'
	* @type {String}
	* @see {@link module:enyo/AccessibilitySupport~AccessibilitySupport#accessibilityRole}
	* @public
	*/
	accessibilityRole: 'button'
});
<|endoftext|>


<commit_before>var PrintDocumentationCommand = function () {
  var handle = function () {
    console.log('Space Kitty - Version 1.0.0');
    console.log('');
    console.log('├── create             ─ Create a new project');
    console.log('├── make');
    console.log('|    ├── command       ─ Create a dispatchable command');
    console.log('|    ├── collection    ─ Create a collection (only supports Mongo)');
    console.log('|    ├── meteor-method ─ Create a Meteor method');
    console.log('|    ├── migration     ─ Create a migration (uses percolate:migrations)');
    console.log('|    └── view          ─ Create a view module');
    console.log('├── remind-me');
    console.log('|    ├── meteor-call   ─ Reminds you how to call a meteor method');
    console.log('|    └── react-loop    ─ Reminds you how to achieve dynamic children in React');
    console.log('└── meow               ─ Verify that Space Kitty can meow');
  };

  return {
    handle: handle
  }
};

module.exports = PrintDocumentationCommand;
<commit_msg>Use package version for printing<commit_after>var pjson = require('./package.json');

var PrintDocumentationCommand = function () {
  var handle = function () {
    console.log('Space Kitty - Version ' + pjson.version);
    console.log('');
    console.log('├── create             ─ Create a new project');
    console.log('├── make');
    console.log('|    ├── command       ─ Create a dispatchable command');
    console.log('|    ├── collection    ─ Create a collection (only supports Mongo)');
    console.log('|    ├── meteor-method ─ Create a Meteor method');
    console.log('|    ├── migration     ─ Create a migration (uses percolate:migrations)');
    console.log('|    └── view          ─ Create a view module');
    console.log('├── remind-me');
    console.log('|    ├── meteor-call   ─ Reminds you how to call a meteor method');
    console.log('|    └── react-loop    ─ Reminds you how to achieve dynamic children in React');
    console.log('└── meow               ─ Verify that Space Kitty can meow');
  };

  return {
    handle: handle
  }
};

module.exports = PrintDocumentationCommand;
<|endoftext|>


<commit_before>/**
 * @file Representation Component
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import Signal from "../../lib/signals.es6.js";

import { defaults } from "../utils.js";
import Component from "./component.js";

// add here to avoid cyclic import dependency
Component.prototype.__getRepresentationComponent = function( repr, p ){
    return new RepresentationComponent(
        this.stage, repr, p, this
    );
};

/**
 * Extends {@link ComponentSignals}
 *
 * @typedef {Object} RepresentationComponentSignals
 * @property {Signal<String>} parametersChanged - on parameters change
 */

/**
 * Component wrapping a {@link Representation} object
 */
class RepresentationComponent extends Component{

    /**
     * Create representation component
     * @param {Stage} stage - stage object the component belongs to
     * @param {Representation} repr - representation object to wrap
     * @param {RepresentationParameters} [params] - component parameters
     * @param {Component} [parent] - parent component
     */
    constructor( stage, repr, params, parent ){

        var p = params || {};
        p.name = defaults( p.name, repr.type );

        super( stage, p );

        /**
         * Events emitted by the component
         * @type {RepresentationComponentSignals}
         */
        this.signals = Object.assign( this.signals, {
            parametersChanged: new Signal()
        } );

        this.parent = parent;

        this.setRepresentation( repr );

    }

    /**
     * Component type
     * @type {String}
     */
    get type(){ return "representation"; }

    getType(){

        return this.repr.type;

    }

    setRepresentation( repr ){

        this.disposeRepresentation();
        this.repr = repr;
        // this.name = repr.type;
        this.stage.tasks.listen( this.repr.tasks );
        this.updateVisibility();

    }

    /**
     * @ignore
     * @alias RepresentationComponent#addRepresentation
     * @return {undefined}
     */
    addRepresentation(){}

    /**
     * @ignore
     * @alias RepresentationComponent#removeRepresentation
     * @return {undefined}
     */
    removeRepresentation(){}

    /**
     * @ignore
     * @alias RepresentationComponent#hasRepresentation
     * @return {undefined}
     */
    hasRepresentation(){}

    disposeRepresentation(){

        if( this.repr ){
            this.stage.tasks.unlisten( this.repr.tasks );
            this.repr.dispose();
        }

    }

    dispose(){

        if( this.parent && this.parent.hasRepresentation( this ) ){
            this.parent.removeRepresentation( this );
        }else{
            this.disposeRepresentation();
            this.signals.disposed.dispatch();
        }

    }

    /**
     * Set the visibility of the component, takes parent visibility into account
     * @param {Boolean} value - visibility flag
     * @return {RepresentationComponent} this object
     */
    setVisibility( value ){

        this.visible = value;
        this.updateVisibility();
        this.signals.visibilityChanged.dispatch( this.visible );

        return this;

    }

    getVisibility(){

        if( this.parent ){
            return this.parent.visible && this.visible;
        }else{
            return this.visible;
        }

    }

    updateVisibility(){

        this.repr.setVisibility( this.getVisibility() );

    }

    /**
     * Set selection
     * @param {Object} what - flags indicating what attributes to update
     * @param {Boolean} what.position - update position attribute
     * @param {Boolean} what.color - update color attribute
     * @param {Boolean} what.radius - update radius attribute
     * @return {RepresentationComponent} this object
     */
    update( what ){

        this.repr.update( what );

        return this;

    }

    build( params ){

        this.repr.build( params );

        return this;

    }

    /**
     * Set selection
     * @param {String} string - selection string
     * @return {RepresentationComponent} this object
     */
    setSelection( string ){

        this.repr.setSelection( string );

        return this;

    }

    /**
     * Set representation parameters
     * @param {RepresentationParameters} params - parameter object
     * @return {RepresentationComponent} this object
     */
    setParameters( params ){

        this.repr.setParameters( params );
        this.signals.parametersChanged.dispatch(
            this.repr.getParameters()
        );

        return this;

    }

    /**
     * Get representation parameters
     * @return {RepresentationParameters} parameter object
     */
    getParameters(){

        return this.repr.getParameters();

    }

    /**
     * Set color
     * @param {String|Color|Hex} value - color value
     * @return {RepresentationComponent} this object
     */
    setColor( value ){

        this.repr.setColor( value );

        return this;

    }

    /**
     * @ignore
     * @return {undefined}
     */
    getCenter(){}

    /**
     * @ignore
     * @return {undefined}
     */
    getZoom(){}

    /**
     * @ignore
     * @return {undefined}
     */
    getBox(){}

}

export default RepresentationComponent;
<commit_msg>added toggleVisibility to repr component<commit_after>/**
 * @file Representation Component
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import Signal from "../../lib/signals.es6.js";

import { defaults } from "../utils.js";
import Component from "./component.js";

// add here to avoid cyclic import dependency
Component.prototype.__getRepresentationComponent = function( repr, p ){
    return new RepresentationComponent(
        this.stage, repr, p, this
    );
};

/**
 * Extends {@link ComponentSignals}
 *
 * @typedef {Object} RepresentationComponentSignals
 * @property {Signal<String>} parametersChanged - on parameters change
 */

/**
 * Component wrapping a {@link Representation} object
 */
class RepresentationComponent extends Component{

    /**
     * Create representation component
     * @param {Stage} stage - stage object the component belongs to
     * @param {Representation} repr - representation object to wrap
     * @param {RepresentationParameters} [params] - component parameters
     * @param {Component} [parent] - parent component
     */
    constructor( stage, repr, params, parent ){

        var p = params || {};
        p.name = defaults( p.name, repr.type );

        super( stage, p );

        /**
         * Events emitted by the component
         * @type {RepresentationComponentSignals}
         */
        this.signals = Object.assign( this.signals, {
            parametersChanged: new Signal()
        } );

        this.parent = parent;

        this.setRepresentation( repr );

    }

    /**
     * Component type
     * @type {String}
     */
    get type(){ return "representation"; }

    getType(){

        return this.repr.type;

    }

    setRepresentation( repr ){

        this.disposeRepresentation();
        this.repr = repr;
        // this.name = repr.type;
        this.stage.tasks.listen( this.repr.tasks );
        this.updateVisibility();

    }

    /**
     * @ignore
     * @alias RepresentationComponent#addRepresentation
     * @return {undefined}
     */
    addRepresentation(){}

    /**
     * @ignore
     * @alias RepresentationComponent#removeRepresentation
     * @return {undefined}
     */
    removeRepresentation(){}

    /**
     * @ignore
     * @alias RepresentationComponent#hasRepresentation
     * @return {undefined}
     */
    hasRepresentation(){}

    disposeRepresentation(){

        if( this.repr ){
            this.stage.tasks.unlisten( this.repr.tasks );
            this.repr.dispose();
        }

    }

    dispose(){

        if( this.parent && this.parent.hasRepresentation( this ) ){
            this.parent.removeRepresentation( this );
        }else{
            this.disposeRepresentation();
            this.signals.disposed.dispatch();
        }

    }

    /**
     * Set the visibility of the component, takes parent visibility into account
     * @param {Boolean} value - visibility flag
     * @return {RepresentationComponent} this object
     */
    setVisibility( value ){

        this.visible = value;
        this.updateVisibility();
        this.signals.visibilityChanged.dispatch( this.visible );

        return this;

    }

    getVisibility(){

        if( this.parent ){
            return this.parent.visible && this.visible;
        }else{
            return this.visible;
        }

    }

    /**
     * Toggle visibility of the component, takes parent visibility into account
     * @return {RepresentationComponent} this object
     */
    toggleVisibility(){

        return this.setVisibility( !this.visible );

    }

    updateVisibility(){

        this.repr.setVisibility( this.getVisibility() );

    }

    /**
     * Set selection
     * @param {Object} what - flags indicating what attributes to update
     * @param {Boolean} what.position - update position attribute
     * @param {Boolean} what.color - update color attribute
     * @param {Boolean} what.radius - update radius attribute
     * @return {RepresentationComponent} this object
     */
    update( what ){

        this.repr.update( what );

        return this;

    }

    build( params ){

        this.repr.build( params );

        return this;

    }

    /**
     * Set selection
     * @param {String} string - selection string
     * @return {RepresentationComponent} this object
     */
    setSelection( string ){

        this.repr.setSelection( string );

        return this;

    }

    /**
     * Set representation parameters
     * @param {RepresentationParameters} params - parameter object
     * @return {RepresentationComponent} this object
     */
    setParameters( params ){

        this.repr.setParameters( params );
        this.signals.parametersChanged.dispatch(
            this.repr.getParameters()
        );

        return this;

    }

    /**
     * Get representation parameters
     * @return {RepresentationParameters} parameter object
     */
    getParameters(){

        return this.repr.getParameters();

    }

    /**
     * Set color
     * @param {String|Color|Hex} value - color value
     * @return {RepresentationComponent} this object
     */
    setColor( value ){

        this.repr.setColor( value );

        return this;

    }

    /**
     * @ignore
     * @return {undefined}
     */
    getCenter(){}

    /**
     * @ignore
     * @return {undefined}
     */
    getZoom(){}

    /**
     * @ignore
     * @return {undefined}
     */
    getBox(){}

}

export default RepresentationComponent;
<|endoftext|>


<commit_before>import find from 'array-find';
import React, { Component, PropTypes } from 'react';
import Menu from 'material-ui/lib/menus/menu';
import MenuItem from 'material-ui/lib/menus/menu-item';
import RenderToLayer from 'material-ui/lib/render-to-layer';

const MENU_HEIGHT = 320;
const MENU_WIDTH = 280;

const RANDOM_MUI_PADDING = 8;
const SCROLLBAR_WIDTH = 7;

const positionInsideWindow = (position, expectedHeight) => {
  const constrained = { x: position.x, y: position.y };
  const h = Math.min(expectedHeight, MENU_HEIGHT);
  const w = MENU_WIDTH;
  if (position.y + h >= window.innerHeight) {
    // position at the bottom of the screen
    constrained.y = window.innerHeight - h;
  }
  if (position.x + w >= window.innerWidth) {
    // position to the left-hand side of the anchor, instead of the right-hand side
    constrained.x -= w;
  }
  return constrained;
};

export default class AddToPlaylistMenu extends Component {
  static propTypes = {
    onClose: PropTypes.func.isRequired,
    onSelect: PropTypes.func.isRequired,
    playlists: PropTypes.arrayOf(PropTypes.object),
    position: PropTypes.shape({
      x: PropTypes.number,
      y: PropTypes.number
    })
  };

  onSelect(e, item) {
    const playlistID = item.props.value;
    this.props.onClose();
    this.props.onSelect(
      find(this.props.playlists, pl => pl._id === playlistID)
    );
  }

  renderLayer() {
    const { playlists, position } = this.props;
    const fixedPosition = positionInsideWindow(position, playlists.length * 48);
    return (

        <Menu
          style={{ textAlign: 'left', zIndex: 30 }}
          maxHeight={MENU_HEIGHT}
          width={MENU_WIDTH}
          autoWidth={false}
          onItemTouchTap={::this.onSelect}
        >
          {playlists.map(playlist => (
            <MenuItem
              key={playlist._id}
              value={playlist._id}
              primaryText={playlist.name}
              secondaryText={`${playlist.size || 0}`}
              checked={!!playlist.active}
            />
          ))}
        </Menu>

    );
  }

  render() {
    const { onClose } = this.props;
    return (
      <RenderToLayer
        open
        componentClickAway={onClose}
        render={::this.renderLayer}
      />
    );
  }
}
<commit_msg>Added a new playlist button for the playlist menu<commit_after>import find from 'array-find';
import React, { Component, PropTypes } from 'react';
import Menu from 'material-ui/lib/menus/menu';
import MenuItem from 'material-ui/lib/menus/menu-item';
import RenderToLayer from 'material-ui/lib/render-to-layer';

const MENU_HEIGHT = 320;
const MENU_WIDTH = 280;

const RANDOM_MUI_PADDING = 8;
const SCROLLBAR_WIDTH = 7;

const positionInsideWindow = (position, expectedHeight) => {
  const constrained = { x: position.x, y: position.y };
  const h = Math.min(expectedHeight, MENU_HEIGHT);
  const w = MENU_WIDTH;
  if (position.y + h >= window.innerHeight) {
    // position at the bottom of the screen
    constrained.y = window.innerHeight - h;
  }
  if (position.x + w >= window.innerWidth) {
    // position to the left-hand side of the anchor, instead of the right-hand side
    constrained.x -= w;
  }
  return constrained;
};

export default class AddToPlaylistMenu extends Component {
  static propTypes = {
    onClose: PropTypes.func.isRequired,
    onSelect: PropTypes.func.isRequired,
    playlists: PropTypes.arrayOf(PropTypes.object),
    position: PropTypes.shape({
      x: PropTypes.number,
      y: PropTypes.number
    })
  };

  onSelect(e, item) {
    const playlistID = item.props.value;
    this.props.onClose();
    this.props.onSelect(
      find(this.props.playlists, pl => pl._id === playlistID)
    );
  }

  renderLayer() {
    const { playlists, position } = this.props;
    const fixedPosition = positionInsideWindow(position, playlists.length * 48);
    return (

        <Menu
          style={{ textAlign: 'left', zIndex: 30 }}
          maxHeight={MENU_HEIGHT}
          width={MENU_WIDTH}
          autoWidth={false}
          onItemTouchTap={::this.onSelect}
        >
		<MenuItem
            primaryText="New Playlist"
        />
          {playlists.map(playlist => (
            <MenuItem
              key={playlist._id}
              value={playlist._id}
              primaryText={playlist.name}
              secondaryText={`${playlist.size || 0}`}
              checked={!!playlist.active}
            />
          ))}
        </Menu>

    );
  }

  render() {
    const { onClose } = this.props;
    return (
      <RenderToLayer
        open
        componentClickAway={onClose}
        render={::this.renderLayer}
      />
    );
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Adobe Systems Incorporated. All Rights Reserved.
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define: false, $: false, CodeMirror: false */

/**
 * InlineEditorProviders contains providers for Brackets' various built-in code editors. "Provider"
 * essentially means "a function that you pass to EditorManager.registerInlineEditProvider()".
 */
define(function (require, exports, module) {
    'use strict';

    // Load dependent modules
    var CodeHintUtils       = require("CodeHintUtils"),
        CSSManager          = require("CSSManager"),
        EditorManager       = require("EditorManager"),
        ProjectManager      = require("ProjectManager");

    /**
     * When cursor is on an HTML tag name, class attribute, or id attribute, find associated
     * CSS rules and show (one/all of them) in an inline editor.
     *
     * @param {!CodeMirror} editor
     * @param {!{line:Number, ch:Number}} pos
     * @return {$.Promise} a promise that will be resolved with:
     *      {{content:DOMElement, height:Number, onAdded:function(inlineId:Number)}}
     *      or null if we're not going to provide anything.
     */
    function htmlToCSSProvider(editor, pos) {
        // Only provide a CSS editor when cursor is in HTML content
        if (editor.getOption("mode") !== "htmlmixed") {
            return null;
        }
        var htmlmixedState = editor.getTokenAt(pos).state;
        if (htmlmixedState.mode !== "html") {
            return null;
        }

        // Only provide CSS editor if the selection is an insertion point
        var selStart = editor.getCursor(false),
            selEnd = editor.getCursor(true);

        if (selStart.line !== selEnd.line || selStart.ch !== selEnd.ch) {
            return null;
        }

        var tagInfo = CodeHintUtils.getTagInfo(editor, pos),
            selectorName = "";

        if (tagInfo.position.tokenType === CodeHintUtils.TAG_NAME) {
            // Type selector
            selectorName = tagInfo.tagName;
        } else if (tagInfo.position.tokenType === CodeHintUtils.ATTR_VALUE) {
            if (tagInfo.attr.name === "class") {
                // Class selector. We only look for the class name
                // that includes the insertion point. For example, if
                // the attribute is:
                //   class="error-dialog modal hide"
                // and the insertion point is inside "modal", we want ".modal"
                var attributeValue = tagInfo.attr.value;
                var startIndex = attributeValue.substr(0, tagInfo.position.offset).lastIndexOf(" ");
                var endIndex = attributeValue.indexOf(" ", tagInfo.position.offset);
                selectorName = "." +
                    attributeValue.substring(
                        startIndex === -1 ? 0 : startIndex + 1,
                        endIndex === -1 ? attributeValue.length : endIndex
                    );

                // If the insertion point is surrounded by space, selectorName is "."
                // Check for that here
                if (selectorName === ".") {
                    selectorName = "";
                }
            } else if (tagInfo.attr.name === "id") {
                // ID selector
                selectorName = "#" + tagInfo.attr.value;
            }
        }

        if (selectorName === "") {
            return null;
        }

        var result = new $.Deferred();

        CSSManager.findMatchingRules(selectorName)
            .done(function (rules) {
                if (rules && rules.length() > 0) {
                    //var inlineInfo = EditorManager.createInlineEditorFromText();
                    var foo = 42;
                }
            })
            .fail(function () {
                console.log("Error in CSSManager.findMatchingRules()");
                result.reject();
            });

        /*
        // Load a project file at random
        var arbitraryFile = "todos.css";
        var fileEntry = new NativeFileSystem.FileEntry(ProjectManager.getProjectRoot().fullPath + arbitraryFile);
        FileUtils.readAsText(fileEntry)
            .done(function (text) {
                // var dummyRange = { startLine: 18, endLine: 22 };    // small rule
                var dummyRange = { startLine: 218, endLine: 255 };    // tall rule
                var inlineInfo = EditorManager.createInlineEditorFromText(editor, text, dummyRange, arbitraryFile);

                var inlineEditor = inlineInfo.editor;

                // For Sprint 4, editor is a read-only view
                inlineEditor.setOption("readOnly", true);

                result.resolve(inlineInfo);
            })
            .fail(function (fileError) {
                console.log("Error in dummy htmlToCSSProvider(): ", fileError);
            });
        */

        return result.promise();
    }

    /** Initialize: register listeners */
    function init() {
        EditorManager.registerInlineEditProvider(htmlToCSSProvider);
    }

    // Define public API
    exports.init = init;
});
<commit_msg>More CSSManager hookup.<commit_after>/*
 * Copyright 2012 Adobe Systems Incorporated. All Rights Reserved.
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define: false, $: false, CodeMirror: false */

/**
 * InlineEditorProviders contains providers for Brackets' various built-in code editors. "Provider"
 * essentially means "a function that you pass to EditorManager.registerInlineEditProvider()".
 */
define(function (require, exports, module) {
    'use strict';

    // Load dependent modules
    var CodeHintUtils       = require("CodeHintUtils"),
        CSSManager          = require("CSSManager"),
        EditorManager       = require("EditorManager"),
        FileUtils           = require("FileUtils"),
        ProjectManager      = require("ProjectManager");

    /**
     * When cursor is on an HTML tag name, class attribute, or id attribute, find associated
     * CSS rules and show (one/all of them) in an inline editor.
     *
     * @param {!CodeMirror} editor
     * @param {!{line:Number, ch:Number}} pos
     * @return {$.Promise} a promise that will be resolved with:
     *      {{content:DOMElement, height:Number, onAdded:function(inlineId:Number)}}
     *      or null if we're not going to provide anything.
     */
    function htmlToCSSProvider(editor, pos) {
        // Only provide a CSS editor when cursor is in HTML content
        if (editor.getOption("mode") !== "htmlmixed") {
            return null;
        }
        var htmlmixedState = editor.getTokenAt(pos).state;
        if (htmlmixedState.mode !== "html") {
            return null;
        }

        // Only provide CSS editor if the selection is an insertion point
        var selStart = editor.getCursor(false),
            selEnd = editor.getCursor(true);

        if (selStart.line !== selEnd.line || selStart.ch !== selEnd.ch) {
            return null;
        }

        var tagInfo = CodeHintUtils.getTagInfo(editor, pos),
            selectorName = "";

        if (tagInfo.position.tokenType === CodeHintUtils.TAG_NAME) {
            // Type selector
            selectorName = tagInfo.tagName;
        } else if (tagInfo.position.tokenType === CodeHintUtils.ATTR_VALUE) {
            if (tagInfo.attr.name === "class") {
                // Class selector. We only look for the class name
                // that includes the insertion point. For example, if
                // the attribute is:
                //   class="error-dialog modal hide"
                // and the insertion point is inside "modal", we want ".modal"
                var attributeValue = tagInfo.attr.value;
                var startIndex = attributeValue.substr(0, tagInfo.position.offset).lastIndexOf(" ");
                var endIndex = attributeValue.indexOf(" ", tagInfo.position.offset);
                selectorName = "." +
                    attributeValue.substring(
                        startIndex === -1 ? 0 : startIndex + 1,
                        endIndex === -1 ? attributeValue.length : endIndex
                    );

                // If the insertion point is surrounded by space, selectorName is "."
                // Check for that here
                if (selectorName === ".") {
                    selectorName = "";
                }
            } else if (tagInfo.attr.name === "id") {
                // ID selector
                selectorName = "#" + tagInfo.attr.value;
            }
        }

        if (selectorName === "") {
            return null;
        }

        var result = new $.Deferred();

        CSSManager.findMatchingRules(selectorName)
            .done(function (rules) {
                if (rules && rules.length > 0) {
                    var rule = rules[0];  // For Sprint 4 we use the first match only

                    FileUtils.readAsText(rule.source)
                        .done(function (text) {
                            var range = { startLine: rule.lineStart, endLine: rule.lineEnd - 1 }; // rule.lineEnd is exclusive, range.endLine is inclusive
                            var inlineInfo = EditorManager.createInlineEditorFromText(editor, text, range, rule.source.fullPath);

                            var inlineEditor = inlineInfo.editor;

                            // For Sprint 4, editor is a read-only view
                            inlineEditor.setOption("readOnly", true);

                            result.resolve(inlineInfo);
                        })
                        .fail(function (fileError) {
                            console.log("Error in dummy htmlToCSSProvider(): ", fileError);
                            result.reject();
                        });
                } else {
                    result.reject();
                }
            })
            .fail(function () {
                console.log("Error in CSSManager.findMatchingRules()");
                result.reject();
            });

        /*
        // Load a project file at random
        var arbitraryFile = "todos.css";
        var fileEntry = new NativeFileSystem.FileEntry(ProjectManager.getProjectRoot().fullPath + arbitraryFile);
        FileUtils.readAsText(fileEntry)
            .done(function (text) {
                // var dummyRange = { startLine: 18, endLine: 22 };    // small rule
                var dummyRange = { startLine: 218, endLine: 255 };    // tall rule
                var inlineInfo = EditorManager.createInlineEditorFromText(editor, text, dummyRange, arbitraryFile);

                var inlineEditor = inlineInfo.editor;

                // For Sprint 4, editor is a read-only view
                inlineEditor.setOption("readOnly", true);

                result.resolve(inlineInfo);
            })
            .fail(function (fileError) {
                console.log("Error in dummy htmlToCSSProvider(): ", fileError);
            });
        */

        return result.promise();
    }

    /** Initialize: register listeners */
    function init() {
        EditorManager.registerInlineEditProvider(htmlToCSSProvider);
    }

    // Define public API
    exports.init = init;
});
<|endoftext|>


<commit_before>// Components
import VBtn from '../VBtn'
import VCard from '../VCard'
import VIcon from '../VIcon'
import VDatePickerTitle from './VDatePickerTitle'
import VDatePickerHeader from './VDatePickerHeader'
import VDatePickerDateTable from './VDatePickerDateTable'
import VDatePickerMonthTable from './VDatePickerMonthTable'
import VDatePickerYears from './VDatePickerYears'

// Mixins
import Picker from '../../mixins/picker'

// Utils
import { pad, createNativeLocaleFormatter } from './util'
import isDateAllowed from './util/isDateAllowed'

export default {
  name: 'v-date-picker',

  components: {
    VBtn,
    VCard,
    VIcon,
    VDatePickerTitle,
    VDatePickerHeader,
    VDatePickerDateTable,
    VDatePickerMonthTable,
    VDatePickerYears
  },

  mixins: [Picker],

  data () {
    const now = new Date()
    return {
      activePicker: this.type.toUpperCase(),
      defaultColor: 'accent',
      isReversing: false,
      now,
      // tableDate is a string in 'YYYY' / 'YYYY-M' format (leading zero for month is not required)
      tableDate: (() => {
        if (this.pickerDate) {
          return this.pickerDate
        }

        const date = this.value || `${now.getFullYear()}-${now.getMonth() + 1}`
        const type = this.type === 'date' ? 'month' : 'year'
        return this.sanitizeDateString(date, type)
      })()
    }
  },

  props: {
    allowedDates: Function,
    appendIcon: {
      type: String,
      default: 'chevron_right'
    },
    // Function formatting the day in date picker table
    dayFormat: {
      type: Function,
      default: null
    },
    events: {
      type: [Array, Object, Function],
      default: () => null
    },
    eventColor: {
      type: [String, Function, Object],
      default: 'warning'
    },
    firstDayOfWeek: {
      type: [String, Number],
      default: 0
    },
    // Function formatting the tableDate in the day/month table header
    headerDateFormat: {
      type: Function,
      default: null
    },
    locale: {
      type: String,
      default: 'en-us'
    },
    max: String,
    min: String,
    // Function formatting month in the months table
    monthFormat: {
      type: Function,
      default: null
    },
    pickerDate: String,
    prependIcon: {
      type: String,
      default: 'chevron_left'
    },
    readonly: Boolean,
    scrollable: Boolean,
    showCurrent: {
      type: [Boolean, String],
      default: true
    },
    // Function formatting currently selected date in the picker title
    titleDateFormat: {
      type: Function,
      default: null
    },
    type: {
      type: String,
      default: 'date',
      validator: type => ['date', 'month'/*, 'year'*/].includes(type)
    },
    value: String,
    // Function formatting the year in table header and pickup title
    yearFormat: {
      type: Function,
      default: null
    },
    yearIcon: String
  },

  computed: {
    current () {
      if (this.showCurrent === true) {
        return this.sanitizeDateString(`${this.now.getFullYear()}-${this.now.getMonth() + 1}-${this.now.getDate()}`, this.type)
      }

      return this.showCurrent || null
    },
    firstAllowedDate () {
      const year = this.now.getFullYear()
      const month = this.now.getMonth()

      if (this.allowedDates) {
        for (let date = 1; date <= 31; date++) {
          const dateString = `${year}-${month + 1}-${date}`
          if (isNaN(new Date(dateString).getDate())) break

          const sanitizedDateString = this.sanitizeDateString(dateString, 'date')
          if (this.isDateAllowed(sanitizedDateString)) {
            return sanitizedDateString
          }
        }
      }

      return this.sanitizeDateString(`${year}-${month + 1}-${this.now.getDate()}`, 'date')
    },
    firstAllowedMonth () {
      const year = this.now.getFullYear()

      if (this.allowedDates) {
        for (let month = 0; month < 12; month++) {
          const dateString = `${year}-${pad(month + 1)}`
          if (this.isDateAllowed(dateString)) {
            return dateString
          }
        }
      }

      return `${year}-${pad(this.now.getMonth() + 1)}`
    },
    // inputDate MUST be a string in ISO 8601 format (including leading zero for month/day)
    // YYYY-MM for month picker
    // YYYY-MM-DD for date picker
    inputDate: {
      get () {
        if (this.value) {
          return this.sanitizeDateString(this.value, this.type)
        }

        return this.type === 'month' ? this.firstAllowedMonth : this.firstAllowedDate
      },
      set (value) {
        const date = value ? this.sanitizeDateString(value, this.type) : null
        this.$emit('input', date)
      }
    },
    day () {
      return this.inputDate.split('-')[2] * 1
    },
    month () {
      return this.inputDate.split('-')[1] - 1
    },
    year () {
      return this.inputDate.split('-')[0] * 1
    },
    tableMonth () {
      return (this.pickerDate || this.tableDate).split('-')[1] - 1
    },
    tableYear () {
      return (this.pickerDate || this.tableDate).split('-')[0] * 1
    },
    minMonth () {
      return this.min ? this.sanitizeDateString(this.min, 'month') : null
    },
    maxMonth () {
      return this.max ? this.sanitizeDateString(this.max, 'month') : null
    },
    minYear () {
      return this.min ? this.sanitizeDateString(this.min, 'year') : null
    },
    maxYear () {
      return this.max ? this.sanitizeDateString(this.max, 'year') : null
    },
    formatters () {
      return {
        year: this.yearFormat || createNativeLocaleFormatter(this.locale, { year: 'numeric', timeZone: 'UTC' }, { length: 4 }),
        titleDate: this.titleDateFormat || this.defaultTitleDateFormatter
      }
    },
    defaultTitleDateFormatter () {
      const titleFormats = {
        year: { year: 'numeric', timeZone: 'UTC' },
        month: { month: 'long', timeZone: 'UTC' },
        date: { weekday: 'short', month: 'short', day: 'numeric', timeZone: 'UTC' }
      }

      const titleDateFormatter = createNativeLocaleFormatter(this.locale, titleFormats[this.type], {
        start: 0,
        length: { date: 10, month: 7, year: 4 }[this.type]
      })

      const landscapeFormatter = date => titleDateFormatter(date)
        .replace(/([^\d\s])([\d])/g, (match, nonDigit, digit) => `${nonDigit} ${digit}`)
        .replace(', ', ',
')

      return this.landscape ? landscapeFormatter : titleDateFormatter
    }
  },

  watch: {
    tableDate (val, prev) {
      // Make a ISO 8601 strings from val and prev for comparision, otherwise it will incorrectly
      // compare for example '2000-9' and '2000-10'
      const sanitizeType = this.type === 'month' ? 'year' : 'month'
      this.isReversing = this.sanitizeDateString(val, sanitizeType) < this.sanitizeDateString(prev, sanitizeType)
      this.$emit('update:pickerDate', val)
    },
    pickerDate (val) {
      if (val) {
        this.tableDate = val
      } else if (this.value && this.type === 'date') {
        this.tableDate = this.sanitizeDateString(val, 'month')
      } else if (this.value && this.type === 'month') {
        this.tableDate = this.sanitizeDateString(val, 'year')
      }
    },
    value () {
      if (this.value && !this.pickerDate) {
        this.tableDate = this.type === 'month' ? `${this.year}` : `${this.year}-${pad(this.month + 1)}`
      }
    },
    type (type) {
      this.activePicker = type.toUpperCase()

      if (this.value) {
        const date = this.sanitizeDateString(this.value, type)
        this.$emit('input', this.isDateAllowed(date) ? date : null)
      }
    }
  },

  methods: {
    isDateAllowed (value) {
      return isDateAllowed(value, this.min, this.max, this.allowedDates)
    },
    yearClick (value) {
      if (this.type === 'month') {
        const date = `${value}-${pad(this.month + 1)}`
        if (this.isDateAllowed(date)) this.inputDate = date
        this.tableDate = `${value}`
      } else {
        const date = `${value}-${pad(this.tableMonth + 1)}-${pad(this.day)}`
        if (this.isDateAllowed(date)) this.inputDate = date
        this.tableDate = `${value}-${pad(this.tableMonth + 1)}`
      }
      this.activePicker = 'MONTH'
    },
    monthClick (value) {
      // Updates inputDate setting 'YYYY-MM' or 'YYYY-MM-DD' format, depending on the picker type
      if (this.type === 'date') {
        const date = `${value}-${pad(this.day)}`
        if (this.isDateAllowed(date)) this.inputDate = date
        this.tableDate = value
        this.activePicker = 'DATE'
      } else {
        this.inputDate = value
        this.$emit('change', value)
      }
    },
    dateClick (value) {
      this.inputDate = value
      this.$emit('change', value)
    },
    genPickerTitle () {
      return this.$createElement('v-date-picker-title', {
        props: {
          date: this.formatters.titleDate(this.inputDate),
          selectingYear: this.activePicker === 'YEAR',
          year: this.formatters.year(`${this.year}`),
          yearIcon: this.yearIcon
        },
        slot: 'title',
        style: this.readonly ? {
          'pointer-events': 'none'
        } : undefined,
        on: {
          'update:selectingYear': value => this.activePicker = value ? 'YEAR' : this.type.toUpperCase()
        }
      })
    },
    genTableHeader () {
      return this.$createElement('v-date-picker-header', {
        props: {
          appendIcon: this.appendIcon,
          color: this.color,
          disabled: this.readonly,
          format: this.headerDateFormat,
          locale: this.locale,
          min: this.activePicker === 'DATE' ? this.minMonth : this.minYear,
          max: this.activePicker === 'DATE' ? this.maxMonth : this.maxYear,
          prependIcon: this.prependIcon,
          value: this.activePicker === 'DATE' ? `${this.tableYear}-${pad(this.tableMonth + 1)}` : `${this.tableYear}`
        },
        on: {
          toggle: () => this.activePicker = (this.activePicker === 'DATE' ? 'MONTH' : 'YEAR'),
          input: value => this.tableDate = value
        }
      })
    },
    genDateTable () {
      return this.$createElement('v-date-picker-date-table', {
        props: {
          allowedDates: this.allowedDates,
          color: this.color,
          current: this.current,
          disabled: this.readonly,
          events: this.events,
          eventColor: this.eventColor,
          firstDayOfWeek: this.firstDayOfWeek,
          format: this.dayFormat,
          locale: this.locale,
          min: this.min,
          max: this.max,
          tableDate: `${this.tableYear}-${pad(this.tableMonth + 1)}`,
          scrollable: this.scrollable,
          value: this.value
        },
        ref: 'table',
        on: {
          input: this.dateClick,
          tableDate: value => this.tableDate = value
        }
      })
    },
    genMonthTable () {
      return this.$createElement('v-date-picker-month-table', {
        props: {
          allowedDates: this.type === 'month' ? this.allowedDates : null,
          color: this.color,
          current: this.sanitizeDateString(this.current, 'month'),
          disabled: this.readonly,
          format: this.monthFormat,
          locale: this.locale,
          min: this.minMonth,
          max: this.maxMonth,
          scrollable: this.scrollable,
          value: (!this.value || this.type === 'month') ? this.value : this.value.substr(0, 7),
          tableDate: `${this.tableYear}`
        },
        ref: 'table',
        on: {
          input: this.monthClick,
          tableDate: value => this.tableDate = value
        }
      })
    },
    genYears () {
      return this.$createElement('v-date-picker-years', {
        props: {
          color: this.color,
          format: this.yearFormat,
          locale: this.locale,
          min: this.minYear,
          max: this.maxYear,
          value: `${this.tableYear}`
        },
        on: {
          input: this.yearClick
        }
      })
    },
    genPickerBody () {
      const children = this.activePicker === 'YEAR' ? [
        this.genYears()
      ] : [
        this.genTableHeader(),
        this.activePicker === 'DATE' ? this.genDateTable() : this.genMonthTable()
      ]

      return this.$createElement('div', {
        key: this.activePicker,
        style: this.readonly ? {
          'pointer-events': 'none'
        } : undefined
      }, children)
    },
    // Adds leading zero to month/day if necessary, returns 'YYYY' if type = 'year',
    // 'YYYY-MM' if 'month' and 'YYYY-MM-DD' if 'date'
    sanitizeDateString (dateString, type) {
      const [year, month = 1, date = 1] = dateString.split('-')
      return `${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type])
    }
  },

  render (h) {
    return this.genPicker('picker--date')
  }
}
<commit_msg>fix (v-date-picker): showCurrent prop not working in month picker<commit_after>// Components
import VBtn from '../VBtn'
import VCard from '../VCard'
import VIcon from '../VIcon'
import VDatePickerTitle from './VDatePickerTitle'
import VDatePickerHeader from './VDatePickerHeader'
import VDatePickerDateTable from './VDatePickerDateTable'
import VDatePickerMonthTable from './VDatePickerMonthTable'
import VDatePickerYears from './VDatePickerYears'

// Mixins
import Picker from '../../mixins/picker'

// Utils
import { pad, createNativeLocaleFormatter } from './util'
import isDateAllowed from './util/isDateAllowed'

export default {
  name: 'v-date-picker',

  components: {
    VBtn,
    VCard,
    VIcon,
    VDatePickerTitle,
    VDatePickerHeader,
    VDatePickerDateTable,
    VDatePickerMonthTable,
    VDatePickerYears
  },

  mixins: [Picker],

  data () {
    const now = new Date()
    return {
      activePicker: this.type.toUpperCase(),
      defaultColor: 'accent',
      isReversing: false,
      now,
      // tableDate is a string in 'YYYY' / 'YYYY-M' format (leading zero for month is not required)
      tableDate: (() => {
        if (this.pickerDate) {
          return this.pickerDate
        }

        const date = this.value || `${now.getFullYear()}-${now.getMonth() + 1}`
        const type = this.type === 'date' ? 'month' : 'year'
        return this.sanitizeDateString(date, type)
      })()
    }
  },

  props: {
    allowedDates: Function,
    appendIcon: {
      type: String,
      default: 'chevron_right'
    },
    // Function formatting the day in date picker table
    dayFormat: {
      type: Function,
      default: null
    },
    events: {
      type: [Array, Object, Function],
      default: () => null
    },
    eventColor: {
      type: [String, Function, Object],
      default: 'warning'
    },
    firstDayOfWeek: {
      type: [String, Number],
      default: 0
    },
    // Function formatting the tableDate in the day/month table header
    headerDateFormat: {
      type: Function,
      default: null
    },
    locale: {
      type: String,
      default: 'en-us'
    },
    max: String,
    min: String,
    // Function formatting month in the months table
    monthFormat: {
      type: Function,
      default: null
    },
    pickerDate: String,
    prependIcon: {
      type: String,
      default: 'chevron_left'
    },
    readonly: Boolean,
    scrollable: Boolean,
    showCurrent: {
      type: [Boolean, String],
      default: true
    },
    // Function formatting currently selected date in the picker title
    titleDateFormat: {
      type: Function,
      default: null
    },
    type: {
      type: String,
      default: 'date',
      validator: type => ['date', 'month'/*, 'year'*/].includes(type)
    },
    value: String,
    // Function formatting the year in table header and pickup title
    yearFormat: {
      type: Function,
      default: null
    },
    yearIcon: String
  },

  computed: {
    current () {
      if (this.showCurrent === true) {
        return this.sanitizeDateString(`${this.now.getFullYear()}-${this.now.getMonth() + 1}-${this.now.getDate()}`, this.type)
      }

      return this.showCurrent || null
    },
    firstAllowedDate () {
      const year = this.now.getFullYear()
      const month = this.now.getMonth()

      if (this.allowedDates) {
        for (let date = 1; date <= 31; date++) {
          const dateString = `${year}-${month + 1}-${date}`
          if (isNaN(new Date(dateString).getDate())) break

          const sanitizedDateString = this.sanitizeDateString(dateString, 'date')
          if (this.isDateAllowed(sanitizedDateString)) {
            return sanitizedDateString
          }
        }
      }

      return this.sanitizeDateString(`${year}-${month + 1}-${this.now.getDate()}`, 'date')
    },
    firstAllowedMonth () {
      const year = this.now.getFullYear()

      if (this.allowedDates) {
        for (let month = 0; month < 12; month++) {
          const dateString = `${year}-${pad(month + 1)}`
          if (this.isDateAllowed(dateString)) {
            return dateString
          }
        }
      }

      return `${year}-${pad(this.now.getMonth() + 1)}`
    },
    // inputDate MUST be a string in ISO 8601 format (including leading zero for month/day)
    // YYYY-MM for month picker
    // YYYY-MM-DD for date picker
    inputDate: {
      get () {
        if (this.value) {
          return this.sanitizeDateString(this.value, this.type)
        }

        return this.type === 'month' ? this.firstAllowedMonth : this.firstAllowedDate
      },
      set (value) {
        const date = value ? this.sanitizeDateString(value, this.type) : null
        this.$emit('input', date)
      }
    },
    day () {
      return this.inputDate.split('-')[2] * 1
    },
    month () {
      return this.inputDate.split('-')[1] - 1
    },
    year () {
      return this.inputDate.split('-')[0] * 1
    },
    tableMonth () {
      return (this.pickerDate || this.tableDate).split('-')[1] - 1
    },
    tableYear () {
      return (this.pickerDate || this.tableDate).split('-')[0] * 1
    },
    minMonth () {
      return this.min ? this.sanitizeDateString(this.min, 'month') : null
    },
    maxMonth () {
      return this.max ? this.sanitizeDateString(this.max, 'month') : null
    },
    minYear () {
      return this.min ? this.sanitizeDateString(this.min, 'year') : null
    },
    maxYear () {
      return this.max ? this.sanitizeDateString(this.max, 'year') : null
    },
    formatters () {
      return {
        year: this.yearFormat || createNativeLocaleFormatter(this.locale, { year: 'numeric', timeZone: 'UTC' }, { length: 4 }),
        titleDate: this.titleDateFormat || this.defaultTitleDateFormatter
      }
    },
    defaultTitleDateFormatter () {
      const titleFormats = {
        year: { year: 'numeric', timeZone: 'UTC' },
        month: { month: 'long', timeZone: 'UTC' },
        date: { weekday: 'short', month: 'short', day: 'numeric', timeZone: 'UTC' }
      }

      const titleDateFormatter = createNativeLocaleFormatter(this.locale, titleFormats[this.type], {
        start: 0,
        length: { date: 10, month: 7, year: 4 }[this.type]
      })

      const landscapeFormatter = date => titleDateFormatter(date)
        .replace(/([^\d\s])([\d])/g, (match, nonDigit, digit) => `${nonDigit} ${digit}`)
        .replace(', ', ',
')

      return this.landscape ? landscapeFormatter : titleDateFormatter
    }
  },

  watch: {
    tableDate (val, prev) {
      // Make a ISO 8601 strings from val and prev for comparision, otherwise it will incorrectly
      // compare for example '2000-9' and '2000-10'
      const sanitizeType = this.type === 'month' ? 'year' : 'month'
      this.isReversing = this.sanitizeDateString(val, sanitizeType) < this.sanitizeDateString(prev, sanitizeType)
      this.$emit('update:pickerDate', val)
    },
    pickerDate (val) {
      if (val) {
        this.tableDate = val
      } else if (this.value && this.type === 'date') {
        this.tableDate = this.sanitizeDateString(val, 'month')
      } else if (this.value && this.type === 'month') {
        this.tableDate = this.sanitizeDateString(val, 'year')
      }
    },
    value () {
      if (this.value && !this.pickerDate) {
        this.tableDate = this.type === 'month' ? `${this.year}` : `${this.year}-${pad(this.month + 1)}`
      }
    },
    type (type) {
      this.activePicker = type.toUpperCase()

      if (this.value) {
        const date = this.sanitizeDateString(this.value, type)
        this.$emit('input', this.isDateAllowed(date) ? date : null)
      }
    }
  },

  methods: {
    isDateAllowed (value) {
      return isDateAllowed(value, this.min, this.max, this.allowedDates)
    },
    yearClick (value) {
      if (this.type === 'month') {
        const date = `${value}-${pad(this.month + 1)}`
        if (this.isDateAllowed(date)) this.inputDate = date
        this.tableDate = `${value}`
      } else {
        const date = `${value}-${pad(this.tableMonth + 1)}-${pad(this.day)}`
        if (this.isDateAllowed(date)) this.inputDate = date
        this.tableDate = `${value}-${pad(this.tableMonth + 1)}`
      }
      this.activePicker = 'MONTH'
    },
    monthClick (value) {
      // Updates inputDate setting 'YYYY-MM' or 'YYYY-MM-DD' format, depending on the picker type
      if (this.type === 'date') {
        const date = `${value}-${pad(this.day)}`
        if (this.isDateAllowed(date)) this.inputDate = date
        this.tableDate = value
        this.activePicker = 'DATE'
      } else {
        this.inputDate = value
        this.$emit('change', value)
      }
    },
    dateClick (value) {
      this.inputDate = value
      this.$emit('change', value)
    },
    genPickerTitle () {
      return this.$createElement('v-date-picker-title', {
        props: {
          date: this.formatters.titleDate(this.inputDate),
          selectingYear: this.activePicker === 'YEAR',
          year: this.formatters.year(`${this.year}`),
          yearIcon: this.yearIcon
        },
        slot: 'title',
        style: this.readonly ? {
          'pointer-events': 'none'
        } : undefined,
        on: {
          'update:selectingYear': value => this.activePicker = value ? 'YEAR' : this.type.toUpperCase()
        }
      })
    },
    genTableHeader () {
      return this.$createElement('v-date-picker-header', {
        props: {
          appendIcon: this.appendIcon,
          color: this.color,
          disabled: this.readonly,
          format: this.headerDateFormat,
          locale: this.locale,
          min: this.activePicker === 'DATE' ? this.minMonth : this.minYear,
          max: this.activePicker === 'DATE' ? this.maxMonth : this.maxYear,
          prependIcon: this.prependIcon,
          value: this.activePicker === 'DATE' ? `${this.tableYear}-${pad(this.tableMonth + 1)}` : `${this.tableYear}`
        },
        on: {
          toggle: () => this.activePicker = (this.activePicker === 'DATE' ? 'MONTH' : 'YEAR'),
          input: value => this.tableDate = value
        }
      })
    },
    genDateTable () {
      return this.$createElement('v-date-picker-date-table', {
        props: {
          allowedDates: this.allowedDates,
          color: this.color,
          current: this.current,
          disabled: this.readonly,
          events: this.events,
          eventColor: this.eventColor,
          firstDayOfWeek: this.firstDayOfWeek,
          format: this.dayFormat,
          locale: this.locale,
          min: this.min,
          max: this.max,
          tableDate: `${this.tableYear}-${pad(this.tableMonth + 1)}`,
          scrollable: this.scrollable,
          value: this.value
        },
        ref: 'table',
        on: {
          input: this.dateClick,
          tableDate: value => this.tableDate = value
        }
      })
    },
    genMonthTable () {
      return this.$createElement('v-date-picker-month-table', {
        props: {
          allowedDates: this.type === 'month' ? this.allowedDates : null,
          color: this.color,
          current: this.current ? this.sanitizeDateString(this.current, 'month') : null,
          disabled: this.readonly,
          format: this.monthFormat,
          locale: this.locale,
          min: this.minMonth,
          max: this.maxMonth,
          scrollable: this.scrollable,
          value: (!this.value || this.type === 'month') ? this.value : this.value.substr(0, 7),
          tableDate: `${this.tableYear}`
        },
        ref: 'table',
        on: {
          input: this.monthClick,
          tableDate: value => this.tableDate = value
        }
      })
    },
    genYears () {
      return this.$createElement('v-date-picker-years', {
        props: {
          color: this.color,
          format: this.yearFormat,
          locale: this.locale,
          min: this.minYear,
          max: this.maxYear,
          value: `${this.tableYear}`
        },
        on: {
          input: this.yearClick
        }
      })
    },
    genPickerBody () {
      const children = this.activePicker === 'YEAR' ? [
        this.genYears()
      ] : [
        this.genTableHeader(),
        this.activePicker === 'DATE' ? this.genDateTable() : this.genMonthTable()
      ]

      return this.$createElement('div', {
        key: this.activePicker,
        style: this.readonly ? {
          'pointer-events': 'none'
        } : undefined
      }, children)
    },
    // Adds leading zero to month/day if necessary, returns 'YYYY' if type = 'year',
    // 'YYYY-MM' if 'month' and 'YYYY-MM-DD' if 'date'
    sanitizeDateString (dateString, type) {
      const [year, month = 1, date = 1] = dateString.split('-')
      return `${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type])
    }
  },

  render (h) {
    return this.genPicker('picker--date')
  }
}
<|endoftext|>


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

// import css from './styles.css';

const searchProvider = 'https://www.starcitygames.com/results?name=';

export default class ScgSearchHelper extends Component {
  constructor() {
    super();

    this.state = {
      searchText: '',
    };

    this.updateSearchText = this.updateSearchText.bind(this);
    this.submitSearch = this.submitSearch.bind(this);
  }

  updateSearchText(event) {
    this.setState({
      searchText: event.target.value,
    })
  }

  submitSearch(event) {
    event.preventDefault()

	  const initialText = this.state.searchText;

    let searchQuery = initialText.replace(/(\s)+/g, '+')
      .replace(/^\+/g, '')
      .replace(/\+$/g, '');

    const searchUrl = `${searchProvider}${searchQuery}`;
    window.open(searchUrl);
  }

  render() {
    return (

        <form
          id="search-form"
          onSubmit={this.submitSearch}
        >
          <input
            id="search-text"
            type="text"
            value={this.state.searchText}
            onChange={this.updateSearchText}
          />
          <button type="submit">
            Search Star City Games!
          </button>
        </form>

    )
  }
}
<commit_msg>Update index.js<commit_after>import React, { Component, PropTypes } from 'react';

import css from './styles.css';

const searchProvider = 'https://www.starcitygames.com/results?name=';

export default class ScgSearchHelper extends Component {
  constructor() {
    super();

    this.state = {
      searchText: '',
    };

    this.updateSearchText = this.updateSearchText.bind(this);
    this.submitSearch = this.submitSearch.bind(this);
  }

  updateSearchText(event) {
    this.setState({
      searchText: event.target.value,
    })
  }

  submitSearch(event) {
    event.preventDefault()

	  const initialText = this.state.searchText;

    let searchQuery = initialText.replace(/(\s)+/g, '+')
      .replace(/^\+/g, '')
      .replace(/\+$/g, '');

    const searchUrl = `${searchProvider}${searchQuery}`;
    window.open(searchUrl);
  }

  render() {
    return (

        <form
          className={css.searchForm}
          id="search-form"
          onSubmit={this.submitSearch}
        >
          <input
            id="search-text"
	    className={css.searchTextBox}
            type="text"
            value={this.state.searchText}
            onChange={this.updateSearchText}
          />
          <button type="submit">
            Search Star City Games!
          </button>
        </form>

    )
  }
}
<|endoftext|>


<commit_before>'use strict';

// @ngInject
var SearchController = function ($scope, $location, NpolarApiSecurity) {

  $scope.security = NpolarApiSecurity;
  $scope.q = $location.search().q;

  // Simplify facet array to facet object
  $scope.facet = function(farr) {
    let keys = Object.keys(farr);
    let key = keys[0];
    let facet = farr[key].map(f => {

      f.facet = key;
      return f;

    });

    return facet;

  };

  // Get search bit of API URI
  $scope.facetHref = function(uri) {
    return "?"+ uri.split("?")[1];
  };

  $scope.facetKey = function(facet) {
    return Object.keys(facet)[0];
  };

  $scope.entryHref = function(id) {
    if ((/[.]/).test(id)) {
      id += ".json";
    }
    // Get relative path of entry by removing hostname + appname from request URI
    let segments = $location.absUrl().split("//")[1].split("?")[0].split("/").filter(s => { return s !== "";}).slice(2);

    if (segments.length === 0) {
      // For apps at /something, we just need to link to the id
      return id;
    } else {
      // For apps at /cat, but with show/edit at /cat/lynx we link to `lynx/${id}`
      return segments.join("/")+'/'+id;
    }
  };

  $scope.title = (entry) => {
    if (entry.titles) {
      entry.titles[0].title;
    }
    return entry.title || entry.name || entry.code || entry.id;
  };

  // Get facet object { term, uri, facet, count } from filter object like { "filter-term": "X" }
  $scope.facetObjectFromFilter = function(filter) {
    //let term = filter[ Object.keys(filter)[0] ];
    let key = $scope.facetKey(filter).replace(/^filter\-/, "");

    //
    let facets = $scope.feed.facets.find(facet => {
      //console.log(facet, term);
      return (facet.hasOwnProperty(key) && facet.hasOwnProperty(key) === "Tromsø");
    });

    //console.log(facet);
    return facets[key];

  };

};

module.exports = SearchController;
<commit_msg>Added missing return<commit_after>'use strict';

// @ngInject
var SearchController = function($scope, $location, NpolarApiSecurity) {

  $scope.security = NpolarApiSecurity;
  $scope.q = $location.search().q;

  // Simplify facet array to facet object
  $scope.facet = function(farr) {
    let keys = Object.keys(farr);
    let key = keys[0];
    let facet = farr[key].map(f => {

      f.facet = key;
      return f;

    });

    return facet;

  };

  // Get search bit of API URI
  $scope.facetHref = function(uri) {
    return "?" + uri.split("?")[1];
  };

  $scope.facetKey = function(facet) {
    return Object.keys(facet)[0];
  };

  $scope.entryHref = function(id) {
    if ((/[.]/).test(id)) {
      id += ".json";
    }
    // Get relative path of entry by removing hostname + appname from request URI
    let segments = $location.absUrl().split("//")[1].split("?")[0].split("/").filter(s => {
      return s !== "";
    }).slice(2);

    if (segments.length === 0) {
      // For apps at /something, we just need to link to the id
      return id;
    } else {
      // For apps at /cat, but with show/edit at /cat/lynx we link to `lynx/${id}`
      return segments.join("/") + '/' + id;
    }
  };

  $scope.title = (entry) => {
    if (entry.titles) {
      return entry.titles[0].title;
    }
    return entry.title || entry.name || entry.code || entry.id;
  };

  // Get facet object { term, uri, facet, count } from filter object like { "filter-term": "X" }
  $scope.facetObjectFromFilter = function(filter) {
    //let term = filter[ Object.keys(filter)[0] ];
    let key = $scope.facetKey(filter).replace(/^filter\-/, "");

    //
    let facets = $scope.feed.facets.find(facet => {
      //console.log(facet, term);
      return (facet.hasOwnProperty(key) && facet.hasOwnProperty(key) === "Tromsø");
    });

    //console.log(facet);
    return facets[key];

  };

};

module.exports = SearchController;
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { _t } from '../../../languageHandler';
import Widgets from '../../../utils/widgets';
import AppTile from '../elements/AppTile';
import ContextualMenu from '../../structures/ContextualMenu';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import sdk from '../../../index';
import SdkConfig from '../../../SdkConfig';
import ScalarAuthClient from '../../../ScalarAuthClient';
import dis from '../../../dispatcher';

export default class Stickerpack extends React.Component {
    constructor(props) {
        super(props);
        this.onShowStickersClick = this.onShowStickersClick.bind(this);
        this.onHideStickersClick = this.onHideStickersClick.bind(this);
        this.onFinished = this.onFinished.bind(this);
        this._launchManageIntegrations = this._launchManageIntegrations.bind(this);
        this._removeStickerpackWidgets = this._removeStickerpackWidgets.bind(this);
        this._onWidgetAction = this._onWidgetAction.bind(this);

        this.defaultStickersContent = (

{ _t("You don't currently have any stickerpacks enabled") }

{ _t("Click") }  { _t("here") } { _t("to add some!") }

        );
        this.popoverWidth = 300;
        this.popoverHeight = 300;

        this.state = {
            stickersContent: this.defaultStickersContent,
            showStickers: false,
        };
    }

    _removeStickerpackWidgets() {
        console.warn('Removing stickerpack widgets');
        Widgets.removeStickerpackWidgets();
        this._getStickerPickerWidget();
        this.onFinished();
    }

    componentDidMount() {
        this.scalarClient = null;
        if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) {
            this.scalarClient = new ScalarAuthClient();
            this.scalarClient.connect().then(() => {
                this.forceUpdate();
            }).catch((e) => {
                console.log("Failed to connect to integrations server");
                // TODO -- Handle Scalar errors
                //     this.setState({
                //         scalar_error: err,
                //     });
            });
        }
        this._getStickerPickerWidget();
        this.dispatcherRef = dis.register(this._onWidgetAction);
    }

    componentWillUnmount() {
        dis.unregister(this.dispatcherRef);
    }

    _onWidgetAction(payload) {
        if (payload.action === "user_widget_updated") {
            console.warn("user widget updated");
            this._getStickerPickerWidget();
            return;
        }
    }

    _getStickerPickerWidget() {
        // Stickers
        // TODO - Add support for stickerpacks from multiple app stores.
        // Render content from multiple stickerpack sources, each within their own iframe, within the stickerpack UI element.
        console.warn("Checking for sticker picker widgets");
        const stickerpackWidget = Widgets.getStickerpackWidgets()[0];
        let stickersContent;

        // Load stickerpack content
        if (stickerpackWidget && stickerpackWidget.content && stickerpackWidget.content.url) {
            // Set default name
            stickerpackWidget.content.name = stickerpackWidget.name || "Stickerpack";
            console.warn('Stickerpack widget', stickerpackWidget);
            this.widgetId = stickerpackWidget.id;

            stickersContent = (

                        <AppTile
                            id={stickerpackWidget.id}
                            url={stickerpackWidget.content.url}
                            name={stickerpackWidget.content.name}
                            room={this.props.room}
                            type={stickerpackWidget.content.type}
                            fullWidth={true}
                            userId={stickerpackWidget.sender || MatrixClientPeg.get().credentials.userId}
                            creatorUserId={MatrixClientPeg.get().credentials.userId}
                            waitForIframeLoad={true}
                            show={true}
                            showMenubar={true}
                            onEditClick={this._launchManageIntegrations}
                            onDeleteClick={this._removeStickerpackWidgets}
                            showTitle={false}
                            showMinimise={false}
                        />

            );
        } else {
            // Default content to show if stickerpack widget not added
            console.warn("No available sticker picker widgets");
            stickersContent = this.defaultStickersContent;
            this.forceUpdate();
        }
        this.setState({
            showStickers: false,
            stickersContent: stickersContent,
        });
    }

    /**
     * Show the sticker picker overlay
     * If no stickerpacks have been added, show a link to the integration manager add sticker packs page.
     * @param  {Event} e Event that triggered the function
     */
    onShowStickersClick(e) {
        this._getStickerPickerWidget();
        const GenericElementContextMenu = sdk.getComponent('context_menus.GenericElementContextMenu');
        const buttonRect = e.target.getBoundingClientRect();

        // The window X and Y offsets are to adjust position when zoomed in to page
        const x = buttonRect.right + window.pageXOffset - 37;
        const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
        // const self = this;
        this.stickersMenu = ContextualMenu.createMenu(GenericElementContextMenu, {
            chevronOffset: 10,
            chevronFace: 'bottom',
            left: x,
            top: y,
            menuWidth: this.popoverWidth,
            menuHeight: this.popoverHeight,
            element: this.state.stickersContent,
            onFinished: this.onFinished,
            menuPaddingTop: 0,
        });

        this.setState({showStickers: true});
    }

    /**
     * Trigger hiding of the sticker picker overlay
     * @param  {Event} ev Event that triggered the function call
     */
    onHideStickersClick(ev) {
        this.stickersMenu.close();
    }

    /**
     * The stickers picker was hidden
     */
    onFinished() {
        this.setState({showStickers: false});
        console.warn("finshed");
    }

    /**
     * Launch the integrations manager on the stickers integration page
     */
    _launchManageIntegrations() {
        const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
        const src = (this.scalarClient !== null && this.scalarClient.hasCredentials()) ?
                this.scalarClient.getScalarInterfaceUrlForRoom(
                    this.props.room,
                    'type_stickerpack',
                    this.widgetId,
                ) :
                null;
        Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, {
            src: src,
        }, "mx_IntegrationsManager");
        this.stickersMenu.close();
    }

    render() {
        const TintableSvg = sdk.getComponent("elements.TintableSvg");
        let stickersButton;
        if (this.state.showStickers) {
            // Show hide-stickers button
            stickersButton =

                    <TintableSvg src="img/icons-hide-stickers.svg" width="35" height="35" />

;
        } else {
            // Show show-stickers button
            stickersButton =

                    <TintableSvg src="img/icons-show-stickers.svg" width="35" height="35" />

;
        }
        return stickersButton;
    }
}
<commit_msg>Add error handling for failure to connect to integration manager.<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { _t } from '../../../languageHandler';
import Widgets from '../../../utils/widgets';
import AppTile from '../elements/AppTile';
import ContextualMenu from '../../structures/ContextualMenu';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import sdk from '../../../index';
import SdkConfig from '../../../SdkConfig';
import ScalarAuthClient from '../../../ScalarAuthClient';
import dis from '../../../dispatcher';

export default class Stickerpack extends React.Component {
    constructor(props) {
        super(props);
        this.onShowStickersClick = this.onShowStickersClick.bind(this);
        this.onHideStickersClick = this.onHideStickersClick.bind(this);
        this.onFinished = this.onFinished.bind(this);
        this._launchManageIntegrations = this._launchManageIntegrations.bind(this);
        this._removeStickerpackWidgets = this._removeStickerpackWidgets.bind(this);
        this._onWidgetAction = this._onWidgetAction.bind(this);

        this.defaultStickersContent = (

{ _t("You don't currently have any stickerpacks enabled") }

{ _t("Click") }  { _t("here") } { _t("to add some!") }

        );
        this.popoverWidth = 300;
        this.popoverHeight = 300;

        this.state = {
            stickersContent: this.defaultStickersContent,
            showStickers: false,
            imError: null,
        };
    }

    _removeStickerpackWidgets() {
        console.warn('Removing stickerpack widgets');
        Widgets.removeStickerpackWidgets();
        this._getStickerPickerWidget();
        this.stickersMenu.close();
    }

    componentDidMount() {
        this.scalarClient = null;
        if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) {
            this.scalarClient = new ScalarAuthClient();
            this.scalarClient.connect().then(() => {
                this.forceUpdate();
            }).catch((e) => {
                this._imError("Failed to connect to integrations server", e);
            });
        }

        if (!this.state.imError) {
            this._getStickerPickerWidget();
            this.dispatcherRef = dis.register(this._onWidgetAction);
        }
    }

    componentWillUnmount() {
        dis.unregister(this.dispatcherRef);
    }

    _imError(errorMsg, e) {
        console.error(errorMsg, e);
        const imErrorContent =

 { errorMsg }

;
        this.setState({
            showStickers: false,
            imError: errorMsg,
            stickersContent: imErrorContent,
        });
    }

    _onWidgetAction(payload) {
        if (payload.action === "user_widget_updated") {
            console.warn("user widget updated");
            this._getStickerPickerWidget();
            return;
        }
    }

    _getStickerPickerWidget() {
        // Stickers
        // TODO - Add support for stickerpacks from multiple app stores.
        // Render content from multiple stickerpack sources, each within their own iframe, within the stickerpack UI element.
        console.warn("Checking for sticker picker widgets");
        const stickerpackWidget = Widgets.getStickerpackWidgets()[0];
        let stickersContent;

        // Load stickerpack content
        if (stickerpackWidget && stickerpackWidget.content && stickerpackWidget.content.url) {
            // Set default name
            stickerpackWidget.content.name = stickerpackWidget.name || "Stickerpack";
            console.warn('Stickerpack widget', stickerpackWidget);
            this.widgetId = stickerpackWidget.id;

            stickersContent = (

                        <AppTile
                            id={stickerpackWidget.id}
                            url={stickerpackWidget.content.url}
                            name={stickerpackWidget.content.name}
                            room={this.props.room}
                            type={stickerpackWidget.content.type}
                            fullWidth={true}
                            userId={stickerpackWidget.sender || MatrixClientPeg.get().credentials.userId}
                            creatorUserId={MatrixClientPeg.get().credentials.userId}
                            waitForIframeLoad={true}
                            show={true}
                            showMenubar={true}
                            onEditClick={this._launchManageIntegrations}
                            onDeleteClick={this._removeStickerpackWidgets}
                            showTitle={false}
                            showMinimise={false}
                        />

            );
        } else {
            // Default content to show if stickerpack widget not added
            console.warn("No available sticker picker widgets");
            stickersContent = this.defaultStickersContent;
            this.forceUpdate();
        }
        this.setState({
            showStickers: false,
            stickersContent: stickersContent,
        });
    }

    /**
     * Show the sticker picker overlay
     * If no stickerpacks have been added, show a link to the integration manager add sticker packs page.
     * @param  {Event} e Event that triggered the function
     */
    onShowStickersClick(e) {
        this._getStickerPickerWidget();
        const GenericElementContextMenu = sdk.getComponent('context_menus.GenericElementContextMenu');
        const buttonRect = e.target.getBoundingClientRect();

        // The window X and Y offsets are to adjust position when zoomed in to page
        const x = buttonRect.right + window.pageXOffset - 37;
        const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
        // const self = this;
        this.stickersMenu = ContextualMenu.createMenu(GenericElementContextMenu, {
            chevronOffset: 10,
            chevronFace: 'bottom',
            left: x,
            top: y,
            menuWidth: this.popoverWidth,
            menuHeight: this.popoverHeight,
            element: this.state.stickersContent,
            onFinished: this.onFinished,
            menuPaddingTop: 0,
        });

        this.setState({showStickers: true});
    }

    /**
     * Trigger hiding of the sticker picker overlay
     * @param  {Event} ev Event that triggered the function call
     */
    onHideStickersClick(ev) {
        this.stickersMenu.close();
    }

    /**
     * The stickers picker was hidden
     */
    onFinished() {
        this.setState({showStickers: false});
        console.warn("finshed");
    }

    /**
     * Launch the integrations manager on the stickers integration page
     */
    _launchManageIntegrations() {
        const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
        const src = (this.scalarClient !== null && this.scalarClient.hasCredentials()) ?
                this.scalarClient.getScalarInterfaceUrlForRoom(
                    this.props.room,
                    'type_stickerpack',
                    this.widgetId,
                ) :
                null;
        Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, {
            src: src,
        }, "mx_IntegrationsManager");
        this.stickersMenu.close();
    }

    render() {
        const TintableSvg = sdk.getComponent("elements.TintableSvg");
        let stickersButton;
        if (this.state.showStickers) {
            // Show hide-stickers button
            stickersButton =

                    <TintableSvg src="img/icons-hide-stickers.svg" width="35" height="35" />

;
        } else {
            // Show show-stickers button
            stickersButton =

                    <TintableSvg src="img/icons-show-stickers.svg" width="35" height="35" />

;
        }
        return stickersButton;
    }
}
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
mongoose.connect("mongodb://localhost:2000/test");<commit_msg>update<commit_after>var mongoose = require('mongoose');
mongoose.connect(process.env.MONGODB_URI);<|endoftext|>


<commit_before>var async = require('async'),
	_ = require('lodash');

module.exports = function threads(app, api){
	var ThreadApi = api.threads;
	var getThreadReplies = function(thread, index, _callback){
		var repliesShown = nconf.get('board:repliesShown');
		if(thread && typeof thread._id == 'number'){
			async.waterfall([
				function(cb){
					ThreadApi.getThreadReplies(thread._id, (index ? repliesShown : 0), cb);
				},
				function(replies, cb){
					if(replies){
						thread.replies = replies;
					}
					if(repliesShown == replies.length){
						ThreadApi.countThreadReplies(thread._id, function(err, totalReplies){
							thread.omitted = totalReplies - repliesShown;
							cb(err, thread);
						});
					}else{
						thread.omitted = 0;
						cb(null, thread);
					}
				}],
			function(err, thread){
				_callback(err, thread);
			});
		}else{
			_callback(true);
		}
	};

	app.get('/clean/:board', function(req, res){
		ThreadApi.clean(req.params.board, null, function(result){
			res.send({'result': result})
		});
	})

	app.get('/totalThreads/:board?', function(req, res){
		var board = req.params.board;

		ThreadApi.getTotalThreads(board, function(err, total){
			if(err){
				res.status(500);
				res.send({'error': true, 'message': err.message});
			}
			res.send(total);
		});
	});

	// The "?" after each parameter makes it optional
	app.get('/threads/:board?/:page?', function(req, res){
		var board = req.params.board,
			page = req.params.page;

		ThreadApi.getIndexThreads(board, page, function(err, threads){
			if(threads){
				async.map(threads, function(reply, cb){
						getThreadReplies(reply, true, cb);
					}, function(err, threads){
					if(err){
						res.status(500);
						res.send({'error': true, 'message': 'Error while retrieving replies'});
					}else{
						res.send(threads);
					}
				});
			}else{
				res.send([]);
			}
		});
	});

	app.get('/thread/:thread/:page?', function(req, res){
		var thread = req.params.thread,
			page = req.params.page;

		ThreadApi.getThread(thread, page, function(err, thread){
			if(thread){
				getThreadReplies(thread, false, function(err, threads){
					if(err){
						res.status(500);
						res.send({'error': true, 'message': 'Error while retrieving replies'});
					}else{
						res.send(thread);
					}
				});
			}else{
				res.send([]);
			}
		});
	});

	app.post('/newThread', function(req, res){

		req.checkBody('name', 'Name can not be empty.').notEmpty();
		req.checkBody('name', 'Too many characters in name field.').len(0, 100);

		if(req.body.email !== ''){
			req.checkBody('email', 'Email field most contain a valid email.').isEmail();
			req.checkBody('email', 'Too many characters in email field.').len(0, 100);
		}

		req.checkBody('subject', 'Subject field can not be empty.').notEmpty();
		req.checkBody('subject', 'Too many characters in subject field.').len(0, 100);

		req.checkBody('comment', 'Comment field can not be empty.').notEmpty();
		req.checkBody('comment', 'Too many characters in comment field.').len(0, 10000);

		if(nconf.get('board:op:fileRequired')){
			req.assert(0, 'File is required').notNull(req.body.file.size);
		}

		req.sanitize('name').escape();
		req.sanitize('subject').escape();
		req.sanitize('comment').escape();

		var errors = req.validationErrors(true);
		if(errors){
			res.status(500);
			return res.send({
				'error': true,
				'message': 'Validation error.',
				'errors': errors.join
			});
		}

		ThreadApi.newThread(req.body, function(err, thread){
			if(err){
				res.status(500);
				res.send({
					'error': true,
					'message': err.message
				});
			}else{
				ThreadApi.clean(req.body.board);
				res.send(thread || []);
			}
		});
	});

	app.post('/newReply', function(req, res){

			req.checkBody('name', 'Name can not be empty.').notEmpty();
			req.checkBody('name', 'Too many characters in name field.').len(0, 100);

			if(req.body.email !== ''){
				req.checkBody('email', 'Email field most contain a valid email.').isEmail();
				req.checkBody('email', 'Too many characters in email field.').len(0, 100);
			}

			req.checkBody('comment', 'Comment field can not be empty.').notEmpty();
			req.checkBody('comment', 'Too many characters in comment field.').len(0, 10000);

			req.sanitize('name').escape();
			req.sanitize('comment').escape();

			var errors = req.validationErrors(true);
			if(errors){
				res.status(500);
				return res.send({
					'error': true,
					'message': 'Validation error.',
					'errors': errors
				});
			}

			ThreadApi.newReply(req.body, function(err, post){
				if(err){
					res.status(500);
					res.send({
						'error': true,
						'message': err.message
					});
				}else{
					res.send(post || []);
				}
			});
		});
};
<commit_msg>Fix #5 and previous commit.<commit_after>var async = require('async'),
	_ = require('lodash');

module.exports = function threads(app, api){
	var ThreadApi = api.threads;
	var getThreadReplies = function(thread, index, _callback){
		var repliesShown = nconf.get('board:repliesShown');
		if(thread && typeof thread._id == 'number'){
			async.waterfall([
				function(cb){
					ThreadApi.getThreadReplies(thread._id, (index ? repliesShown : 0), cb);
				},
				function(replies, cb){
					if(replies){
						thread.replies = replies;
					}
					if(repliesShown == replies.length){
						ThreadApi.countThreadReplies(thread._id, function(err, totalReplies){
							thread.omitted = totalReplies - repliesShown;
							cb(err, thread);
						});
					}else{
						thread.omitted = 0;
						cb(null, thread);
					}
				}],
			function(err, thread){
				_callback(err, thread);
			});
		}else{
			_callback(true);
		}
	};

	app.get('/clean/:board', function(req, res){
		ThreadApi.clean(req.params.board, null, function(result){
			res.send({'result': result})
		});
	})

	app.get('/totalThreads/:board?', function(req, res){
		var board = req.params.board;

		ThreadApi.getTotalThreads(board, function(err, total){
			if(err){
				res.status(500);
				res.send({'error': true, 'message': err.message});
			}
			res.send(total);
		});
	});

	// The "?" after each parameter makes it optional
	app.get('/threads/:board?/:page?', function(req, res){
		var board = req.params.board,
			page = req.params.page;

		ThreadApi.getIndexThreads(board, page, function(err, threads){
			if(threads){
				async.map(threads, function(reply, cb){
						getThreadReplies(reply, true, cb);
					}, function(err, threads){
					if(err){
						res.status(500);
						res.send({'error': true, 'message': 'Error while retrieving replies'});
					}else{
						res.send(threads);
					}
				});
			}else{
				res.send([]);
			}
		});
	});

	app.get('/thread/:thread/:page?', function(req, res){
		var thread = req.params.thread,
			page = req.params.page;

		ThreadApi.getThread(thread, page, function(err, thread){
			if(thread){
				getThreadReplies(thread, false, function(err, threads){
					if(err){
						res.status(500);
						res.send({'error': true, 'message': 'Error while retrieving replies'});
					}else{
						res.send(thread);
					}
				});
			}else{
				res.send([]);
			}
		});
	});

	app.post('/newThread', function(req, res){

		req.checkBody('name', 'Name can not be empty.').notEmpty();
		req.checkBody('name', 'Too many characters in name field.').len(0, 100);

		if(req.body.email !== ''){
			req.checkBody('email', 'Email field most contain a valid email.').isEmail();
			req.checkBody('email', 'Too many characters in email field.').len(0, 100);
		}

		req.checkBody('subject', 'Subject field can not be empty.').notEmpty();
		req.checkBody('subject', 'Too many characters in subject field.').len(0, 100);

		req.checkBody('comment', 'Comment field can not be empty.').notEmpty();
		req.checkBody('comment', 'Too many characters in comment field.').len(0, 10000);

		if(nconf.get('board:op:fileRequired')){
			req.assert(0, 'File is required').notNull(req.body.file.size);
		}

		req.sanitize('name').escape();
		req.sanitize('subject').escape();
		req.sanitize('comment').escape();

		var errors = req.validationErrors(true);
		if(errors){
			res.status(500);
			return res.send({
				'error': true,
				'message': 'Validation error.',
				'errors': errors
			});
		}

		ThreadApi.newThread(req.body, function(err, thread){
			if(err){
				res.status(500);
				res.send({
					'error': true,
					'message': err.message
				});
			}else{
				ThreadApi.clean(req.body.board);
				res.send(thread || []);
			}
		});
	});

	app.post('/newReply', function(req, res){

			req.checkBody('name', 'Name can not be empty.').notEmpty();
			req.checkBody('name', 'Too many characters in name field.').len(0, 100);

			if(req.body.email !== ''){
				req.checkBody('email', 'Email field most contain a valid email.').isEmail();
				req.checkBody('email', 'Too many characters in email field.').len(0, 100);
			}

			req.checkBody('comment', 'Comment field can not be empty.').notEmpty();
			req.checkBody('comment', 'Too many characters in comment field.').len(0, 10000);

			req.sanitize('name').escape();
			req.sanitize('comment').escape();

			var errors = req.validationErrors(true);
			if(errors){
				res.status(500);
				return res.send({
					'error': true,
					'message': 'Validation error.',
					'errors': errors
				});
			}

			ThreadApi.newReply(req.body, function(err, post){
				if(err){
					res.status(500);
					res.send({
						'error': true,
						'message': err.message
					});
				}else{
					res.send(post || []);
				}
			});
		});
};
<|endoftext|>