<commit_before>/*
	Terminal Kit

	Copyright (c) 2009 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;

// Container: an enclosed surface (ScreenBuffer), with a viewport to allow scrolling.

const Element = require( './Element.js' ) ;
const Slider = require( './Slider.js' ) ;
const ScreenBuffer = require( '../ScreenBuffer.js' ) ;

function Container( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	this.onKey = this.onKey.bind( this ) ;
	this.onClick = this.onClick.bind( this ) ;
	this.onDrag = this.onDrag.bind( this ) ;
	this.onWheel = this.onWheel.bind( this ) ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }

	//console.error( 'this.document:' , !! this.document , !! ( this.document && this.document.palette ) , !! options.palette ) ;
	this.palette = options.palette || ( this.document && this.document.palette ) ;
	this.object2attr = object => ScreenBuffer.object2attr( object , this.palette && this.palette.colorNameToIndex ) ;

	this.scrollable = !! options.scrollable ;
	this.hasVScrollBar = this.scrollable && !! options.vScrollBar ;
	this.hasHScrollBar = this.scrollable && !! options.hScrollBar ;
	this.scrollX = options.scrollX || 0 ;
	this.scrollY = options.scrollY || 0 ;
	this.vScrollBarSlider = null ;
	this.hScrollBarSlider = null ;

	this.movable = !! options.movable ;

	this.viewportX = this.outputX + this.containerBorderSize ;
	this.viewportY = this.outputY + this.containerBorderSize ;
	this.viewportWidth = this.outputWidth - this.containerBorderSize * 2 ;
	this.viewportHeight = this.outputHeight - this.containerBorderSize * 2 ;

	this.inputX = options.inputX || this.viewportX + this.scrollX ;
	this.inputY = options.inputY || this.viewportY + this.scrollY ;
	this.inputWidth = options.inputWidth || this.viewportWidth ;
	this.inputHeight = options.inputHeight || this.viewportHeight ;

	this.inputDst = new ScreenBuffer( {
		dst: this.outputDst ,
		x: this.inputX ,
		y: this.inputY ,
		width: this.inputWidth ,
		height: this.inputHeight ,
		palette: this.palette
	} ) ;

	this.deltaDraw = false ;	// Useful for Document, not so useful for other containers
	this.backgroundAttr = options.backgroundAttr || { bgColor: 'default' } ;

	this.on( 'key' , this.onKey ) ;
	this.on( 'click' , this.onClick ) ;
	this.on( 'drag' , this.onDrag ) ;
	this.on( 'wheel' , this.onWheel ) ;

	this.initChildren() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Container' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Container ;

Container.prototype = Object.create( Element.prototype ) ;
Container.prototype.constructor = Container ;
Container.prototype.elementType = 'Container' ;

Container.prototype.isContainer = true ;
Container.prototype.containerBorderSize = 0 ;

Container.prototype.destroy = function( isSubDestroy ) {
	this.off( 'key' , this.onKey ) ;
	this.off( 'click' , this.onClick ) ;
	this.off( 'drag' , this.onDrag ) ;
	this.off( 'wheel' , this.onWheel ) ;

	Element.prototype.destroy.call( this , isSubDestroy ) ;
} ;

Container.prototype.keyBindings = {
	UP: 'tinyScrollUp' ,
	DOWN: 'tinyScrollDown' ,
	PAGE_UP: 'scrollUp' ,
	PAGE_DOWN: 'scrollDown' ,
	' ': 'scrollDown' ,
	HOME: 'scrollTop' ,
	END: 'scrollBottom' ,
	LEFT: 'scrollLeft' ,
	RIGHT: 'scrollRight'
} ;

Container.prototype.initChildren = function() {
	if ( this.hasVScrollBar ) {
		this.vScrollBarSlider = new Slider( {
			internal: true ,
			parent: this ,
			x: this.viewportWidth - 1 ,
			y: 0 ,
			height: this.viewportHeight ,
			isVertical: true ,
			valueToRate: scrollY => -scrollY / Math.max( 1 , this.inputHeight - this.viewportHeight ) ,
			rateToValue: rate => -rate * Math.max( 1 , this.inputHeight - this.viewportHeight ) ,
			noDraw: true
		} ) ;

		this.vScrollBarSlider.on( 'slideStep' , d => this.scroll( 0 , -d * Math.ceil( this.viewportHeight / 2 ) ) ) ;
		this.vScrollBarSlider.on( 'slide' , value => this.scrollTo( null , value ) ) ;
	}

	if ( this.hasHScrollBar ) {
		this.hScrollBarSlider = new Slider( {
			internal: true ,
			parent: this ,
			x: 0 ,
			y: this.inputWidth - this.containerBorderSize * 2 - 1 ,
			width: this.viewportWidth - this.hasVScrollBar ,
			valueToRate: scrollY => -scrollY / Math.max( 1 , this.inputWidth - this.viewportWidth ) ,
			rateToValue: rate => -rate * Math.max( 1 , this.inputWidth - this.viewportWidth ) ,
			noDraw: true
		} ) ;

		this.hScrollBarSlider.on( 'slideStep' , d => this.scroll( -d * Math.ceil( this.viewportWidth / 2 ) , 0 ) ) ;
		this.hScrollBarSlider.on( 'slide' , value => this.scrollTo( value , null ) ) ;
	}
} ;

// Accept ScreenBuffer#resize() argument: x, y, width, height.
// Should it support output* and input* args?
Container.prototype.resizeViewport = function( to ) {
	this.viewportWidth = to.width ;
	this.viewportHeight = to.height ;
} ;

Container.prototype.resizeInput = function( to ) {
	if ( ! to.x ) { to.x = 0 ; }
	if ( ! to.y ) { to.y = 0 ; }

	this.inputDst.resize( to ) ;

	this.inputWidth = this.inputDst.width ;
	this.inputHeight = this.inputDst.height ;

	this.children.forEach( child => child.emit( 'parentResize' , to ) ) ;
} ;

Container.prototype.resize = function( to ) {
	if ( ! to.x ) { to.x = 0 ; }
	if ( ! to.y ) { to.y = 0 ; }

	this.inputDst.resize( to ) ;

	this.viewportWidth = this.inputWidth = this.inputDst.width ;
	this.viewportHeight = this.inputHeight = this.inputDst.height ;

	this.children.forEach( child => child.emit( 'parentResize' , to ) ) ;
} ;

Container.prototype.move = function( dx , dy , noDraw = false ) {
	return this.moveTo( this.outputX + dx , this.outputY + dy , noDraw ) ;
} ;

Container.prototype.moveTo = function( x , y , noDraw = false ) {
	this.outputX = x ;
	this.outputY = y ;
	this.viewportX = this.outputX + this.containerBorderSize ;
	this.viewportY = this.outputY + this.containerBorderSize ;
	this.inputDst.x = this.inputX = this.viewportX + this.scrollX ;
	this.inputDst.y = this.inputY = this.viewportY + this.scrollY ;

	if ( ! noDraw ) { this.redraw() ; }
} ;

Container.prototype.scroll = function( dx , dy , dontDraw = false ) {
	return this.scrollTo( dx ? this.scrollX + dx : null , dy ? this.scrollY + dy : null , dontDraw ) ;
} ;

Container.prototype.scrollToTop = function( dontDraw = false ) {
	return this.scrollTo( null , 0 , dontDraw ) ;
} ;

Container.prototype.scrollToBottom = function( dontDraw = false ) {
	// Ignore extra scrolling here
	return this.scrollTo( null , this.viewportHeight - this.inputHeight , dontDraw ) ;
} ;

Container.prototype.scrollTo = function( x , y , noDraw = false ) {
	if ( ! this.scrollable ) { return ; }

	if ( x !== undefined && x !== null ) {
		// Got a +1 after content size because of the word-wrap thing and eventual invisible \n
		this.scrollX = Math.min( 0 , Math.max( Math.round( x ) ,
			this.viewportWidth - this.inputWidth + 1
		) ) ;

		this.inputDst.x = this.inputX = this.viewportX + this.scrollX ;
		//console.error( "New x-scrolling:" , x , this.scrollX , this.viewportX , this.inputX ) ;
	}

	if ( y !== undefined && y !== null ) {
		this.scrollY = Math.min( 0 , Math.max( Math.round( y ) ,
			this.viewportHeight - this.inputHeight
		) ) ;

		this.inputDst.y = this.inputY = this.viewportY + this.scrollY ;
		//console.error( "New y-scrolling:" , y , this.scrollY , this.viewportY , this.inputY ) ;
	}

	if ( this.vScrollBarSlider ) {
		this.vScrollBarSlider.setValue( this.scrollY , true ) ;
		this.vScrollBarSlider.setSizeAndPosition( { y: -this.scrollY } ) ;
	}

	if ( this.hScrollBarSlider ) {
		this.hScrollBarSlider.setValue( this.scrollX , true ) ;
	}

	if ( ! noDraw ) { this.draw() ; }
} ;

Container.prototype.preDrawSelf = function() {
	this.inputDst.fill( { char: ' ' , attr: this.backgroundAttr } ) ;
} ;

Container.prototype.postDrawSelf = function() {
	this.inputDst.draw( {
		dst: this.outputDst ,
		delta: this.deltaDraw ,		// Draw only diff or not?
		inline: this.strictInline ,
		x: this.inputX ,
		y: this.inputY ,
		dstClipRect: {
			x: this.viewportX ,
			y: this.viewportY ,
			width: this.viewportWidth ,
			height: this.viewportHeight
		}
	} ) ;
} ;

Container.prototype.drawSelfCursor = function( elementTargeted ) {
	if ( elementTargeted ) { this.restoreCursor() ; }
	else { this.inputDst.drawCursor() ; }
} ;

Container.prototype.onKey = function( key , trash , data ) {
	switch( this.keyBindings[ key ] ) {
		case 'tinyScrollUp' :
			this.scroll( 0 , Math.ceil( this.textAreaHeight / 5 ) ) ;
			break ;

		case 'tinyScrollDown' :
			this.scroll( 0 , -Math.ceil( this.textAreaHeight / 5 ) ) ;
			break ;

		case 'scrollUp' :
			this.scroll( 0 , Math.ceil( this.textAreaHeight / 2 ) ) ;
			break ;

		case 'scrollDown' :
			this.scroll( 0 , -Math.ceil( this.textAreaHeight / 2 ) ) ;
			break ;

		case 'scrollLeft' :
			this.scroll( Math.ceil( this.textAreaWidth / 2 ) , 0 ) ;
			break ;

		case 'scrollRight' :
			this.scroll( -Math.ceil( this.textAreaWidth / 2 ) , 0 ) ;
			break ;

		case 'scrollTop' :
			this.scrollToTop() ;
			break ;

		case 'scrollBottom' :
			this.scrollToBottom() ;
			break ;

		default :
			return ;	// Bubble up
	}

	return true ;		// Do not bubble up
} ;

Container.prototype.onClick = function( data ) {
	// It is susceptible to click event only when it is scrollable
	if ( this.scrollable && ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}
} ;

Container.prototype.onWheel = function( data ) {
	// It's a "tiny" scroll
	if ( ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}

	if ( this.scrollable ) {
		this.scroll( 0 , -data.yDirection * Math.ceil( this.viewportHeight / 5 ) ) ;
	}
} ;

Container.prototype.onDrag = function( data ) {
	if ( ! this.movable || ( ! data.dx && ! data.dy ) ) { return ; }
	this.move( data.dx , data.dy ) ;
} ;

<commit_msg>fix: wrong parameter used in `scroll` function in Container onKey event handler<commit_after>/*
	Terminal Kit

	Copyright (c) 2009 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;

// Container: an enclosed surface (ScreenBuffer), with a viewport to allow scrolling.

const Element = require( './Element.js' ) ;
const Slider = require( './Slider.js' ) ;
const ScreenBuffer = require( '../ScreenBuffer.js' ) ;

function Container( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	this.onKey = this.onKey.bind( this ) ;
	this.onClick = this.onClick.bind( this ) ;
	this.onDrag = this.onDrag.bind( this ) ;
	this.onWheel = this.onWheel.bind( this ) ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }

	//console.error( 'this.document:' , !! this.document , !! ( this.document && this.document.palette ) , !! options.palette ) ;
	this.palette = options.palette || ( this.document && this.document.palette ) ;
	this.object2attr = object => ScreenBuffer.object2attr( object , this.palette && this.palette.colorNameToIndex ) ;

	this.scrollable = !! options.scrollable ;
	this.hasVScrollBar = this.scrollable && !! options.vScrollBar ;
	this.hasHScrollBar = this.scrollable && !! options.hScrollBar ;
	this.scrollX = options.scrollX || 0 ;
	this.scrollY = options.scrollY || 0 ;
	this.vScrollBarSlider = null ;
	this.hScrollBarSlider = null ;

	this.movable = !! options.movable ;

	this.viewportX = this.outputX + this.containerBorderSize ;
	this.viewportY = this.outputY + this.containerBorderSize ;
	this.viewportWidth = this.outputWidth - this.containerBorderSize * 2 ;
	this.viewportHeight = this.outputHeight - this.containerBorderSize * 2 ;

	this.inputX = options.inputX || this.viewportX + this.scrollX ;
	this.inputY = options.inputY || this.viewportY + this.scrollY ;
	this.inputWidth = options.inputWidth || this.viewportWidth ;
	this.inputHeight = options.inputHeight || this.viewportHeight ;

	this.inputDst = new ScreenBuffer( {
		dst: this.outputDst ,
		x: this.inputX ,
		y: this.inputY ,
		width: this.inputWidth ,
		height: this.inputHeight ,
		palette: this.palette
	} ) ;

	this.deltaDraw = false ;	// Useful for Document, not so useful for other containers
	this.backgroundAttr = options.backgroundAttr || { bgColor: 'default' } ;

	this.on( 'key' , this.onKey ) ;
	this.on( 'click' , this.onClick ) ;
	this.on( 'drag' , this.onDrag ) ;
	this.on( 'wheel' , this.onWheel ) ;

	this.initChildren() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Container' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Container ;

Container.prototype = Object.create( Element.prototype ) ;
Container.prototype.constructor = Container ;
Container.prototype.elementType = 'Container' ;

Container.prototype.isContainer = true ;
Container.prototype.containerBorderSize = 0 ;

Container.prototype.destroy = function( isSubDestroy ) {
	this.off( 'key' , this.onKey ) ;
	this.off( 'click' , this.onClick ) ;
	this.off( 'drag' , this.onDrag ) ;
	this.off( 'wheel' , this.onWheel ) ;

	Element.prototype.destroy.call( this , isSubDestroy ) ;
} ;

Container.prototype.keyBindings = {
	UP: 'tinyScrollUp' ,
	DOWN: 'tinyScrollDown' ,
	PAGE_UP: 'scrollUp' ,
	PAGE_DOWN: 'scrollDown' ,
	' ': 'scrollDown' ,
	HOME: 'scrollTop' ,
	END: 'scrollBottom' ,
	LEFT: 'scrollLeft' ,
	RIGHT: 'scrollRight'
} ;

Container.prototype.initChildren = function() {
	if ( this.hasVScrollBar ) {
		this.vScrollBarSlider = new Slider( {
			internal: true ,
			parent: this ,
			x: this.viewportWidth - 1 ,
			y: 0 ,
			height: this.viewportHeight ,
			isVertical: true ,
			valueToRate: scrollY => -scrollY / Math.max( 1 , this.inputHeight - this.viewportHeight ) ,
			rateToValue: rate => -rate * Math.max( 1 , this.inputHeight - this.viewportHeight ) ,
			noDraw: true
		} ) ;

		this.vScrollBarSlider.on( 'slideStep' , d => this.scroll( 0 , -d * Math.ceil( this.viewportHeight / 2 ) ) ) ;
		this.vScrollBarSlider.on( 'slide' , value => this.scrollTo( null , value ) ) ;
	}

	if ( this.hasHScrollBar ) {
		this.hScrollBarSlider = new Slider( {
			internal: true ,
			parent: this ,
			x: 0 ,
			y: this.inputWidth - this.containerBorderSize * 2 - 1 ,
			width: this.viewportWidth - this.hasVScrollBar ,
			valueToRate: scrollY => -scrollY / Math.max( 1 , this.inputWidth - this.viewportWidth ) ,
			rateToValue: rate => -rate * Math.max( 1 , this.inputWidth - this.viewportWidth ) ,
			noDraw: true
		} ) ;

		this.hScrollBarSlider.on( 'slideStep' , d => this.scroll( -d * Math.ceil( this.viewportWidth / 2 ) , 0 ) ) ;
		this.hScrollBarSlider.on( 'slide' , value => this.scrollTo( value , null ) ) ;
	}
} ;

// Accept ScreenBuffer#resize() argument: x, y, width, height.
// Should it support output* and input* args?
Container.prototype.resizeViewport = function( to ) {
	this.viewportWidth = to.width ;
	this.viewportHeight = to.height ;
} ;

Container.prototype.resizeInput = function( to ) {
	if ( ! to.x ) { to.x = 0 ; }
	if ( ! to.y ) { to.y = 0 ; }

	this.inputDst.resize( to ) ;

	this.inputWidth = this.inputDst.width ;
	this.inputHeight = this.inputDst.height ;

	this.children.forEach( child => child.emit( 'parentResize' , to ) ) ;
} ;

Container.prototype.resize = function( to ) {
	if ( ! to.x ) { to.x = 0 ; }
	if ( ! to.y ) { to.y = 0 ; }

	this.inputDst.resize( to ) ;

	this.viewportWidth = this.inputWidth = this.inputDst.width ;
	this.viewportHeight = this.inputHeight = this.inputDst.height ;

	this.children.forEach( child => child.emit( 'parentResize' , to ) ) ;
} ;

Container.prototype.move = function( dx , dy , noDraw = false ) {
	return this.moveTo( this.outputX + dx , this.outputY + dy , noDraw ) ;
} ;

Container.prototype.moveTo = function( x , y , noDraw = false ) {
	this.outputX = x ;
	this.outputY = y ;
	this.viewportX = this.outputX + this.containerBorderSize ;
	this.viewportY = this.outputY + this.containerBorderSize ;
	this.inputDst.x = this.inputX = this.viewportX + this.scrollX ;
	this.inputDst.y = this.inputY = this.viewportY + this.scrollY ;

	if ( ! noDraw ) { this.redraw() ; }
} ;

Container.prototype.scroll = function( dx , dy , dontDraw = false ) {
	return this.scrollTo( dx ? this.scrollX + dx : null , dy ? this.scrollY + dy : null , dontDraw ) ;
} ;

Container.prototype.scrollToTop = function( dontDraw = false ) {
	return this.scrollTo( null , 0 , dontDraw ) ;
} ;

Container.prototype.scrollToBottom = function( dontDraw = false ) {
	// Ignore extra scrolling here
	return this.scrollTo( null , this.viewportHeight - this.inputHeight , dontDraw ) ;
} ;

Container.prototype.scrollTo = function( x , y , noDraw = false ) {
	if ( ! this.scrollable ) { return ; }

	if ( x !== undefined && x !== null ) {
		// Got a +1 after content size because of the word-wrap thing and eventual invisible \n
		this.scrollX = Math.min( 0 , Math.max( Math.round( x ) ,
			this.viewportWidth - this.inputWidth + 1
		) ) ;

		this.inputDst.x = this.inputX = this.viewportX + this.scrollX ;
		//console.error( "New x-scrolling:" , x , this.scrollX , this.viewportX , this.inputX ) ;
	}

	if ( y !== undefined && y !== null ) {
		this.scrollY = Math.min( 0 , Math.max( Math.round( y ) ,
			this.viewportHeight - this.inputHeight
		) ) ;

		this.inputDst.y = this.inputY = this.viewportY + this.scrollY ;
		//console.error( "New y-scrolling:" , y , this.scrollY , this.viewportY , this.inputY ) ;
	}

	if ( this.vScrollBarSlider ) {
		this.vScrollBarSlider.setValue( this.scrollY , true ) ;
		this.vScrollBarSlider.setSizeAndPosition( { y: -this.scrollY } ) ;
	}

	if ( this.hScrollBarSlider ) {
		this.hScrollBarSlider.setValue( this.scrollX , true ) ;
	}

	if ( ! noDraw ) { this.draw() ; }
} ;

Container.prototype.preDrawSelf = function() {
	this.inputDst.fill( { char: ' ' , attr: this.backgroundAttr } ) ;
} ;

Container.prototype.postDrawSelf = function() {
	this.inputDst.draw( {
		dst: this.outputDst ,
		delta: this.deltaDraw ,		// Draw only diff or not?
		inline: this.strictInline ,
		x: this.inputX ,
		y: this.inputY ,
		dstClipRect: {
			x: this.viewportX ,
			y: this.viewportY ,
			width: this.viewportWidth ,
			height: this.viewportHeight
		}
	} ) ;
} ;

Container.prototype.drawSelfCursor = function( elementTargeted ) {
	if ( elementTargeted ) { this.restoreCursor() ; }
	else { this.inputDst.drawCursor() ; }
} ;

Container.prototype.onKey = function( key , trash , data ) {
	switch( this.keyBindings[ key ] ) {
		case 'tinyScrollUp' :
			this.scroll( 0 , Math.ceil( this.viewportHeight / 5 ) ) ;
			break ;

		case 'tinyScrollDown' :
			this.scroll( 0 , -Math.ceil( this.viewportHeight / 5 ) ) ;
			break ;

		case 'scrollUp' :
			this.scroll( 0 , Math.ceil( this.viewportHeight / 2 ) ) ;
			break ;

		case 'scrollDown' :
			this.scroll( 0 , -Math.ceil( this.viewportHeight / 2 ) ) ;
			break ;

		case 'scrollLeft' :
			this.scroll( Math.ceil( this.viewportWidth / 2 ) , 0 ) ;
			break ;

		case 'scrollRight' :
			this.scroll( -Math.ceil( this.viewportWidth / 2 ) , 0 ) ;
			break ;

		case 'scrollTop' :
			this.scrollToTop() ;
			break ;

		case 'scrollBottom' :
			this.scrollToBottom() ;
			break ;

		default :
			return ;	// Bubble up
	}

	return true ;		// Do not bubble up
} ;

Container.prototype.onClick = function( data ) {
	// It is susceptible to click event only when it is scrollable
	if ( this.scrollable && ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}
} ;

Container.prototype.onWheel = function( data ) {
	// It's a "tiny" scroll
	if ( ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}

	if ( this.scrollable ) {
		this.scroll( 0 , -data.yDirection * Math.ceil( this.viewportHeight / 5 ) ) ;
	}
} ;

Container.prototype.onDrag = function( data ) {
	if ( ! this.movable || ( ! data.dx && ! data.dy ) ) { return ; }
	this.move( data.dx , data.dy ) ;
} ;

<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import StyleButton from '../StyleButton';
import { getSelectedLink } from '../decorators/LinkDecorator';

const LINK = (

);

import { Popover, Button, Overlay, OverlayTrigger, Tooltip } from 'react-bootstrap';

export class CreateLinkControl extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            show: false,
            urlValue: null
        };

        this.showInputField = () => this._showInputField();
    }

    _showInputField() {
        const { editorState } = this.props;
        const selectionState = editorState.getSelection();
        const isSelected = getSelectedLink(editorState, selectionState);

        if (isSelected !== null) {
            return null;
        } else {
            // window.alert('lskdfjlsd')
        }

        // if (this.state.urlValue === null && this.state.renderInput === false) {
        //     window.prompt('sldkfj');
        // }
    }

    render() {
        const { editorState } = this.props;
        const selectionState = editorState.getSelection();
        const isSelected = getSelectedLink(editorState, selectionState);

        if (this.props.showInput) {
            return (

                            <OverlayTrigger
                                trigger="click"
                                rootClose
                                placement="bottom"
                                overlay={(
                                    <Popover
                                        id="insert-url"
                                        title="Insert URL"
                                    >
                                        Selected Text: <input placeholder=""></input>

                                        Navigate To URL: <input placeholder="Link to page..."></input>
                                    </Popover>
                                )}
                            >


<commit_before>"use strict";

import React from 'react';
import ReactDOM from 'react-dom';
import * as Three from 'three';
import {parseData, updateScene} from './scene-creator';
import {disposeScene} from './three-memory-cleaner';
import diff from 'immutablediff';
import {initPointerLock} from "./pointer-lock-navigation";
import {firstPersonOnKeyDown, firstPersonOnKeyUp} from "./libs/first-person-controls";

export default class Viewer3DFirstPerson extends React.Component {

  constructor(props) {
    super(props);

    this.width = props.width;
    this.height = props.height;
    this.stopRendering = false;
    this.renderer = window.__threeRenderer || new Three.WebGLRenderer();
    window.__threeRenderer = this.renderer;
  }

  componentDidMount() {

    /** Variables for movement control **/
    var prevTime = performance.now();
    var velocity = new Three.Vector3();
    let moveForward = false;
    let moveBackward = false;
    let moveLeft = false;
    let moveRight = false;

    let {catalog} = this.context;

    let actions = {
      areaActions: this.context.areaActions,
      holesActions: this.context.holesActions,
      itemsActions: this.context.itemsActions,
      linesActions: this.context.linesActions,
      projectActions: this.context.projectActions
    };

    let {state} = this.props;
    let data = state.scene;
    let canvasWrapper = ReactDOM.findDOMNode(this.refs.canvasWrapper);

    let scene3D = new Three.Scene();

    // As I need to show the pointer above all scene objects, I use this workaround http://stackoverflow.com/a/13309722
    let sceneOnTop = new Three.Scene();

    //RENDERER
    this.renderer.setClearColor(new Three.Color(0xffffff));
    this.renderer.setSize(this.width, this.height);

    // LOAD DATA
    let planData = parseData(data, actions, catalog);

    scene3D.add(planData.plan);

    // CAMERA
    let aspectRatio = this.width / this.height;
    let camera = new Three.PerspectiveCamera(45, aspectRatio, 0.1, 300000);

    sceneOnTop.add(camera); // The pointer is on the camera so I show it above all

    // Set position for the camera
    camera.position.set(0, 0, 0);
    camera.up = new Three.Vector3(0, 1, 0);

    // HELPER AXIS
    let axisHelper = new Three.AxisHelper(100);
    scene3D.add(axisHelper);

    // LIGHT
    let light = new Three.AmbientLight(0xafafaf); // soft white light
    scene3D.add(light);

    // Add another light
    let spotLight1 = new Three.SpotLight(0xffffff, 0.30);
    spotLight1.position.set(1000, 0, -1000);
    camera.add(spotLight1);

    // POINTER LOCK

    document.body.requestPointerLock = document.body.requestPointerLock ||
      document.body.mozRequestPointerLock ||
      document.body.webkitRequestPointerLock;

    document.body.requestPointerLock();

    let {controls, pointerlockChangeEvent, requestPointerLockEvent} = initPointerLock(camera, this.renderer.domElement);
    this.controls = controls;
    this.pointerlockChangeListener = pointerlockChangeEvent;
    this.requestPointerLockEvent = requestPointerLockEvent;

    /* Set user initial position */
    let humanHeight = 170; // 170 cm

    let yInitialPosition = planData.boundingBox.min.y + (planData.boundingBox.min.y - planData.boundingBox.max.y) / 2 + humanHeight;
    this.controls.getObject().position.set(-50, yInitialPosition, -100);
    sceneOnTop.add(this.controls.getObject()); // Add the pointer lock controls to the scene that will be rendered on top

    // Add move controls on the page
    this.keyDownEvent = (event) => {
      let moveResult = firstPersonOnKeyDown(event, moveForward, moveLeft, moveBackward, moveRight);
      moveForward = moveResult.moveForward;
      moveLeft = moveResult.moveLeft;
      moveBackward = moveResult.moveBackward;
      moveRight = moveResult.moveRight;
    };

    this.keyUpEvent = (event) => {
      let moveResult = firstPersonOnKeyUp(event, moveForward, moveLeft, moveBackward, moveRight);
      moveForward = moveResult.moveForward;
      moveLeft = moveResult.moveLeft;
      moveBackward = moveResult.moveBackward;
      moveRight = moveResult.moveRight;
    };

    document.addEventListener('keydown', this.keyDownEvent);
    document.addEventListener('keyup', this.keyUpEvent);

    // Add a pointer to the scene

    let pointer = new Three.Object3D();

    let pointerMaterial = new Three.MeshBasicMaterial({depthTest: false, depthWrite: false, color: 0x000000});
    let pointerGeometry1 = new Three.Geometry();
    pointerGeometry1.vertices.push(new Three.Vector3(-10, 0, 0));
    pointerGeometry1.vertices.push(new Three.Vector3(10, 0, 0));

    let linePointer1 = new Three.Line(pointerGeometry1, pointerMaterial);
    linePointer1.position.z -= 100;

    let pointerGeometry2 = new Three.Geometry();
    pointerGeometry2.vertices.push(new Three.Vector3(0, 10, 0));
    pointerGeometry2.vertices.push(new Three.Vector3(0, -10, 0));

    let linePointer2 = new Three.Line(pointerGeometry2, pointerMaterial);
    linePointer2.renderDepth = 1e20;
    linePointer2.position.z -= 100;

    let pointerGeometry3 = new Three.Geometry();
    pointerGeometry3.vertices.push(new Three.Vector3(-1, 1, 0));
    pointerGeometry3.vertices.push(new Three.Vector3(1, 1, 0));
    pointerGeometry3.vertices.push(new Three.Vector3(1, -1, 0));
    pointerGeometry3.vertices.push(new Three.Vector3(-1, -1, 0));
    pointerGeometry3.vertices.push(new Three.Vector3(-1, 1, 0));

    let linePointer3 = new Three.Line(pointerGeometry3, pointerMaterial);
    linePointer3.position.z -= 100;

    pointer.add(linePointer1);
    pointer.add(linePointer2);
    pointer.add(linePointer3);

    camera.add(pointer); // Add the pointer to the camera

    // OBJECT PICKING
    let toIntersect = [planData.plan];

    let mouseVector = new Three.Vector2(0, 0);
    let raycaster = new Three.Raycaster();

    this.firstPersonMouseDown = (event) => {

      // First of all I check if controls are enabled

      if (this.controls.enabled) {
        event.preventDefault();

        /* Per avere la direzione da assegnare al raycaster, chiamo il metodo getDirection di PointerLockControls,
         * che restituisce una funzione che a sua volta prende un vettore, vi scrive i valori degli oggetti
         * pitch e yaw e lo restituisce */

        raycaster.setFromCamera(mouseVector, camera);

        var intersects = raycaster.intersectObjects(toIntersect, true);
        if (intersects.length > 0) {
          intersects[0].object.interact && intersects[0].object.interact();
        } else {
          this.context.projectActions.unselectAll();
        }
      }

    };

    document.addEventListener('mousedown', this.firstPersonMouseDown, false);

    // add the output of the renderer to the html element
    canvasWrapper.appendChild(this.renderer.domElement);
    this.renderer.autoClear = false;

    let render = () => {

      yInitialPosition = planData.boundingBox.min.y + humanHeight;

      this.controls.getObject().position.y = yInitialPosition;

      let time = performance.now();
      let delta = ( time - prevTime ) / 200;

      velocity.x -= velocity.x * 10.0 * delta;
      velocity.z -= velocity.z * 10.0 * delta;

      if (moveForward) velocity.z -= 400.0 * delta;
      if (moveBackward) velocity.z += 400.0 * delta;

      if (moveLeft) velocity.x -= 400.0 * delta;
      if (moveRight) velocity.x += 400.0 * delta;

      this.controls.getObject().translateX(velocity.x * delta);
      this.controls.getObject().translateZ(velocity.z * delta);

      prevTime = time;

      this.renderer.clear();                     // clear buffers
      this.renderer.render(scene3D, camera);     // render scene 1
      this.renderer.clearDepth();                // clear depth buffer
      this.renderer.render(sceneOnTop, camera);  // render scene 2

      if (!this.stopRendering) {
        requestAnimationFrame(render);
      }
    };

    render();

    this.camera = camera;
    this.scene3D = scene3D;
    this.sceneOnTop = sceneOnTop;
    this.planData = planData;
  }

  componentWillUnmount() {
    this.stopRendering = true;
    this.renderer.autoClear = true;
    document.removeEventListener('mousedown', this.firstPersonMouseDown);
    document.removeEventListener('keydown', this.keyDownEvent);
    document.removeEventListener('keyup', this.keyUpEvent);
    document.removeEventListener('pointerlockchange', this.pointerlockChangeEvent);
    document.removeEventListener('mozpointerlockchange', this.pointerlockChangeEvent);
    document.removeEventListener('webkitpointerlockchange', this.pointerlockChangeEvent);
    this.renderer.domElement.removeEventListener('click', this.requestPointerLockEvent);

    disposeScene(this.scene3D);

    this.scene3D.remove(this.planData.plan);

    this.scene3D = null;
    this.planData = null;
  }

  componentWillReceiveProps(nextProps) {
    let {width, height} = nextProps;
    let {camera, renderer, scene3D, sceneOnTop, planData} = this;

    let actions = {
      areaActions: this.context.areaActions,
      holesActions: this.context.holesActions,
      itemsActions: this.context.itemsActions,
      linesActions: this.context.linesActions,
      projectActions: this.context.projectActions
    };

    this.width = width;
    this.height = height;

    camera.aspect = width / height;

    camera.updateProjectionMatrix();

    if (nextProps.scene !== this.props.state.scene) {
      let changedValues = diff(this.props.state.scene, nextProps.state.scene);
      updateScene(planData, nextProps.state.scene, this.props.state.scene, changedValues.toJS(), actions, this.context.catalog);
    }

    renderer.setSize(width, height);
    renderer.clear();                     // clear buffers
    renderer.render(scene3D, camera);     // render scene 1
    renderer.clearDepth();                // clear depth buffer
    renderer.render(sceneOnTop, camera);  // render scene 2

  }

  render() {
    return React.createElement("div", {
      ref: "canvasWrapper"
    });
  }
}

Viewer3DFirstPerson.propTypes = {
  state: React.PropTypes.object.isRequired,
  width: React.PropTypes.number.isRequired,
  height: React.PropTypes.number.isRequired
};

Viewer3DFirstPerson.contextTypes = {
  areaActions: React.PropTypes.object.isRequired,
  holesActions: React.PropTypes.object.isRequired,
  itemsActions: React.PropTypes.object.isRequired,
  linesActions: React.PropTypes.object.isRequired,
  projectActions: React.PropTypes.object.isRequired,
  catalog: React.PropTypes.object
};
<commit_msg>Temporary fix for selection between layers in 3D first person<commit_after>"use strict";

import React from 'react';
import ReactDOM from 'react-dom';
import * as Three from 'three';
import {parseData, updateScene} from './scene-creator';
import {disposeScene} from './three-memory-cleaner';
import diff from 'immutablediff';
import {initPointerLock} from "./pointer-lock-navigation";
import {firstPersonOnKeyDown, firstPersonOnKeyUp} from "./libs/first-person-controls";

export default class Viewer3DFirstPerson extends React.Component {

  constructor(props) {
    super(props);

    this.width = props.width;
    this.height = props.height;
    this.stopRendering = false;
    this.renderer = window.__threeRenderer || new Three.WebGLRenderer();
    window.__threeRenderer = this.renderer;
  }

  componentDidMount() {

    /** Variables for movement control **/
    var prevTime = performance.now();
    var velocity = new Three.Vector3();
    let moveForward = false;
    let moveBackward = false;
    let moveLeft = false;
    let moveRight = false;

    let {catalog} = this.context;

    let actions = {
      areaActions: this.context.areaActions,
      holesActions: this.context.holesActions,
      itemsActions: this.context.itemsActions,
      linesActions: this.context.linesActions,
      projectActions: this.context.projectActions
    };

    let {state} = this.props;
    let data = state.scene;
    let canvasWrapper = ReactDOM.findDOMNode(this.refs.canvasWrapper);

    let scene3D = new Three.Scene();

    // As I need to show the pointer above all scene objects, I use this workaround http://stackoverflow.com/a/13309722
    let sceneOnTop = new Three.Scene();

    //RENDERER
    this.renderer.setClearColor(new Three.Color(0xffffff));
    this.renderer.setSize(this.width, this.height);

    // LOAD DATA
    let planData = parseData(data, actions, catalog);

    scene3D.add(planData.plan);

    // CAMERA
    let aspectRatio = this.width / this.height;
    let camera = new Three.PerspectiveCamera(45, aspectRatio, 0.1, 300000);

    sceneOnTop.add(camera); // The pointer is on the camera so I show it above all

    // Set position for the camera
    camera.position.set(0, 0, 0);
    camera.up = new Three.Vector3(0, 1, 0);

    // HELPER AXIS
    let axisHelper = new Three.AxisHelper(100);
    scene3D.add(axisHelper);

    // LIGHT
    let light = new Three.AmbientLight(0xafafaf); // soft white light
    scene3D.add(light);

    // Add another light
    let spotLight1 = new Three.SpotLight(0xffffff, 0.30);
    spotLight1.position.set(1000, 0, -1000);
    camera.add(spotLight1);

    // POINTER LOCK

    document.body.requestPointerLock = document.body.requestPointerLock ||
      document.body.mozRequestPointerLock ||
      document.body.webkitRequestPointerLock;

    document.body.requestPointerLock();

    let {controls, pointerlockChangeEvent, requestPointerLockEvent} = initPointerLock(camera, this.renderer.domElement);
    this.controls = controls;
    this.pointerlockChangeListener = pointerlockChangeEvent;
    this.requestPointerLockEvent = requestPointerLockEvent;

    /* Set user initial position */
    let humanHeight = 170; // 170 cm

    let yInitialPosition = planData.boundingBox.min.y + (planData.boundingBox.min.y - planData.boundingBox.max.y) / 2 + humanHeight;
    this.controls.getObject().position.set(-50, yInitialPosition, -100);
    sceneOnTop.add(this.controls.getObject()); // Add the pointer lock controls to the scene that will be rendered on top

    // Add move controls on the page
    this.keyDownEvent = (event) => {
      let moveResult = firstPersonOnKeyDown(event, moveForward, moveLeft, moveBackward, moveRight);
      moveForward = moveResult.moveForward;
      moveLeft = moveResult.moveLeft;
      moveBackward = moveResult.moveBackward;
      moveRight = moveResult.moveRight;
    };

    this.keyUpEvent = (event) => {
      let moveResult = firstPersonOnKeyUp(event, moveForward, moveLeft, moveBackward, moveRight);
      moveForward = moveResult.moveForward;
      moveLeft = moveResult.moveLeft;
      moveBackward = moveResult.moveBackward;
      moveRight = moveResult.moveRight;
    };

    document.addEventListener('keydown', this.keyDownEvent);
    document.addEventListener('keyup', this.keyUpEvent);

    // Add a pointer to the scene

    let pointer = new Three.Object3D();

    let pointerMaterial = new Three.MeshBasicMaterial({depthTest: false, depthWrite: false, color: 0x000000});
    let pointerGeometry1 = new Three.Geometry();
    pointerGeometry1.vertices.push(new Three.Vector3(-10, 0, 0));
    pointerGeometry1.vertices.push(new Three.Vector3(10, 0, 0));

    let linePointer1 = new Three.Line(pointerGeometry1, pointerMaterial);
    linePointer1.position.z -= 100;

    let pointerGeometry2 = new Three.Geometry();
    pointerGeometry2.vertices.push(new Three.Vector3(0, 10, 0));
    pointerGeometry2.vertices.push(new Three.Vector3(0, -10, 0));

    let linePointer2 = new Three.Line(pointerGeometry2, pointerMaterial);
    linePointer2.renderDepth = 1e20;
    linePointer2.position.z -= 100;

    let pointerGeometry3 = new Three.Geometry();
    pointerGeometry3.vertices.push(new Three.Vector3(-1, 1, 0));
    pointerGeometry3.vertices.push(new Three.Vector3(1, 1, 0));
    pointerGeometry3.vertices.push(new Three.Vector3(1, -1, 0));
    pointerGeometry3.vertices.push(new Three.Vector3(-1, -1, 0));
    pointerGeometry3.vertices.push(new Three.Vector3(-1, 1, 0));

    let linePointer3 = new Three.Line(pointerGeometry3, pointerMaterial);
    linePointer3.position.z -= 100;

    pointer.add(linePointer1);
    pointer.add(linePointer2);
    pointer.add(linePointer3);

    camera.add(pointer); // Add the pointer to the camera

    // OBJECT PICKING
    let toIntersect = [planData.plan];

    let mouseVector = new Three.Vector2(0, 0);
    let raycaster = new Three.Raycaster();

    this.firstPersonMouseDown = (event) => {

      // First of all I check if controls are enabled

      if (this.controls.enabled) {
        event.preventDefault();

        /* Per avere la direzione da assegnare al raycaster, chiamo il metodo getDirection di PointerLockControls,
         * che restituisce una funzione che a sua volta prende un vettore, vi scrive i valori degli oggetti
         * pitch e yaw e lo restituisce */

        raycaster.setFromCamera(mouseVector, camera);

        let intersects = raycaster.intersectObjects(toIntersect, true);
        if (intersects.length > 0) {
          this.context.projectActions.unselectAll();
          intersects[0].object.interact && intersects[0].object.interact();
        } else {
          this.context.projectActions.unselectAll();
        }
      }

    };

    document.addEventListener('mousedown', this.firstPersonMouseDown, false);

    // add the output of the renderer to the html element
    canvasWrapper.appendChild(this.renderer.domElement);
    this.renderer.autoClear = false;

    let render = () => {

      yInitialPosition = planData.boundingBox.min.y + humanHeight;

      this.controls.getObject().position.y = yInitialPosition;

      let time = performance.now();
      let delta = ( time - prevTime ) / 200;

      velocity.x -= velocity.x * 10.0 * delta;
      velocity.z -= velocity.z * 10.0 * delta;

      if (moveForward) velocity.z -= 400.0 * delta;
      if (moveBackward) velocity.z += 400.0 * delta;

      if (moveLeft) velocity.x -= 400.0 * delta;
      if (moveRight) velocity.x += 400.0 * delta;

      this.controls.getObject().translateX(velocity.x * delta);
      this.controls.getObject().translateZ(velocity.z * delta);

      prevTime = time;

      this.renderer.clear();                     // clear buffers
      this.renderer.render(scene3D, camera);     // render scene 1
      this.renderer.clearDepth();                // clear depth buffer
      this.renderer.render(sceneOnTop, camera);  // render scene 2

      if (!this.stopRendering) {
        requestAnimationFrame(render);
      }
    };

    render();

    this.camera = camera;
    this.scene3D = scene3D;
    this.sceneOnTop = sceneOnTop;
    this.planData = planData;
  }

  componentWillUnmount() {
    this.stopRendering = true;
    this.renderer.autoClear = true;
    document.removeEventListener('mousedown', this.firstPersonMouseDown);
    document.removeEventListener('keydown', this.keyDownEvent);
    document.removeEventListener('keyup', this.keyUpEvent);
    document.removeEventListener('pointerlockchange', this.pointerlockChangeEvent);
    document.removeEventListener('mozpointerlockchange', this.pointerlockChangeEvent);
    document.removeEventListener('webkitpointerlockchange', this.pointerlockChangeEvent);
    this.renderer.domElement.removeEventListener('click', this.requestPointerLockEvent);

    disposeScene(this.scene3D);

    this.scene3D.remove(this.planData.plan);

    this.scene3D = null;
    this.planData = null;
  }

  componentWillReceiveProps(nextProps) {
    let {width, height} = nextProps;
    let {camera, renderer, scene3D, sceneOnTop, planData} = this;

    let actions = {
      areaActions: this.context.areaActions,
      holesActions: this.context.holesActions,
      itemsActions: this.context.itemsActions,
      linesActions: this.context.linesActions,
      projectActions: this.context.projectActions
    };

    this.width = width;
    this.height = height;

    camera.aspect = width / height;

    camera.updateProjectionMatrix();

    if (nextProps.scene !== this.props.state.scene) {
      let changedValues = diff(this.props.state.scene, nextProps.state.scene);
      updateScene(planData, nextProps.state.scene, this.props.state.scene, changedValues.toJS(), actions, this.context.catalog);
    }

    renderer.setSize(width, height);
    renderer.clear();                     // clear buffers
    renderer.render(scene3D, camera);     // render scene 1
    renderer.clearDepth();                // clear depth buffer
    renderer.render(sceneOnTop, camera);  // render scene 2

  }

  render() {
    return React.createElement("div", {
      ref: "canvasWrapper"
    });
  }
}

Viewer3DFirstPerson.propTypes = {
  state: React.PropTypes.object.isRequired,
  width: React.PropTypes.number.isRequired,
  height: React.PropTypes.number.isRequired
};

Viewer3DFirstPerson.contextTypes = {
  areaActions: React.PropTypes.object.isRequired,
  holesActions: React.PropTypes.object.isRequired,
  itemsActions: React.PropTypes.object.isRequired,
  linesActions: React.PropTypes.object.isRequired,
  projectActions: React.PropTypes.object.isRequired,
  catalog: React.PropTypes.object
};
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket 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.
*/

var React = require("react");
var sdk = require("../../../index");
var Invite = require("../../../Invite");
var createRoom = require("../../../createRoom");
var MatrixClientPeg = require("../../../MatrixClientPeg");
var rate_limited_func = require("../../../ratelimitedfunc");
var Modal = require('../../../Modal');

const TRUNCATE_QUERY_LIST = 40;

module.exports = React.createClass({
    displayName: "ChatInviteDialog",
    propTypes: {
        title: React.PropTypes.string,
        description: React.PropTypes.oneOfType([
            React.PropTypes.element,
            React.PropTypes.string,
        ]),
        value: React.PropTypes.string,
        placeholder: React.PropTypes.string,
        button: React.PropTypes.string,
        focus: React.PropTypes.bool,
        onFinished: React.PropTypes.func.isRequired
    },

    getDefaultProps: function() {
        return {
            title: "Start a chat",
            description: "Who would you like to communicate with?",
            value: "",
            placeholder: "User ID, Name or email",
            button: "Start Chat",
            focus: true
        };
    },

    getInitialState: function() {
        return {
            user: null,
            queryList: [],
            addressSelected: false,
            selected: 0,
        };
    },

    componentDidMount: function() {
        if (this.props.focus) {
            // Set the cursor at the end of the text input
            this.refs.textinput.value = this.props.value;
        }
        this._updateUserList();
    },

    onStartChat: function() {
        if (this.state.user) {
            this._startChat(this.state.user.userId);
        } else {
            this._startChat(this.refs.textinput.value);
        }
    },

    onCancel: function() {
        this.props.onFinished(false);
    },

    onKeyDown: function(e) {
        if (e.keyCode === 27) { // escape
            e.stopPropagation();
            e.preventDefault();
            this.props.onFinished(false);
        } else if (e.keyCode === 38) { // up arrow
            e.stopPropagation();
            e.preventDefault();
            if (this.state.selected > 0) {
                this.setState({ selected: this.state.selected - 1 });
            }
        } else if (e.keyCode === 40) { // down arrow
            e.stopPropagation();
            e.preventDefault();
            if (this.state.selected < this._maxSelected(this.state.queryList)) {
                this.setState({ selected: this.state.selected + 1 });
            }
        } else if (e.keyCode === 13) { // enter
            e.stopPropagation();
            e.preventDefault();
            if (this.state.queryList.length > 0) {
                this.setState({
                    user: this.state.queryList[this.state.selected],
                    addressSelected: true,
                    queryList: [],
                });
            }
        }
    },

    onQueryChanged: function(ev) {
        var query = ev.target.value;
        var queryList = [];

        // Only do search if there is something to search
        if (query.length > 0) {
            queryList = this._userList.filter((user) => {
                return this._matches(query, user);
            });
        }

        // Make sure the selected item isn't outside the list bounds
        var selected = this.state.selected;
        var maxSelected = this._maxSelected(queryList);
        if (selected > maxSelected) {
            selected = maxSelected;
        }

        this.setState({
            queryList: queryList,
            selected: selected,
        });
    },

    onDismissed: function() {
        this.setState({
            user: null,
            addressSelected: false,
            selected: 0,
            queryList: [],
        });
    },

    createQueryListTiles: function() {
        var self = this;
        var AddressTile = sdk.getComponent("elements.AddressTile");
        var maxSelected = this._maxSelected(this.state.queryList);
        var queryList = [];
        if (this.state.queryList.length > 0) {
            for (var i = 0; i <= maxSelected; i++) {
                queryList.push(

                        <AddressTile user={this.state.queryList[i]} canDismiss={false} />

                );
            }
        }
        return queryList;
    },

    _startChat: function(addr) {
        // Start the chat
        createRoom().then(function(roomId) {
            return Invite.inviteToRoom(roomId, addr);
        })
        .catch(function(err) {
            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createDialog(ErrorDialog, {
                title: "Failure to invite user",
                description: err.toString()
            });
            return null;
        })
        .done();

        // Close - this will happen before the above, as that is async
        this.props.onFinished(true, addr);
    },

    _updateUserList: new rate_limited_func(function() {
        // Get all the users
        this._userList = MatrixClientPeg.get().getUsers();
    }, 500),

    _maxSelected: function(list) {
        var listSize = list.length === 0 ? 0 : list.length - 1;
        var maxSelected = listSize > (TRUNCATE_QUERY_LIST - 1) ? (TRUNCATE_QUERY_LIST - 1) : listSize
        return maxSelected;
    },

    // This is the search algorithm for matching users
    _matches: function(query, user) {
        var name = user.displayName.toLowerCase();
        var uid = user.userId.toLowerCase();
        query = query.toLowerCase();

        // direct prefix matches
        if (name.indexOf(query) === 0 || uid.indexOf(query) === 0) {
            return true;
        }

        // strip @ on uid and try matching again
        if (uid.length > 1 && uid[0] === "@" && uid.substring(1).indexOf(query) === 0) {
            return true;
        }

        // split spaces in name and try matching constituent parts
        var parts = name.split(" ");
        for (var i = 0; i < parts.length; i++) {
            if (parts[i].indexOf(query) === 0) {
                return true;
            }
        }
        return false;
    },

    render: function() {
        var TintableSvg = sdk.getComponent("elements.TintableSvg");
        console.log("### D E B U G - queryList:");
        console.log(this.state.queryList);

        var query;
        if (this.state.addressSelected) {
            var AddressTile = sdk.getComponent("elements.AddressTile");
            query = (
                <AddressTile user={this.state.user} canDismiss={true} onDismissed={this.onDismissed} />
            );
        } else {
            query = (
                <input type="text"
                    id="textinput"
                    ref="textinput"
                    className="mx_ChatInviteDialog_input"
                    onChange={this.onQueryChanged}
                    placeholder={this.props.placeholder}
                    defaultValue={this.props.value}
                    autoFocus={this.props.focus}
                    onKeyDown={this.onKeyDown}
                    autoComplete="off"
                    size="64"/>
            );
        }

        var queryList;
        var queryListElements = this.createQueryListTiles();
        if (queryListElements.length > 0) {
            queryList = (

                    { queryListElements }

            );
        }

        return (

                    {this.props.title}

                    <TintableSvg src="img/icons-close-button.svg" width="35" height="35" />

                    { this.props.description }

{ query }

                    { queryList }

                    <button className="mx_Dialog_primary" onClick={this.onStartChat}>
                        {this.props.button}
                    </button>

        );
    }
});
<commit_msg>Circumventing autofill by using a textarea rather than an input type=text<commit_after>/*
Copyright 2015, 2016 OpenMarket 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.
*/

var React = require("react");
var sdk = require("../../../index");
var Invite = require("../../../Invite");
var createRoom = require("../../../createRoom");
var MatrixClientPeg = require("../../../MatrixClientPeg");
var rate_limited_func = require("../../../ratelimitedfunc");
var Modal = require('../../../Modal');

const TRUNCATE_QUERY_LIST = 40;

module.exports = React.createClass({
    displayName: "ChatInviteDialog",
    propTypes: {
        title: React.PropTypes.string,
        description: React.PropTypes.oneOfType([
            React.PropTypes.element,
            React.PropTypes.string,
        ]),
        value: React.PropTypes.string,
        placeholder: React.PropTypes.string,
        button: React.PropTypes.string,
        focus: React.PropTypes.bool,
        onFinished: React.PropTypes.func.isRequired
    },

    getDefaultProps: function() {
        return {
            title: "Start a chat",
            description: "Who would you like to communicate with?",
            value: "",
            placeholder: "User ID, Name or email",
            button: "Start Chat",
            focus: true
        };
    },

    getInitialState: function() {
        return {
            user: null,
            queryList: [],
            addressSelected: false,
            selected: 0,
        };
    },

    componentDidMount: function() {
        if (this.props.focus) {
            // Set the cursor at the end of the text input
            this.refs.textinput.value = this.props.value;
        }
        this._updateUserList();
    },

    onStartChat: function() {
        if (this.state.user) {
            this._startChat(this.state.user.userId);
        } else {
            this._startChat(this.refs.textinput.value);
        }
    },

    onCancel: function() {
        this.props.onFinished(false);
    },

    onKeyDown: function(e) {
        if (e.keyCode === 27) { // escape
            e.stopPropagation();
            e.preventDefault();
            this.props.onFinished(false);
        } else if (e.keyCode === 38) { // up arrow
            e.stopPropagation();
            e.preventDefault();
            if (this.state.selected > 0) {
                this.setState({ selected: this.state.selected - 1 });
            }
        } else if (e.keyCode === 40) { // down arrow
            e.stopPropagation();
            e.preventDefault();
            if (this.state.selected < this._maxSelected(this.state.queryList)) {
                this.setState({ selected: this.state.selected + 1 });
            }
        } else if (e.keyCode === 13) { // enter
            e.stopPropagation();
            e.preventDefault();
            if (this.state.queryList.length > 0) {
                this.setState({
                    user: this.state.queryList[this.state.selected],
                    addressSelected: true,
                    queryList: [],
                });
            }
        }
    },

    onQueryChanged: function(ev) {
        var query = ev.target.value;
        var queryList = [];

        // Only do search if there is something to search
        if (query.length > 0) {
            queryList = this._userList.filter((user) => {
                return this._matches(query, user);
            });
        }

        // Make sure the selected item isn't outside the list bounds
        var selected = this.state.selected;
        var maxSelected = this._maxSelected(queryList);
        if (selected > maxSelected) {
            selected = maxSelected;
        }

        this.setState({
            queryList: queryList,
            selected: selected,
        });
    },

    onDismissed: function() {
        this.setState({
            user: null,
            addressSelected: false,
            selected: 0,
            queryList: [],
        });
    },

    createQueryListTiles: function() {
        var self = this;
        var AddressTile = sdk.getComponent("elements.AddressTile");
        var maxSelected = this._maxSelected(this.state.queryList);
        var queryList = [];
        if (this.state.queryList.length > 0) {
            for (var i = 0; i <= maxSelected; i++) {
                queryList.push(

                        <AddressTile user={this.state.queryList[i]} canDismiss={false} />

                );
            }
        }
        return queryList;
    },

    _startChat: function(addr) {
        // Start the chat
        createRoom().then(function(roomId) {
            return Invite.inviteToRoom(roomId, addr);
        })
        .catch(function(err) {
            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createDialog(ErrorDialog, {
                title: "Failure to invite user",
                description: err.toString()
            });
            return null;
        })
        .done();

        // Close - this will happen before the above, as that is async
        this.props.onFinished(true, addr);
    },

    _updateUserList: new rate_limited_func(function() {
        // Get all the users
        this._userList = MatrixClientPeg.get().getUsers();
    }, 500),

    _maxSelected: function(list) {
        var listSize = list.length === 0 ? 0 : list.length - 1;
        var maxSelected = listSize > (TRUNCATE_QUERY_LIST - 1) ? (TRUNCATE_QUERY_LIST - 1) : listSize
        return maxSelected;
    },

    // This is the search algorithm for matching users
    _matches: function(query, user) {
        var name = user.displayName.toLowerCase();
        var uid = user.userId.toLowerCase();
        query = query.toLowerCase();

        // direct prefix matches
        if (name.indexOf(query) === 0 || uid.indexOf(query) === 0) {
            return true;
        }

        // strip @ on uid and try matching again
        if (uid.length > 1 && uid[0] === "@" && uid.substring(1).indexOf(query) === 0) {
            return true;
        }

        // split spaces in name and try matching constituent parts
        var parts = name.split(" ");
        for (var i = 0; i < parts.length; i++) {
            if (parts[i].indexOf(query) === 0) {
                return true;
            }
        }
        return false;
    },

    render: function() {
        var TintableSvg = sdk.getComponent("elements.TintableSvg");
        console.log("### D E B U G - queryList:");
        console.log(this.state.queryList);

        var query;
        if (this.state.addressSelected) {
            var AddressTile = sdk.getComponent("elements.AddressTile");
            query = (
                <AddressTile user={this.state.user} canDismiss={true} onDismissed={this.onDismissed} />
            );
        } else {
            query = (
                <textarea rows="1"
                    id="textinput"
                    ref="textinput"
                    className="mx_ChatInviteDialog_input"
                    onChange={this.onQueryChanged}
                    placeholder={this.props.placeholder}
                    defaultValue={this.props.value}
                    autoFocus={this.props.focus}
                    onKeyDown={this.onKeyDown}>
                </textarea>
            );
        }

        var queryList;
        var queryListElements = this.createQueryListTiles();
        if (queryListElements.length > 0) {
            queryList = (

                    { queryListElements }

            );
        }

        return (

                    {this.props.title}

                    <TintableSvg src="img/icons-close-button.svg" width="35" height="35" />

                    { this.props.description }

{ query }

                    { queryList }

                    <button className="mx_Dialog_primary" onClick={this.onStartChat}>
                        {this.props.button}
                    </button>

        );
    }
});
<|endoftext|>


<commit_before>import { connect } from 'react-redux';
import PluginSwitch from './PluginSwitch.js';
import OHIF from 'ohif-core';

const { setLayout } = OHIF.redux.actions;

const mapStateToProps = state => {
  const { activeViewportIndex, layout, viewportSpecificData } = state.viewports;

  return {
    activeViewportIndex,
    viewportSpecificData,
    layout,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    setLayout: data => {
      dispatch(setLayout(data));
    },
  };
};

/*function setSingleLayoutData(originalArray, viewportIndex, data) {
  const viewports = originalArray.slice();
  const layoutData = Object.assign({}, viewports[viewportIndex], data);

  viewports[viewportIndex] = layoutData;

  return viewports;
}*/

const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
  //const { activeViewportIndex, layout } = propsFromState;
  //const { setLayout } = propsFromDispatch;

  // TODO: Do not display certain options if the current display set
  // cannot be displayed using these view types
  const buttons = [
    /*{
      text: 'Acquired',
      type: 'command',
      icon: 'bars',
      active: false,
      onClick: () => {
        console.warn('Original Acquisition');

        const layoutData = setSingleLayoutData(
          layout.viewports,
          activeViewportIndex,
          { plugin: 'cornerstone' }
        );

        setLayout({ viewports: layoutData });
      },
    },
    {
      text: 'Axial',
      icon: 'cube',
      active: false,
      onClick: () => {
        window.commandsManager.runCommand('axial', {}, 'vtk');
      },
    },
    {
      text: 'Sagittal',
      icon: 'cube',
      active: false,
      onClick: () => {
        window.commandsManager.runCommand('sagittal', {}, 'vtk');
      },
    },
    {
      text: 'Coronal',
      icon: 'cube',
      active: false,
      onClick: () => {
        window.commandsManager.runCommand('coronal', {}, 'vtk');
      },
    },*/
    {
      text: '2D MPR',
      icon: 'cube',
      active: false,
      onClick: () => {
        window.commandsManager.runCommand('mpr2d', {}, 'vtk');
      },
    },
  ];

  return {
    buttons,
  };
};

const ConnectedPluginSwitch = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(PluginSwitch);

export default ConnectedPluginSwitch;
<commit_msg>Technically available in _any_ context, not just VTK<commit_after>import OHIF from 'ohif-core';
import PluginSwitch from './PluginSwitch.js';
import { connect } from 'react-redux';

const { setLayout } = OHIF.redux.actions;

const mapStateToProps = state => {
  const { activeViewportIndex, layout, viewportSpecificData } = state.viewports;

  return {
    activeViewportIndex,
    viewportSpecificData,
    layout,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    setLayout: data => {
      dispatch(setLayout(data));
    },
  };
};

/*function setSingleLayoutData(originalArray, viewportIndex, data) {
  const viewports = originalArray.slice();
  const layoutData = Object.assign({}, viewports[viewportIndex], data);

  viewports[viewportIndex] = layoutData;

  return viewports;
}*/

const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
  //const { activeViewportIndex, layout } = propsFromState;
  //const { setLayout } = propsFromDispatch;

  // TODO: Do not display certain options if the current display set
  // cannot be displayed using these view types
  const buttons = [
    /*{
      text: 'Acquired',
      type: 'command',
      icon: 'bars',
      active: false,
      onClick: () => {
        console.warn('Original Acquisition');

        const layoutData = setSingleLayoutData(
          layout.viewports,
          activeViewportIndex,
          { plugin: 'cornerstone' }
        );

        setLayout({ viewports: layoutData });
      },
    },
    {
      text: 'Axial',
      icon: 'cube',
      active: false,
      onClick: () => {
        window.commandsManager.runCommand('axial', {}, 'vtk');
      },
    },
    {
      text: 'Sagittal',
      icon: 'cube',
      active: false,
      onClick: () => {
        window.commandsManager.runCommand('sagittal', {}, 'vtk');
      },
    },
    {
      text: 'Coronal',
      icon: 'cube',
      active: false,
      onClick: () => {
        window.commandsManager.runCommand('coronal', {}, 'vtk');
      },
    },*/
    {
      text: '2D MPR',
      icon: 'cube',
      active: false,
      onClick: () => {
        window.commandsManager.runCommand('mpr2d');
      },
    },
  ];

  return {
    buttons,
  };
};

const ConnectedPluginSwitch = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(PluginSwitch);

export default ConnectedPluginSwitch;
<|endoftext|>


<commit_before>function recognize(s, context) {
  try {
    var result = eval('function () {' + s + '}');

    return {
      'com.solveforall.recognition.programming.Javascript' : [
        {
          matchedText: s,
          recognitionLevel : 1.0,
        }
      ]
    };
  } catch (e) {
    return null;
  }
}
<commit_msg>If the query string is JSON (including number and boolean literals), have the recognition level for Javascript be 0.<commit_after>/*jslint continue: true, devel: true, evil: true, indent: 2, nomen: true, plusplus: true, regexp: true, rhino: true, sloppy: true, sub: true, unparam: true, vars: true, white: true */
/*global _, HostAdapter, hostAdapter */

function recognize(s, context) {
  try {
    var result = eval('function () {' + s + '}');

    var isJson = false;
    try {
      JSON.parse(s);
      isJson = true;
    } catch (je) {
      // Not JSON
    }

    return {
      'com.solveforall.recognition.programming.Javascript' : [
        {
          matchedText: s,
          recognitionLevel: (isJson ? 0.0 : 1.0)
        }
      ]
    };
  } catch (e) {
    return null;
  }
}
<|endoftext|>


<commit_before>/**
 * Created by c14hht on 2016-04-28.
 *
 * Class that when entering the studentid, assignmentid, courseid and submitting gets the feedback and view it.
 *
 * This is to be changed when studentid, assignmentid and courseid will be connected with the logged in person.
 *
 */

window.Feedback = React.createClass ({

    handleClick: function () {
        var formValues = {};
        formValues["studentID"] = $("#student").val();
        formValues["assignmentID"] = $("#ass").val();
        formValues["courseID"] = $("#course").val();
        formValues["courseCode"] = 1;

        $.ajax({
            type : "GET",
            url : window.globalURL+"/feedback/get",
            data : formValues,
            timeout : 100000,
            success : function(response) {
                console.log("SUCCESS: ", response);
                var params = $.param(formValues);
                return (
                    ReactDOM.render(
                        <NewFeedback feedbackResponse={response}
                                     sourceInfo={window.globalURL + "/feedback/video?" + params}/>,
                        document.getElementById('courseContent')
                    )
                )
            }, error : function(e) {
                console.log("ERROR: ", e);
            }
        });

    },
    render: function () {
        return (

                <form>
                    StudentID:

                    <input id="student"type="text" name="studentid"/>

                    AssID:

                    <input id="ass" type="text" name="assid"/>

                    CourseID:

                    <input id="course" type="text" name="courseid"/>
                </form>
                <button style={{backgroundColor: 'black',color:'white'}} onClick={this.handleClick}>Submit</button>

        )
    }
});

var NewFeedback = React.createClass ({

    render: function () {

        var feedbackResponse = this.props.feedbackResponse;

        if(feedbackResponse.error === undefined) {
            return (

Teacher:

                    {feedbackResponse.teacher}

Grade:

                    {feedbackResponse.grade}

Time:

                    {feedbackResponse.time}

Feedback:

                    {feedbackResponse.feedback}

                    <video width="720" height="460" src={this.props.sourceInfo} preload="auto" controls/>

            )
        } else {
            return (

Error: {feedbackResponse.error}

            )
        }
    }
});<commit_msg>Changed to RESTfull http requests<commit_after>/**
 * Created by c14hht on 2016-04-28.
 *
 * Class that when entering the student id, assignment id and submitting gets the feedback and view it.
 *
 * This is to be changed when student id, assignment id  will be connected with the logged in person.
 *
 */

window.Feedback = React.createClass({

    handleClick: function () {
        //Skicka till assignments/{assignmentid}/submissions/{studentid}
        $.ajax({
            type: "GET",
            url: window.globalURL + "/assignments/" + $("#assignment").val() + "/submissions/" + $("#student").val(),
            timeout: 100000,
            success: function (response) {
                console.log("SUCCESS: ", response);
                return (
                    ReactDOM.render(
                        <NewFeedback feedbackResponse={response}
                                     sourceInfo={window.globalURL + "/feedback/video?"}/>,
                        document.getElementById('courseContent')
                    )
                )
            }, error: function (e) {
                console.log("ERROR: ", e);
                ReactDOM.render(

                        ERROR

,
                    document.getElementById('courseContent')
                )
            }, statusCode: {
                404: function () {
                    alert("Page not found");
                }
            }
        });
    },
    render: function () {
        return (

                <form>
                    StudentID:

                    <input id="student" type="text" name="studentid"/>

                    AssID:

                    <input id="assignment" type="text" name="assid"/>

                    <button id="submitbutton" type="button" onClick={this.handleClick}>Submit</button>
                </form>

        )
    }
});

var NewFeedback = React.createClass({

    render: function () {

        var feedbackResponse = this.props.feedbackResponse;
        return (

Teacher:

                {feedbackResponse.teacher}

Grade:

                {feedbackResponse.grade}

Time:

                {feedbackResponse.time}

Feedback:

                {feedbackResponse.feedback}

                <video width="720" height="460" src={this.props.sourceInfo} preload="auto" controls/>

        )
    }
});<|endoftext|>


<commit_before>"use strict";
let commandsInModule = [];
let unirest = require('unirest');
let request = require('request');

const auth = require("../../auth.json");
const pats = require("../lists/pats.js").pats;
const Permissions = require("../../databases/helpers/permissions.js");
const ServerSettings = require("../../databases/helpers/serversettings.js");

commandsInModule.pat = {
	name: 'pat', module: 'Fun',
	help: 'Pats a user!',
	usage: '[@user/user]',
	cooldown: 10, levelReq: 0,
	exec: function (Client, msg, args) {
		if (msg.mentions.length <= 0) {
			/* We need to fetch all the members to search in a broader list */
			Client.Users.fetchMembers();

			let userObj = Client.Users.getBy("username", args);

			if (!userObj) {
				msg.channel.sendMessage(":warning: User `" + args + "` doesn't seem to exist.");
				return;
			}

			let guildUserObj = userObj.memberOf(msg.guild);

			if (!guildUserObj) {
				msg.channel.sendMessage(":warning: User `" + args + "` doesn't seem to exist.");
				return;
			}

			msg.channel.sendMessage(guildUserObj.mention + " you have been patted by " + msg.author.mention + "\n" +
			pats[Math.floor(Math.random() * pats.length)]);
		} else {
			if (msg.mentions.length > 1) {
				msg.channel.sendMessage(":confounded: Sorry, I can only pat one user at a time.");
				return;
			}

			let guildUserObj = msg.mentions[0].memberOf(msg.guild);

			if (!guildUserObj) {
				msg.channel.sendMessage(":warning: User tagged is not a valid member of the guild!");
				return;
			}

			msg.channel.sendMessage(guildUserObj.mention + " you have been patted by " + msg.author.mention + "\n" +
			pats[Math.floor(Math.random() * pats.length)]);
		}
	}
}

commandsInModule.ud = {
	name: 'ud', module: 'Fun',
	help: 'Looks up a definition on Urban Dictionary.',
	usage: '[search terms]',
	cooldown: 30, levelReq: 0,
	exec: function (Client, msg, args) {
		if (!args) {
			msg.channel.sendMessage(":warning: No arguments specified!");
		}

		let definitionURL = "https://mashape-community-urban-dictionary.p.mashape.com/define?term=" + args.split(" ").join("+");

		unirest.get(definitionURL).header("X-Mashape-Key", auth.mashapeKey)
		.header("Accept", "text/plain")
		.end(function (result) {
			if (result.status === 200) {
				if (result.body) {
					let msgArray = [];
					let topResult = result.body.list[0];

					msgArray.push("__**Search terms**__: **" + args + "**");
					msgArray.push("__**Definition**__: " + topResult.definition);
					msgArray.push("__**Example**__:\n*" + topResult.example + "*");
					msgArray.push("__**Author**__: " + topResult.author);
					msgArray.push("__**Thumbs Up**__: " + topResult.thumbs_up + " | __Thumbs Down__: " + topResult.thumbs_down);
					msgArray.push("<" + topResult.permalink + ">");

					msg.channel.sendMessage(msgArray.join("\n"));
				}
			} else {
				msg.channel.sendMessage(":warning: Something went wrong while trying to get the definition, or it doesn't exist!");
			}
		});
	}
}

commandsInModule.roll = {
	name: 'roll', module: 'Fun',
	help: 'Roll a dice! If no dice type is specified, the default is 1d6.',
	usage: '[(number of rolls)d(sides of the dice)]',
	cooldown: 10, levelReq: 0,
	exec: function (Client, msg, args) {
		let arg = args.split(" ");
		let dice, urlReq, object;

		if (!/\d+d\d+/.test(arg[0])) {
			dice = "1d6";
		} else {
			dice = arg[0];
		}

		urlReq = `https://rolz.org/api/?${dice}.json`

		request(urlReq, {}, function(error, response, body) {
			if (error) {
				console.log(error);
				msg.channel.sendMessage(":warning: There was an error while processing that request. Details:\n```xl\n" + error + "```");
			}

			if (response.statusCode === 200) {
				object = JSON.parse(body);

				msg.channel.sendMessage(":game_die: You rolled a **" + object.result + "**!\n__Details__:" + object.details);
			}
		});
	}
}

commandsInModule.russianroulette = {
	name: 'russianroulette', module: 'Fun',
	help: 'Play russian roulette! If you lose, you get muted for 1 to 5 minutes, so be careful.',
	usage: '',
	cooldown: 120, levelReq: 0,
	exec: function (Client, msg, args) {
		if (!Client.User.permissionsFor(msg.guild).General.MANAGE_ROLES) {
			msg.channel.sendMessage(':sob: I do not have permission to manage roles in this server!');
			return;
		}

		let time = (Math.floor(Math.random() * 4) + 1) * 60 * 1000;
		let chance = parseInt(Math.floor(Math.random() * 2));

		if (chance === 1) {
			Permissions.getMuteRole(msg.guild).then(r => {
				let muteRole = msg.guild.roles.find(k => k.id === r);

				if (!muteRole) {
					msg.channel.sendMessage(':warning: No mute role has been set! Set it with `setmute [Role Name/@Role]`');
					return;
				}

				let member = msg.author.memberOf(msg.guild);

				member.assignRole(muteRole).then(() => {
					msg.channel.sendMessage(':gun: Member `' + member.username + '#' + member.discriminator + '` got shot while playing Russian Roulette! (muted for: **' + (time / 1000 / 60) + '** minutes).')
				}).catch(er => {
					console.log(er);
				});

				setTimeout(() => {member.unassignRole(muteRole)}, time);
			});
		} else {
			msg.channel.sendMessage(":relieved: The odds were with you this time!");
		}
	}
}

commandsInModule.color = {
	name: 'color', module: 'Fun',
	help: 'Set a custom color role for yourself!',
	usage: '[color hex]',
	cooldown: 300, levelReq: 1,
	exec: function (Client, msg, args) {
		ServerSettings.checkColors(msg.guild).then(r => {
			if (r === "ENABLED") {
				if (!/#[A-F0-9]{6}/i.test(args) || args.length > 7) {
					msg.reply("invalid color hex! Precede the code by a `#`. Only numbers and letters A through F are allowed. Example: `#B4A90F`");
					return;
				} else if (!args) {
					msg.reply("you did not specify a color!");
					return;
				}

				if (!Client.User.permissionsFor(msg.guild).General.MANAGE_ROLES) {
					msg.channel.sendMessage(':sob: I do not have permission to manage roles in this server!');
					return;
				}

				let findColorRole = msg.guild.roles.find(k => k.name === args.toUpperCase());

				if (findColorRole) {
					msg.member.assignRole(findColorRole).then(() => {
						msg.channel.sendMessage(":ok_hand::skin-tone-1: Your color has been set to `" + args.toUpperCase() + "`. Note: if the color doesn't show up, that means the role has to be moved up in the list!");
					}).catch(e => {
						msg.channel.sendMessage(':interrobang: Woah! Something went wrong while running that command!. Stack:\n```xl\n' + e.stack + '```');
					});
				} else {
					msg.guild.createRole().then(newRole => {
						let rolePerms = newRole.permissions;

						/* Empty role permissions */
						for (var x in rolePerms.General) {
							rolePerms.General[x] = false;
						}
						for (var x in rolePerms.Text) {
							rolePerms.Text[x] = false;
						}
						for (var x in rolePerms.Voice) {
							rolePerms.Voice[x] = false;
						}

						let name = args.toUpperCase();
						let color = parseInt("0x" + args.replace(/#/, ""));

						newRole.commit(name, color, false, false).then(() => {}).catch(e => {
							msg.channel.sendMessage(':interrobang: Woah! Something went wrong while running that command!. Stack:\n```xl\n' + e.stack + '```');
						});

						msg.member.assignRole(newRole).then(() => {
							msg.channel.sendMessage(":ok_hand::skin-tone-1: Your color has been set to `" + args.toUpperCase() + "`. Note: if the color doesn't show up, that means the role has to be moved up in the list!");
						}).catch(e => {
							msg.channel.sendMessage(':interrobang: Woah! Something went wrong while running that command!. Stack:\n```xl\n' + e.stack + '```');
						});
					});
				}
			} else if (r === "DISABLED") {
				msg.channel.sendMessage(":raised_hand::skin-tone-1: Color roles are disabled on this server!");
			}
		}).catch(e => {
			console.log(er);
		})
	}
}

exports.commandsInModule = commandsInModule;
<commit_msg>Fixed message on color command<commit_after>"use strict";
let commandsInModule = [];
let unirest = require('unirest');
let request = require('request');

const auth = require("../../auth.json");
const pats = require("../lists/pats.js").pats;
const Permissions = require("../../databases/helpers/permissions.js");
const ServerSettings = require("../../databases/helpers/serversettings.js");

commandsInModule.pat = {
	name: 'pat', module: 'Fun',
	help: 'Pats a user!',
	usage: '[@user/user]',
	cooldown: 10, levelReq: 0,
	exec: function (Client, msg, args) {
		if (msg.mentions.length <= 0) {
			/* We need to fetch all the members to search in a broader list */
			Client.Users.fetchMembers();

			let userObj = Client.Users.getBy("username", args);

			if (!userObj) {
				msg.channel.sendMessage(":warning: User `" + args + "` doesn't seem to exist.");
				return;
			}

			let guildUserObj = userObj.memberOf(msg.guild);

			if (!guildUserObj) {
				msg.channel.sendMessage(":warning: User `" + args + "` doesn't seem to exist.");
				return;
			}

			msg.channel.sendMessage(guildUserObj.mention + " you have been patted by " + msg.author.mention + "\n" +
			pats[Math.floor(Math.random() * pats.length)]);
		} else {
			if (msg.mentions.length > 1) {
				msg.channel.sendMessage(":confounded: Sorry, I can only pat one user at a time.");
				return;
			}

			let guildUserObj = msg.mentions[0].memberOf(msg.guild);

			if (!guildUserObj) {
				msg.channel.sendMessage(":warning: User tagged is not a valid member of the guild!");
				return;
			}

			msg.channel.sendMessage(guildUserObj.mention + " you have been patted by " + msg.author.mention + "\n" +
			pats[Math.floor(Math.random() * pats.length)]);
		}
	}
}

commandsInModule.ud = {
	name: 'ud', module: 'Fun',
	help: 'Looks up a definition on Urban Dictionary.',
	usage: '[search terms]',
	cooldown: 30, levelReq: 0,
	exec: function (Client, msg, args) {
		if (!args) {
			msg.channel.sendMessage(":warning: No arguments specified!");
		}

		let definitionURL = "https://mashape-community-urban-dictionary.p.mashape.com/define?term=" + args.split(" ").join("+");

		unirest.get(definitionURL).header("X-Mashape-Key", auth.mashapeKey)
		.header("Accept", "text/plain")
		.end(function (result) {
			if (result.status === 200) {
				if (result.body) {
					let msgArray = [];
					let topResult = result.body.list[0];

					msgArray.push("__**Search terms**__: **" + args + "**");
					msgArray.push("__**Definition**__: " + topResult.definition);
					msgArray.push("__**Example**__:\n*" + topResult.example + "*");
					msgArray.push("__**Author**__: " + topResult.author);
					msgArray.push("__**Thumbs Up**__: " + topResult.thumbs_up + " | __Thumbs Down__: " + topResult.thumbs_down);
					msgArray.push("<" + topResult.permalink + ">");

					msg.channel.sendMessage(msgArray.join("\n"));
				}
			} else {
				msg.channel.sendMessage(":warning: Something went wrong while trying to get the definition, or it doesn't exist!");
			}
		});
	}
}

commandsInModule.roll = {
	name: 'roll', module: 'Fun',
	help: 'Roll a dice! If no dice type is specified, the default is 1d6.',
	usage: '[(number of rolls)d(sides of the dice)]',
	cooldown: 10, levelReq: 0,
	exec: function (Client, msg, args) {
		let arg = args.split(" ");
		let dice, urlReq, object;

		if (!/\d+d\d+/.test(arg[0])) {
			dice = "1d6";
		} else {
			dice = arg[0];
		}

		urlReq = `https://rolz.org/api/?${dice}.json`

		request(urlReq, {}, function(error, response, body) {
			if (error) {
				console.log(error);
				msg.channel.sendMessage(":warning: There was an error while processing that request. Details:\n```xl\n" + error + "```");
			}

			if (response.statusCode === 200) {
				object = JSON.parse(body);

				msg.channel.sendMessage(":game_die: You rolled a **" + object.result + "**!\n__Details__:" + object.details);
			}
		});
	}
}

commandsInModule.russianroulette = {
	name: 'russianroulette', module: 'Fun',
	help: 'Play russian roulette! If you lose, you get muted for 1 to 5 minutes, so be careful.',
	usage: '',
	cooldown: 120, levelReq: 0,
	exec: function (Client, msg, args) {
		if (!Client.User.permissionsFor(msg.guild).General.MANAGE_ROLES) {
			msg.channel.sendMessage(':sob: I do not have permission to manage roles in this server!');
			return;
		}

		let time = (Math.floor(Math.random() * 4) + 1) * 60 * 1000;
		let chance = parseInt(Math.floor(Math.random() * 2));

		if (chance === 1) {
			Permissions.getMuteRole(msg.guild).then(r => {
				let muteRole = msg.guild.roles.find(k => k.id === r);

				if (!muteRole) {
					msg.channel.sendMessage(':warning: No mute role has been set! Set it with `setmute [Role Name/@Role]`');
					return;
				}

				let member = msg.author.memberOf(msg.guild);

				member.assignRole(muteRole).then(() => {
					msg.channel.sendMessage(':gun: Member `' + member.username + '#' + member.discriminator + '` got shot while playing Russian Roulette! (muted for: **' + (time / 1000 / 60) + '** minutes).')
				}).catch(er => {
					console.log(er);
				});

				setTimeout(() => {member.unassignRole(muteRole)}, time);
			});
		} else {
			msg.channel.sendMessage(":relieved: The odds were with you this time!");
		}
	}
}

commandsInModule.color = {
	name: 'color', module: 'Fun',
	help: 'Set a custom color role for yourself!',
	usage: '[color hex]',
	cooldown: 300, levelReq: 1,
	exec: function (Client, msg, args) {
		ServerSettings.checkColors(msg.guild).then(r => {
			if (r === "ENABLED") {
				if (!/#[A-F0-9]{6}/i.test(args) || args.length > 7) {
					msg.reply("invalid color hex! Precede the code by a `#`. Only numbers and letters A through F are allowed. Example: `#B4A90F`");
					return;
				} else if (!args) {
					msg.reply("you did not specify a color!");
					return;
				}

				if (!Client.User.permissionsFor(msg.guild).General.MANAGE_ROLES) {
					msg.channel.sendMessage(':sob: I do not have permission to manage roles in this server!');
					return;
				}

				let findColorRole = msg.guild.roles.find(k => k.name === args.toUpperCase());

				if (findColorRole) {
					msg.member.assignRole(findColorRole).then(() => {
						msg.channel.sendMessage(":ok_hand::skin-tone-1: Your color has been set to `" + args.toUpperCase() + "`. Note: if the color doesn't show up, make sure that there are no other roles with colors on top of your color role.");
					}).catch(e => {
						msg.channel.sendMessage(':interrobang: Woah! Something went wrong while running that command!. Stack:\n```xl\n' + e.stack + '```');
					});
				} else {
					msg.guild.createRole().then(newRole => {
						let rolePerms = newRole.permissions;

						/* Empty role permissions */
						for (var x in rolePerms.General) {
							rolePerms.General[x] = false;
						}
						for (var x in rolePerms.Text) {
							rolePerms.Text[x] = false;
						}
						for (var x in rolePerms.Voice) {
							rolePerms.Voice[x] = false;
						}

						let name = args.toUpperCase();
						let color = parseInt("0x" + args.replace(/#/, ""));

						newRole.commit(name, color, false, false).then(() => {}).catch(e => {
							msg.channel.sendMessage(':interrobang: Woah! Something went wrong while running that command!. Stack:\n```xl\n' + e.stack + '```');
						});

						msg.member.assignRole(newRole).then(() => {
							msg.channel.sendMessage(":ok_hand::skin-tone-1: Your color has been set to `" + args.toUpperCase() + "`. Note: if the color doesn't show up, make sure that there are no other roles with colors on top of your color role.");
						}).catch(e => {
							msg.channel.sendMessage(':interrobang: Woah! Something went wrong while running that command!. Stack:\n```xl\n' + e.stack + '```');
						});
					});
				}
			} else if (r === "DISABLED") {
				msg.channel.sendMessage(":raised_hand::skin-tone-1: Color roles are disabled on this server!");
			}
		}).catch(e => {
			console.log(er);
		})
	}
}

exports.commandsInModule = commandsInModule;
<|endoftext|>