<commit_before>var gameMananger = function(game) {}

gameMananger.prototype = {
	init: function(){
		this.crown = null;
		highScore = 0;
		crowned = -1;
		players = [];
		this.timeCircle = null;
		this.gameTime = 10; //sec
		this.initialTime = 0;
		lastCrowned = 0;
		if (numberPlayers > 0) {
			this.game.world.scale.set((-1/24)*numberPlayers+7/12);
		}
		w2 = (this.game.world.width/2)/this.game.world.scale.x;
		h2 = (this.game.world.height/2)/this.game.world.scale.x;
		gameOver = false;
		graphics = this.game.add.graphics(w2, h2);
		muteAudio = false;
		paused = false;
		this.powerTimer = null;
		totalTime = 0;
		pauseTween = null;
	},

	create: function() {
		if (numberPlayers > 0) {
			this.game.stage.backgroundColor = bgColorsDark[chosenColor];
		} else {
			document.body.style.background = bgColorsDark[chosenColor];

		}

		//Choose snake locations
		for(var i=0; i <= numberPlayers; i++){
			players[i] = new Player(i,
			Math.round(Math.cos((2*Math.PI/(numberPlayers+1))*i)*500/this.game.world.scale.x)+w2,
			Math.round(Math.sin((2*Math.PI/(numberPlayers+1))*i)*250/this.game.world.scale.y)+h2,
			keys[i], this.game);
		}

		groupPowers = this.game.add.group();
		groupTrails = [];

		this.game.physics.startSystem(Phaser.Physics.ARCADE);
		this.game.physics.arcade.gravity.y = 0;

		for(var i=0; i <= numberPlayers; i++){
			players[i].create();
			players[i].player.rotation = ((2*Math.PI/(numberPlayers+1))*i);
		}

		for(var i=0; i <= numberPlayers; i++){
			for (var j = 0; j < groupTrails.length; j++) {
				if (groupTrails[j] != players[i].groupTrail) {
					players[i].enemyTrails.push(groupTrails[j]);
				}
			}
		}

		if(numberPlayers > 0){
			this.crown = this.game.add.sprite(w2, -32/this.game.world.scale.y, 'crown');
			this.crown.anchor.setTo(0.5,0.8);
			this.game.physics.enable(this.crown, Phaser.Physics.ARCADE);
		}

		if(numberPlayers > 0){
			graphics.lineStyle(0);
			graphics.beginFill(0x000000, 0.2);
			this.timeCircle = graphics.drawCircle(w2,h2,Math.sqrt(w2*w2+h2*h2)*2);
			this.timeCircle.pivot.x = w2;
			this.timeCircle.pivot.y = h2;
		} else {
			var textSize = 15;
	  	if (mobile) {
	  		textSize = 30
	  	}
			powerText = this.game.add.text(this.x, this.y, "1",
			{ font: "" + textSize + "px Arial Black",
	      fill: "#ffffff",
	      align: "center"
	  	});
	  	powerText.anchor.setTo(0.5,0.5);

		}

		this.initialTime = this.game.time.totalElapsedSeconds();

		//Generate powers
		if (numberPlayers > 0) {
			this.powerTimer = this.game.time.events.loop(Phaser.Timer.SECOND * 2, this.createPower, this);
		} else {
			this.createPower();
		}
		if(mobile){
			pauseSprite = this.game.add.button(w2, h2, 'pauseButton',this.touchePauseButton,this);
	    	pauseSprite.anchor.setTo(0.5, 0.5);
	    	pauseSprite.input.useHandCursor=true;
		}

		this.game.input.keyboard.addKey(Phaser.Keyboard.ESC).onDown.add(this.pause, this);
	},

	update: function() {
		if(!paused){
			totalTime += this.game.time.physicsElapsed;

			//Give crown
			if (crowned != -1) {
				if (Math.abs(this.crown.x - players[crowned].player.x) < 30 && Math.abs(this.crown.y - players[crowned].player.y) < 30) {
					players[crowned].addCrown();

					this.crown.x = players[crowned].player.x;
					this.crown.y = players[crowned].player.y;
					this.crown.visible = false;
				} else {
					this.game.physics.arcade.moveToObject(this.crown, players[crowned].player, 800);
					this.crown.visible = true;
				}
			}
			if(numberPlayers>0 && this.gameTime >= (totalTime)){
				this.timeCircle.scale.set((-1/this.gameTime)*(totalTime)+1);
			}
			else if(numberPlayers>0){
				this.endGame();
			}

			else if(players[0].dead){
				this.endGame();
			}

			if(muteAudio && !this.game.mute){
				audioButton.loadTexture('audiooff_button');
		        this.game.sound.mute = true;
			}
			else if(!muteAudio && this.game.mute){
				audioButton.loadTexture('audio_button');
		        this.game.sound.mute = false;
			}
		}

		//Update players
		for(var i=0; i <= numberPlayers; i++){
			players[i].update();
		}
	},

	createPower: function() {
		var powerup = new PowerUp(this.game);
		powerup.preload();
		powerup.create();
	},

	decreaseTimeBar: function(timeCut){
		this.timeBar.width=this.timeBar.width-timeCut;
	},

	endGame: function(){
		if(!gameOver){
			if (numberPlayers > 0) {
				this.game.time.events.remove(this.powerTimer);
			}
			for(var i = 0; i<players.length; i++){
				players[i].kill();
			}

		    mainMenu = this.game.add.button(w2, h2-97/this.game.world.scale.x,"exit_button",function(){this.game.state.start("Menu");},this);
		    mainMenu.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
			mainMenu.anchor.setTo(0.5,0.5);
			mainMenu.input.useHandCursor=true;

		  	restartButton = this.game.add.button(w2, h2+97/this.game.world.scale.x,"restart_button",function(){this.game.state.restart(true,false,numberPlayers);},this);
			restartButton.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
			restartButton.anchor.setTo(0.5,0.5);
			restartButton.input.useHandCursor=true;

			if(mobile){
				pauseSprite.alpha = 0;
				pauseSprite.input.useHandCursor=false;
			}

		  	if(numberPlayers > 0){
		  		scoreInMenu = this.game.add.text(w2, h2+256/this.game.world.scale.x, "player " + lastCrowned + " with: " + highScore, {
		        font: "40px Arial",
		        fill: "#ff0044",
		        align: "center"});
	    		scoreInMenu.anchor.setTo(0.5,0.5);
		  		scoreInMenu.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
		  	}

	    	if(numberPlayers == 0){
	    		scoreInMenu = this.game.add.text(w2, h2+256/this.game.world.scale.x, bestScore, {
		        font: "40px Arial",
		        fill: "#ff0044",
		        align: "center"});
	    		scoreInMenu.anchor.setTo(0.5,0.5);
		  		scoreInMenu.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
	    	}
		  	gameOver = true;
		}
	},

	pause: function() {
		if (!paused) { //pause
			if(pauseTween){
				pauseTween.stop();
			}
			paused = true;
			this.game.input.onDown.active = false;

			if(mobile){
				pauseSprite.alpha = 0;
				pauseSprite.callBack
			}

			if (numberPlayers > 0) {
				this.game.time.events.remove(this.powerTimer);
			}

	        // Then add the menu
	        menu = this.game.add.button(w2, h2-150/this.game.world.scale.x, 'resume_button',function(){this.pause();},this);
	        menu.anchor.setTo(0.5, 0.5);
	        menu.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
	        menu.input.useHandCursor=true;

	        restart = this.game.add.button(w2-150/this.game.world.scale.x, h2, 'restart_button',function(){this.game.state.restart();},this);
	        restart.anchor.setTo(0.5, 0.5);
	        restart.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
	        restart.input.useHandCursor=true;

	        exit = this.game.add.button(w2, h2+150/this.game.world.scale.x, 'exit_button',function(){this.game.state.start("Menu");},this);
	        exit.anchor.setTo(0.5, 0.5);
	        exit.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
	        exit.input.useHandCursor=true;

	        if(this.game.sound.mute){
		    	audioButton = this.game.add.button(w2+150/this.game.world.scale.x, h2,"audiooff_button",this.muteSound,this);
		  		audioButton.anchor.setTo(0.5,0.5);
		  		audioButton.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
		  		audioButton.input.useHandCursor=true;
		    }
		    else{
		        audioButton = this.game.add.button(w2+150/this.game.world.scale.x, h2,"audio_button",this.muteSound,this);
		        audioButton.anchor.setTo(0.5,0.5);
		        audioButton.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
		        audioButton.input.useHandCursor=true;
		    }

		}else { //unpause
			if (numberPlayers > 0) {
				this.powerTimer = this.game.time.events.loop(Phaser.Timer.SECOND * 2, this.createPower, this);
			}
			this.game.input.onDown.active = true;
			if(mobile){
				pauseSprite.alpha = 0.1;
				pauseSprite.input.useHandCursor=true;
			}
			menu.destroy();
            restart.destroy();
            exit.destroy();
            audioButton.destroy();
            paused = false;
		}

	},

	touchePauseButton: function(){
		if(!paused){
			this.pause();
			if(mobile){
				pauseSprite.input.useHandCursor=false;
			}
		}
	},

	componentToHex: function(c) {
	    var hex = c.toString(16);
	    return hex.length == 1 ? "0" + hex : hex;
	},

	muteSound: function(){
	    if(this.game.sound.mute){
		    audioButton.loadTexture('audio_button');
		    this.game.sound.mute = false;
	    }
	    else{
	        audioButton.loadTexture('audiooff_button');
	        this.game.sound.mute = true;
	    }
  	},

	render: function(){
		//players[0].render();
		//groupPowers.forEachAlive(this.renderGroup, this);
	},

	renderGroup: function(member) {
		this.game.debug.body(member);
	}
};<commit_msg>uma cena bue minima<commit_after>var gameMananger = function(game) {}

gameMananger.prototype = {
	init: function(){
		this.crown = null;
		highScore = 0;
		crowned = -1;
		players = [];
		this.timeCircle = null;
		this.gameTime = 10; //sec
		this.initialTime = 0;
		lastCrowned = 0;
		if (numberPlayers > 0) {
			this.game.world.scale.set((-1/24)*numberPlayers+7/12);
		}
		w2 = (this.game.world.width/2)/this.game.world.scale.x;
		h2 = (this.game.world.height/2)/this.game.world.scale.x;
		gameOver = false;
		graphics = this.game.add.graphics(w2, h2);
		muteAudio = false;
		paused = false;
		this.powerTimer = null;
		totalTime = 0;
		pauseTween = null;
	},

	create: function() {
		if (numberPlayers > 0) {
			this.game.stage.backgroundColor = bgColorsDark[chosenColor];
		} else {
			document.body.style.background = bgColorsDark[chosenColor];

		}

		if(mobile){
			pauseSprite = this.game.add.button(w2, h2, 'pauseButton',this.touchePauseButton,this);
	    	pauseSprite.anchor.setTo(0.5, 0.5);
	    	pauseSprite.input.useHandCursor=true;
		}

		//Choose snake locations
		for(var i=0; i <= numberPlayers; i++){
			players[i] = new Player(i,
			Math.round(Math.cos((2*Math.PI/(numberPlayers+1))*i)*500/this.game.world.scale.x)+w2,
			Math.round(Math.sin((2*Math.PI/(numberPlayers+1))*i)*250/this.game.world.scale.y)+h2,
			keys[i], this.game);
		}

		groupPowers = this.game.add.group();
		groupTrails = [];

		this.game.physics.startSystem(Phaser.Physics.ARCADE);
		this.game.physics.arcade.gravity.y = 0;

		for(var i=0; i <= numberPlayers; i++){
			players[i].create();
			players[i].player.rotation = ((2*Math.PI/(numberPlayers+1))*i);
		}

		for(var i=0; i <= numberPlayers; i++){
			for (var j = 0; j < groupTrails.length; j++) {
				if (groupTrails[j] != players[i].groupTrail) {
					players[i].enemyTrails.push(groupTrails[j]);
				}
			}
		}

		if(numberPlayers > 0){
			this.crown = this.game.add.sprite(w2, -32/this.game.world.scale.y, 'crown');
			this.crown.anchor.setTo(0.5,0.8);
			this.game.physics.enable(this.crown, Phaser.Physics.ARCADE);
		}

		if(numberPlayers > 0){
			graphics.lineStyle(0);
			graphics.beginFill(0x000000, 0.2);
			this.timeCircle = graphics.drawCircle(w2,h2,Math.sqrt(w2*w2+h2*h2)*2);
			this.timeCircle.pivot.x = w2;
			this.timeCircle.pivot.y = h2;
		} else {
			var textSize = 15;
	  	if (mobile) {
	  		textSize = 30
	  	}
			powerText = this.game.add.text(this.x, this.y, "1",
			{ font: "" + textSize + "px Arial Black",
	      fill: "#ffffff",
	      align: "center"
	  	});
	  	powerText.anchor.setTo(0.5,0.5);

		}

		this.initialTime = this.game.time.totalElapsedSeconds();

		//Generate powers
		if (numberPlayers > 0) {
			this.powerTimer = this.game.time.events.loop(Phaser.Timer.SECOND * 2, this.createPower, this);
		} else {
			this.createPower();
		}

		this.game.input.keyboard.addKey(Phaser.Keyboard.ESC).onDown.add(this.pause, this);
	},

	update: function() {
		if(!paused){
			totalTime += this.game.time.physicsElapsed;

			//Give crown
			if (crowned != -1) {
				if (Math.abs(this.crown.x - players[crowned].player.x) < 30 && Math.abs(this.crown.y - players[crowned].player.y) < 30) {
					players[crowned].addCrown();

					this.crown.x = players[crowned].player.x;
					this.crown.y = players[crowned].player.y;
					this.crown.visible = false;
				} else {
					this.game.physics.arcade.moveToObject(this.crown, players[crowned].player, 800);
					this.crown.visible = true;
				}
			}
			if(numberPlayers>0 && this.gameTime >= (totalTime)){
				this.timeCircle.scale.set((-1/this.gameTime)*(totalTime)+1);
			}
			else if(numberPlayers>0){
				this.endGame();
			}

			else if(players[0].dead){
				this.endGame();
			}

			if(muteAudio && !this.game.mute){
				audioButton.loadTexture('audiooff_button');
		        this.game.sound.mute = true;
			}
			else if(!muteAudio && this.game.mute){
				audioButton.loadTexture('audio_button');
		        this.game.sound.mute = false;
			}
		}

		//Update players
		for(var i=0; i <= numberPlayers; i++){
			players[i].update();
		}
	},

	createPower: function() {
		var powerup = new PowerUp(this.game);
		powerup.preload();
		powerup.create();
	},

	decreaseTimeBar: function(timeCut){
		this.timeBar.width=this.timeBar.width-timeCut;
	},

	endGame: function(){
		if(!gameOver){
			if (numberPlayers > 0) {
				this.game.time.events.remove(this.powerTimer);
			}
			for(var i = 0; i<players.length; i++){
				players[i].kill();
			}

		    mainMenu = this.game.add.button(w2, h2-97/this.game.world.scale.x,"exit_button",function(){this.game.state.start("Menu");},this);
		    mainMenu.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
			mainMenu.anchor.setTo(0.5,0.5);
			mainMenu.input.useHandCursor=true;

		  	restartButton = this.game.add.button(w2, h2+97/this.game.world.scale.x,"restart_button",function(){this.game.state.restart(true,false,numberPlayers);},this);
			restartButton.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
			restartButton.anchor.setTo(0.5,0.5);
			restartButton.input.useHandCursor=true;

			if(mobile){
				pauseSprite.alpha = 0;
				pauseSprite.input.useHandCursor=false;
			}

		  	if(numberPlayers > 0){
		  		scoreInMenu = this.game.add.text(w2, h2+256/this.game.world.scale.x, "player " + lastCrowned + " with: " + highScore, {
		        font: "40px Arial",
		        fill: "#ff0044",
		        align: "center"});
	    		scoreInMenu.anchor.setTo(0.5,0.5);
		  		scoreInMenu.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
		  	}

	    	if(numberPlayers == 0){
	    		scoreInMenu = this.game.add.text(w2, h2+256/this.game.world.scale.x, bestScore, {
		        font: "40px Arial",
		        fill: "#ff0044",
		        align: "center"});
	    		scoreInMenu.anchor.setTo(0.5,0.5);
		  		scoreInMenu.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
	    	}
		  	gameOver = true;
		}
	},

	pause: function() {
		if (!paused) { //pause
			if(pauseTween){
				pauseTween.stop();
			}
			paused = true;
			this.game.input.onDown.active = false;

			if(mobile){
				pauseSprite.alpha = 0;
				pauseSprite.callBack
			}

			if (numberPlayers > 0) {
				this.game.time.events.remove(this.powerTimer);
			}

	        // Then add the menu
	        menu = this.game.add.button(w2, h2-150/this.game.world.scale.x, 'resume_button',function(){this.pause();},this);
	        menu.anchor.setTo(0.5, 0.5);
	        menu.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
	        menu.input.useHandCursor=true;

	        restart = this.game.add.button(w2-150/this.game.world.scale.x, h2, 'restart_button',function(){this.game.state.restart();},this);
	        restart.anchor.setTo(0.5, 0.5);
	        restart.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
	        restart.input.useHandCursor=true;

	        exit = this.game.add.button(w2, h2+150/this.game.world.scale.x, 'exit_button',function(){this.game.state.start("Menu");},this);
	        exit.anchor.setTo(0.5, 0.5);
	        exit.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
	        exit.input.useHandCursor=true;

	        if(this.game.sound.mute){
		    	audioButton = this.game.add.button(w2+150/this.game.world.scale.x, h2,"audiooff_button",this.muteSound,this);
		  		audioButton.anchor.setTo(0.5,0.5);
		  		audioButton.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
		  		audioButton.input.useHandCursor=true;
		    }
		    else{
		        audioButton = this.game.add.button(w2+150/this.game.world.scale.x, h2,"audio_button",this.muteSound,this);
		        audioButton.anchor.setTo(0.5,0.5);
		        audioButton.scale.set(1/this.game.world.scale.x,1/this.game.world.scale.x);
		        audioButton.input.useHandCursor=true;
		    }

		}else { //unpause
			if (numberPlayers > 0) {
				this.powerTimer = this.game.time.events.loop(Phaser.Timer.SECOND * 2, this.createPower, this);
			}
			this.game.input.onDown.active = true;
			if(mobile){
				pauseSprite.alpha = 0.1;
				pauseSprite.input.useHandCursor=true;
			}
			menu.destroy();
            restart.destroy();
            exit.destroy();
            audioButton.destroy();
            paused = false;
		}

	},

	touchePauseButton: function(){
		if(!paused){
			this.pause();
			if(mobile){
				pauseSprite.input.useHandCursor=false;
			}
		}
	},

	componentToHex: function(c) {
	    var hex = c.toString(16);
	    return hex.length == 1 ? "0" + hex : hex;
	},

	muteSound: function(){
	    if(this.game.sound.mute){
		    audioButton.loadTexture('audio_button');
		    this.game.sound.mute = false;
	    }
	    else{
	        audioButton.loadTexture('audiooff_button');
	        this.game.sound.mute = true;
	    }
  	},

	render: function(){
		//players[0].render();
		//groupPowers.forEachAlive(this.renderGroup, this);
	},

	renderGroup: function(member) {
		this.game.debug.body(member);
	}
};<|endoftext|>


<commit_before>import _ from 'lodash';
import fetch from 'isomorphic-fetch';
// We have to remove node_modules/react to avoid having multiple copies loaded.
// eslint-disable-next-line import/no-unresolved
import React, { PropTypes } from 'react';
import Match from 'react-router/Match';

import Pane from '@folio/stripes-components/lib/Pane';
import Paneset from '@folio/stripes-components/lib/Paneset';
import PaneMenu from '@folio/stripes-components/lib/PaneMenu';
import Button from '@folio/stripes-components/lib/Button';
import Icon from '@folio/stripes-components/lib/Icon';
import MultiColumnList from '@folio/stripes-components/lib/MultiColumnList';
import FilterPaneSearch from '@folio/stripes-components/lib/FilterPaneSearch';
import FilterControlGroup from '@folio/stripes-components/lib/FilterControlGroup';
import Layer from '@folio/stripes-components/lib/Layer';

import FilterGroups, { initialFilterState, filters2cql, onChangeFilter } from '@folio/stripes-components/lib/FilterGroups';

import UserForm from './UserForm';
import ViewUser from './ViewUser';

const filterConfig = [
  {
    label: 'Filters',
    name: 'active',
    cql: 'active',
    values: [
      { name: 'Active', cql: 'true' },
      { name: 'Inactive', cql: 'false' },
    ],
  },
];

class Users extends React.Component {
  static contextTypes = {
    router: PropTypes.object.isRequired,
    store: PropTypes.object,
  };

  static propTypes = {
    data: PropTypes.object.isRequired,
    pathname: PropTypes.string.isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
      query: PropTypes.object, // object of key=value pairs
      search: PropTypes.string, // string combining all parts of query
    }).isRequired,
    mutator: PropTypes.object,
  };

  static manifest = Object.freeze({
    addUserMode: { },
    users: {
      type: 'okapi',
      records: 'users',
      path: (queryParams, _pathComponents, _resourceValues) => {
        // console.log('Users manifest "users" path function, queryParams = ', queryParams);
        const { query, filters, sort } = queryParams || {};

        let cql;
        if (query) {
          cql = `username="${query}*" or personal.first_name="${query}*" or personal.last_name="${query}*"`;
        }

        const filterCql = filters2cql(filterConfig, filters);
        if (filterCql) {
          if (cql) {
            cql = `(${cql}) and ${filterCql}`;
          } else {
            cql = filterCql;
          }
        }

        if (sort) {
          const sortMap = {
            Active: 'active',
            Name: 'personal.last_name personal.first_name',
            Username: 'username',
            Email: 'personal.email',
          };
          const sortIndex = sortMap[sort];
          if (sortIndex) {
            if (cql === undefined) cql = 'username=*';
            cql += ` sortby ${sortIndex}`;
          }
        }

        let path = 'users';
        if (cql) path += `?query=${encodeURIComponent(cql)}`;

        console.log(`query=${query} filters=${filters} sort=${sort} -> ${path}`);
        return path;
      },
      staticFallback: { path: 'users' },
    },
  });

  constructor(props, context) {
    super(props);

    const query = props.location.query || {};
    this.state = {
      filters: initialFilterState(filterConfig, query.filters),
      selectedItem: {},
      searchTerm: query.query || '',
      sortOrder: query.sort || '',
    };
    props.mutator.addUserMode.replace({ mode: false });

    this.okapi = context.store.getState().okapi;

    this.onClickAddNewUser = this.onClickAddNewUser.bind(this);
    this.onClickCloseNewUser = this.onClickCloseNewUser.bind(this);
    this.onChangeFilter = onChangeFilter.bind(this);
    // this.performSearch = _.debounce(this.performSearch.bind(this), 250);
    this.performSearch = this.performSearch.bind(this); // For now, prefer instant response
    this.onChangeSearch = this.onChangeSearch.bind(this);
    this.onClearSearch = this.onClearSearch.bind(this);
    this.onSort = this.onSort.bind(this);
    this.onSelectRow = this.onSelectRow.bind(this);
  }

  // search Handlers...
  onClearSearch() {
    console.log('User cleared search');
    this.setState({ searchTerm: '' });
    this.context.router.transitionTo(this.props.location.pathname);
  }

  onSort(e, meta) {
    const sortOrder = meta.name;
    console.log('User sorted by', sortOrder);
    this.setState({ sortOrder });
    this.updateSearch(this.state.searchTerm, sortOrder, this.state.filters);
  }

  onSelectRow(e, meta) {
    const userId = meta.id;
    const username = meta.username;
    console.log('User clicked', userId, 'location = ', this.props.location);
    this.setState({ selectedItem: meta });
    this.context.router.transitionTo(`/users/view/${userId}/${username}${this.props.location.search}`);
  }

  // end search Handlers

  // AddUser Handlers
  onClickAddNewUser(e) {
    if (e) e.preventDefault();
    this.props.mutator.addUserMode.replace({ mode: true });
  }

  onClickCloseNewUser(e) {
    if (e) e.preventDefault();
    this.props.mutator.addUserMode.replace({ mode: false });
  }
  // end AddUser Handlers

  onChangeSearch(e) {
    const term = e.target.value;
    this.setState({ searchTerm: term });
    this.performSearch(term);
  }

  performSearch(term) {
    console.log('User searched:', term, 'at', this.props.location.pathname);
    this.updateSearch(term, this.state.sortOrder, this.state.filters);
  }

  updateFilters(filters) { // provided for onChangeFilter
    this.updateSearch(this.state.searchTerm, this.state.sortOrder, filters);
  }

  // We need to explicitly pass changed values into this function,
  // as state-change only happens after event is handled.
  updateSearch(query, sortOrder, filters) {
    console.log(`updateSearch('${query}', '${sortOrder}',`, filters, ')');
    let transitionLoc = this.props.location.pathname;
    const params = {};
    if (query) params.query = query;
    if (sortOrder) params.sort = sortOrder;

    const activeFilters = [];
    for (const name in filters) {
      if (filters[name]) activeFilters.push(name);
    }

    if (activeFilters) params.filters = activeFilters.join(',');

    const keys = Object.keys(params);
    if (keys.length) {
      // eslint-disable-next-line prefer-template
      transitionLoc += '?' + keys.map(key => `${key}=${encodeURIComponent(params[key])}`).join('&');
    }
    this.context.router.transitionTo(transitionLoc);
  }

  create(data) {
    // extract creds object from user object
    const creds = Object.assign({}, data.creds, { username: data.username });
    if (data.creds) delete data.creds; // eslint-disable-line no-param-reassign
    // POST user record
    const p = this.props.mutator.users.POST(data);
    console.log('got promise', p);
    p.then((x) => {
      console.log('POST promise was OK:', x);
    }).catch((x) => {
      console.log('POST promise failed:', x);
    });
    // POST credentials, permission-user, permissions;
    this.postCreds(data.username, { credentials: creds });
    this.onClickCloseNewUser();
  }

  postCreds(username, creds) {
    fetch(`${this.okapi.url}/authn/users`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token }),
      body: JSON.stringify(creds),
    }).then((response) => {
      if (response.status >= 400) {
        console.log('Users. POST of creds failed.');
      } else {
        this.postPerms(username, 'users.super');
      }
    });
  }

  postPerms(username, perms) {
    fetch(`${this.okapi.url}/perms/users`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token }),
      body: username,
    }).then((response) => {
      if (response.status >= 400) {
        console.log('Users. POST of username failed.');
      } else {
        this.postUsersPerms(username, perms);
      }
    });
  }

  postUsersPerms(username, perm) {
    fetch(`${this.okapi.url}/perms/users/${username}/permissions`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token }),
      body: JSON.stringify({ permission_name: perm }),
    }).then((response) => {
      if (response.status >= 400) {
        console.log("Users. POST of user's perms failed.");
      } else {
        // nothing to do
      }
    });
  }

  render() {
    const { data, pathname } = this.props;
    const users = data.users || [];

    /* searchHeader is a 'custom pane header'*/
    const searchHeader = <FilterPaneSearch id="SearchField" onChange={this.onChangeSearch} onClear={this.onClearSearch} value={this.state.searchTerm} />;
    const resultMenu = <PaneMenu><button><Icon icon="bookmark" /></button></PaneMenu>;

    const resultsFormatter = {
      Active: user => user.active,
      Name: user => `${_.get(user, ['personal', 'last_name'], '')}, ${_.get(user, ['personal', 'first_name'], '')}`,
      Username: user => user.username,
      Email: user => _.get(user, ['personal', 'email']),
    };

    return (
      <Paneset>
        {/* Filter Pane */}
        <Pane defaultWidth="16%" header={searchHeader}>
          <FilterGroups config={filterConfig} filters={this.state.filters} onChangeFilter={this.onChangeFilter} />
          <FilterControlGroup label="Actions">
            <Button fullWidth onClick={this.onClickAddNewUser}>New user</Button>
          </FilterControlGroup>
        </Pane>
        {/* Results Pane */}
        <Pane
          defaultWidth="40%"
          paneTitle={

              Results

                {users.length} Result{users.length === 1 ? '' : 's'} Found

          }
          lastMenu={resultMenu}
        >
          <MultiColumnList
            contentData={users}
            selectedRow={this.state.selectedItem}
            rowMetadata={['id', 'username']}
            formatter={resultsFormatter}
            onRowClick={this.onSelectRow}
            onHeaderClick={this.onSort}
            visibleColumns={['Active', 'Name', 'Username', 'Email']}
            fullWidth
            sortOrder={this.state.sortOrder}
            isEmptyMessage={`No results found for "${this.state.searchTerm}". Please check your spelling and filters.`}
          />
        </Pane>

        {/* Details Pane */}
        <Match pattern={`${pathname}/view/:userid/:username`} render={props => <ViewUser placeholder={'placeholder'} {...props} />} />
        <Layer isOpen={data.addUserMode ? data.addUserMode.mode : false} label="Add New User Dialog">
          <UserForm
            onSubmit={(record) => { this.create(record); }}
            onCancel={this.onClickCloseNewUser}
          />
        </Layer>
      </Paneset>
    );
  }
}

export default Users;
<commit_msg>Add transitionToParams method; rewrite search and sort handlers to use it. Part of STRIPES-215.<commit_after>import _ from 'lodash';
import fetch from 'isomorphic-fetch';
// We have to remove node_modules/react to avoid having multiple copies loaded.
// eslint-disable-next-line import/no-unresolved
import React, { PropTypes } from 'react';
import Match from 'react-router/Match';

import Pane from '@folio/stripes-components/lib/Pane';
import Paneset from '@folio/stripes-components/lib/Paneset';
import PaneMenu from '@folio/stripes-components/lib/PaneMenu';
import Button from '@folio/stripes-components/lib/Button';
import Icon from '@folio/stripes-components/lib/Icon';
import MultiColumnList from '@folio/stripes-components/lib/MultiColumnList';
import FilterPaneSearch from '@folio/stripes-components/lib/FilterPaneSearch';
import FilterControlGroup from '@folio/stripes-components/lib/FilterControlGroup';
import Layer from '@folio/stripes-components/lib/Layer';

import FilterGroups, { initialFilterState, filters2cql, onChangeFilter } from '@folio/stripes-components/lib/FilterGroups';

import UserForm from './UserForm';
import ViewUser from './ViewUser';

const filterConfig = [
  {
    label: 'Filters',
    name: 'active',
    cql: 'active',
    values: [
      { name: 'Active', cql: 'true' },
      { name: 'Inactive', cql: 'false' },
    ],
  },
];

class Users extends React.Component {
  static contextTypes = {
    router: PropTypes.object.isRequired,
    store: PropTypes.object,
  };

  static propTypes = {
    data: PropTypes.object.isRequired,
    pathname: PropTypes.string.isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
      query: PropTypes.object, // object of key=value pairs
      search: PropTypes.string, // string combining all parts of query
    }).isRequired,
    mutator: PropTypes.object,
  };

  static manifest = Object.freeze({
    addUserMode: { },
    users: {
      type: 'okapi',
      records: 'users',
      path: (queryParams, _pathComponents, _resourceValues) => {
        // console.log('Users manifest "users" path function, queryParams = ', queryParams);
        const { query, filters, sort } = queryParams || {};

        let cql;
        if (query) {
          cql = `username="${query}*" or personal.first_name="${query}*" or personal.last_name="${query}*"`;
        }

        const filterCql = filters2cql(filterConfig, filters);
        if (filterCql) {
          if (cql) {
            cql = `(${cql}) and ${filterCql}`;
          } else {
            cql = filterCql;
          }
        }

        if (sort) {
          const sortMap = {
            Active: 'active',
            Name: 'personal.last_name personal.first_name',
            Username: 'username',
            Email: 'personal.email',
          };
          const sortIndex = sortMap[sort];
          if (sortIndex) {
            if (cql === undefined) cql = 'username=*';
            cql += ` sortby ${sortIndex}`;
          }
        }

        let path = 'users';
        if (cql) path += `?query=${encodeURIComponent(cql)}`;

        console.log(`query=${query} filters=${filters} sort=${sort} -> ${path}`);
        return path;
      },
      staticFallback: { path: 'users' },
    },
  });

  constructor(props, context) {
    super(props);

    const query = props.location.query || {};
    this.state = {
      filters: initialFilterState(filterConfig, query.filters),
      selectedItem: {},
      searchTerm: query.query || '',
      sortOrder: query.sort || '',
    };
    props.mutator.addUserMode.replace({ mode: false });

    this.okapi = context.store.getState().okapi;

    this.onClickAddNewUser = this.onClickAddNewUser.bind(this);
    this.onClickCloseNewUser = this.onClickCloseNewUser.bind(this);
    this.onChangeFilter = onChangeFilter.bind(this);
    // this.performSearch = _.debounce(this.performSearch.bind(this), 250);
    this.performSearch = this.performSearch.bind(this); // For now, prefer instant response
    this.onChangeSearch = this.onChangeSearch.bind(this);
    this.onClearSearch = this.onClearSearch.bind(this);
    this.onSort = this.onSort.bind(this);
    this.onSelectRow = this.onSelectRow.bind(this);
  }

  // search Handlers...
  onClearSearch() {
    console.log('User cleared search');
    this.setState({ searchTerm: '' });
    this.context.router.transitionTo(this.props.location.pathname);
  }

  onSort(e, meta) {
    const sortOrder = meta.name;
    console.log('User sorted by', sortOrder);
    this.setState({ sortOrder });
    this.transitionToParams({ 'sort': sortOrder });
  }

  onSelectRow(e, meta) {
    const userId = meta.id;
    const username = meta.username;
    console.log('User clicked', userId, 'location = ', this.props.location);
    this.setState({ selectedItem: meta });
    this.context.router.transitionTo(`/users/view/${userId}/${username}${this.props.location.search}`);
  }

  // end search Handlers

  // AddUser Handlers
  onClickAddNewUser(e) {
    if (e) e.preventDefault();
    this.props.mutator.addUserMode.replace({ mode: true });
  }

  onClickCloseNewUser(e) {
    if (e) e.preventDefault();
    this.props.mutator.addUserMode.replace({ mode: false });
  }
  // end AddUser Handlers

  onChangeSearch(e) {
    const term = e.target.value;
    this.setState({ searchTerm: term });
    this.performSearch(term);
  }

  performSearch(term) {
    console.log('User searched:', term, 'at', this.props.location.pathname);
    this.transitionToParams({ 'query': term });
  }

  updateFilters(filters) { // provided for onChangeFilter
    this.updateSearch(this.state.searchTerm, this.state.sortOrder, filters);
  }

  // We need to explicitly pass changed values into this function,
  // as state-change only happens after event is handled.
  updateSearch(query, sortOrder, filters) {
    console.log(`updateSearch('${query}', '${sortOrder}',`, filters, ')');
    let transitionLoc = this.props.location.pathname;
    const params = {};
    if (query) params.query = query;
    if (sortOrder) params.sort = sortOrder;

    const activeFilters = [];
    for (const name in filters) {
      if (filters[name]) activeFilters.push(name);
    }

    if (activeFilters) params.filters = activeFilters.join(',');

    const keys = Object.keys(params);
    if (keys.length) {
      // eslint-disable-next-line prefer-template
      transitionLoc += '?' + keys.map(key => `${key}=${encodeURIComponent(params[key])}`).join('&');
    }
    this.context.router.transitionTo(transitionLoc);
  }

  transitionToParams(params) {
    const location = this.props.location;
    const router = this.context.router;
    const allParams = Object.assign({}, location.query, params);

    let url = location.pathname;
    const keys = Object.keys(allParams);
    if (keys.length) {
      // eslint-disable-next-line prefer-template
      url += '?' + keys.map(key => `${key}=${encodeURIComponent(allParams[key])}`).join('&');
    }

    router.transitionTo(url);
  }

  create(data) {
    // extract creds object from user object
    const creds = Object.assign({}, data.creds, { username: data.username });
    if (data.creds) delete data.creds; // eslint-disable-line no-param-reassign
    // POST user record
    const p = this.props.mutator.users.POST(data);
    console.log('got promise', p);
    p.then((x) => {
      console.log('POST promise was OK:', x);
    }).catch((x) => {
      console.log('POST promise failed:', x);
    });
    // POST credentials, permission-user, permissions;
    this.postCreds(data.username, { credentials: creds });
    this.onClickCloseNewUser();
  }

  postCreds(username, creds) {
    fetch(`${this.okapi.url}/authn/users`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token }),
      body: JSON.stringify(creds),
    }).then((response) => {
      if (response.status >= 400) {
        console.log('Users. POST of creds failed.');
      } else {
        this.postPerms(username, 'users.super');
      }
    });
  }

  postPerms(username, perms) {
    fetch(`${this.okapi.url}/perms/users`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token }),
      body: username,
    }).then((response) => {
      if (response.status >= 400) {
        console.log('Users. POST of username failed.');
      } else {
        this.postUsersPerms(username, perms);
      }
    });
  }

  postUsersPerms(username, perm) {
    fetch(`${this.okapi.url}/perms/users/${username}/permissions`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token }),
      body: JSON.stringify({ permission_name: perm }),
    }).then((response) => {
      if (response.status >= 400) {
        console.log("Users. POST of user's perms failed.");
      } else {
        // nothing to do
      }
    });
  }

  render() {
    const { data, pathname } = this.props;
    const users = data.users || [];

    /* searchHeader is a 'custom pane header'*/
    const searchHeader = <FilterPaneSearch id="SearchField" onChange={this.onChangeSearch} onClear={this.onClearSearch} value={this.state.searchTerm} />;
    const resultMenu = <PaneMenu><button><Icon icon="bookmark" /></button></PaneMenu>;

    const resultsFormatter = {
      Active: user => user.active,
      Name: user => `${_.get(user, ['personal', 'last_name'], '')}, ${_.get(user, ['personal', 'first_name'], '')}`,
      Username: user => user.username,
      Email: user => _.get(user, ['personal', 'email']),
    };

    return (
      <Paneset>
        {/* Filter Pane */}
        <Pane defaultWidth="16%" header={searchHeader}>
          <FilterGroups config={filterConfig} filters={this.state.filters} onChangeFilter={this.onChangeFilter} />
          <FilterControlGroup label="Actions">
            <Button fullWidth onClick={this.onClickAddNewUser}>New user</Button>
          </FilterControlGroup>
        </Pane>
        {/* Results Pane */}
        <Pane
          defaultWidth="40%"
          paneTitle={

              Results

                {users.length} Result{users.length === 1 ? '' : 's'} Found

          }
          lastMenu={resultMenu}
        >
          <MultiColumnList
            contentData={users}
            selectedRow={this.state.selectedItem}
            rowMetadata={['id', 'username']}
            formatter={resultsFormatter}
            onRowClick={this.onSelectRow}
            onHeaderClick={this.onSort}
            visibleColumns={['Active', 'Name', 'Username', 'Email']}
            fullWidth
            sortOrder={this.state.sortOrder}
            isEmptyMessage={`No results found for "${this.state.searchTerm}". Please check your spelling and filters.`}
          />
        </Pane>

        {/* Details Pane */}
        <Match pattern={`${pathname}/view/:userid/:username`} render={props => <ViewUser placeholder={'placeholder'} {...props} />} />
        <Layer isOpen={data.addUserMode ? data.addUserMode.mode : false} label="Add New User Dialog">
          <UserForm
            onSubmit={(record) => { this.create(record); }}
            onCancel={this.onClickCloseNewUser}
          />
        </Layer>
      </Paneset>
    );
  }
}

export default Users;
<|endoftext|>


<commit_before>import _ from 'lodash';
// We have to remove node_modules/react to avoid having multiple copies loaded.
// eslint-disable-next-line import/no-unresolved
import React, { PropTypes } from 'react';
import Route from 'react-router-dom/Route';
import queryString from 'query-string';
import fetch from 'isomorphic-fetch';

import Pane from '@folio/stripes-components/lib/Pane';
import Paneset from '@folio/stripes-components/lib/Paneset';
import PaneMenu from '@folio/stripes-components/lib/PaneMenu';
import Button from '@folio/stripes-components/lib/Button';
import Icon from '@folio/stripes-components/lib/Icon';
import MultiColumnList from '@folio/stripes-components/lib/MultiColumnList';
import FilterPaneSearch from '@folio/stripes-components/lib/FilterPaneSearch';
import FilterControlGroup from '@folio/stripes-components/lib/FilterControlGroup';
import Layer from '@folio/stripes-components/lib/Layer';
import FilterGroups, { initialFilterState, onChangeFilter } from '@folio/stripes-components/lib/FilterGroups';
import transitionToParams from '@folio/stripes-components/util/transitionToParams';
import makeQueryFunction from '@folio/stripes-components/util/makeQueryFunction';
import IfPermission from '@folio/stripes-components/lib/IfPermission';

import UserForm from './UserForm';
import ViewUser from './ViewUser';

const filterConfig = [
  {
    label: 'User status',
    name: 'active',
    cql: 'active',
    values: [
      { name: 'Active', cql: 'true' },
      { name: 'Inactive', cql: 'false' },
    ],
  },
  {
    label: 'Patron group',
    name: 'pg',
    cql: 'patron_group',
    values: [
      { name: 'On-campus', cql: 'on_campus' },
      { name: 'Off-campus', cql: 'off_campus' },
      { name: 'Other', cql: 'other' },
    ],
  },
];

class Users extends React.Component {
  static contextTypes = {
    store: PropTypes.object,
  };

  static propTypes = {
    stripes: PropTypes.shape({
      logger: PropTypes.shape({
        log: PropTypes.func.isRequired,
      }).isRequired,
      connect: PropTypes.func.isRequired,
    }).isRequired,
    data: PropTypes.object.isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
      search: PropTypes.string,
    }).isRequired,
    match: PropTypes.shape({
      path: PropTypes.string.isRequired,
    }).isRequired,
    mutator: PropTypes.shape({
      addUserMode: PropTypes.shape({
        replace: PropTypes.func,
      }),
      users: PropTypes.shape({
        POST: PropTypes.func,
      }),
    }).isRequired,
  };

  static manifest = Object.freeze({
    addUserMode: {},
    users: {
      type: 'okapi',
      records: 'users',
      recordsRequired: 30,
      perRequest: 10,
      path: 'users',
      GET: {
        params: {
          query: makeQueryFunction(
            'username=*',
            'username="$QUERY*" or personal.first_name="$QUERY*" or personal.last_name="$QUERY*"',
            {
              Active: 'active',
              Name: 'personal.last_name personal.first_name',
              'Patron Group': 'patron_group',
              'User ID': 'username',
              Email: 'personal.email',
            },
            filterConfig,
          ),
        },
        staticFallback: { params: {} },
      },
    },
    patronGroups: {
      type: 'okapi',
      path: 'groups',
      records: 'usergroups',
    },
  });

  constructor(props, context) {
    super(props);

    const query = props.location.search ? queryString.parse(props.location.search) : {};
    this.state = {
      filters: initialFilterState(filterConfig, query.filters),
      selectedItem: {},
      searchTerm: query.query || '',
      sortOrder: query.sort || '',
    };

    this.okapi = context.store.getState().okapi;

    this.onClearSearch = this.onClearSearch.bind(this);
    this.onSort = this.onSort.bind(this);
    this.onSelectRow = this.onSelectRow.bind(this);
    this.onClickAddNewUser = this.onClickAddNewUser.bind(this);
    this.onClickCloseNewUser = this.onClickCloseNewUser.bind(this);
    this.onChangeSearch = this.onChangeSearch.bind(this);
    this.performSearch = this.performSearch.bind(this); // For now, prefer instant response
    // this.performSearch = _.debounce(this.performSearch.bind(this), 250);

    this.onChangeFilter = onChangeFilter.bind(this);
    this.transitionToParams = transitionToParams.bind(this);

    this.collapseDetails = this.collapseDetails.bind(this);
    this.connectedViewUser = props.stripes.connect(ViewUser);
    const logger = props.stripes.logger;
    this.log = logger.log.bind(logger);
  }

  componentWillMount() {
    if (_.isEmpty(this.props.data.addUserMode)) this.props.mutator.addUserMode.replace({ mode: false });
  }

  onClearSearch() {
    this.log('action', 'cleared search');
    this.setState({ searchTerm: '' });
    this.props.history.push(this.props.location.pathname);
  }

  onSort(e, meta) {
    const sortOrder = meta.alias;
    this.log('action', `sorted by ${sortOrder}`);
    this.setState({ sortOrder });
    this.transitionToParams({ sort: sortOrder });
  }

  onSelectRow(e, meta) {
    const userId = meta.id;
    const username = meta.username;
    this.log('action', `clicked ${userId}, selected user =`, meta);
    this.setState({ selectedItem: meta });
    this.props.history.push(`/users/view/${userId}/${username}${this.props.location.search}`);
  }

  onClickAddNewUser(e) {
    if (e) e.preventDefault();
    this.log('action', 'clicked "add new user"');
    this.props.mutator.addUserMode.replace({ mode: true });
  }

  onClickCloseNewUser(e) {
    if (e) e.preventDefault();
    this.log('action', 'clicked "close new user"');
    this.props.mutator.addUserMode.replace({ mode: false });
  }

  onChangeSearch(e) {
    const query = e.target.value;
    this.setState({ searchTerm: query });
    this.performSearch(query);
  }

  performSearch(query) {
    this.log('action', `searched for '${query}'`);
    this.transitionToParams({ query });
  }

  updateFilters(filters) { // provided for onChangeFilter
    this.transitionToParams({ filters: Object.keys(filters).filter(key => filters[key]).join(',') });
  }

  create(data) {
    // extract creds object from user object
    const creds = Object.assign({}, data.creds, { username: data.username });
    if (data.creds) delete data.creds; // eslint-disable-line no-param-reassign
    // POST user record
    this.props.mutator.users.POST(data);
    // POST credentials, permission-user, permissions;
    this.postCreds(data.username, creds);
    this.onClickCloseNewUser();
  }

  postCreds(username, creds) {
    this.log('xhr', `POST credentials for new user '${username}':`, creds);
    fetch(`${this.okapi.url}/authn/credentials`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token, 'Content-Type': 'application/json' }),
      body: JSON.stringify(creds),
    }).then((response) => {
      if (response.status >= 400) {
        this.log('xhr', 'Users. POST of creds failed.');
      } else {
        this.postPerms(username, [
          'users.collection.get',       // so the user can search for his own user record after login
          'perms.permissions.get',      // so the user can fetch his own permissions after login
          'usergroups.collection.get',  // so patron groups can be listed in the Users module
          'module.trivial.enabled',     // so that at least one module is available to new users
        ]);
      }
    });
  }

  postPerms(username, perms) {
    this.log('xhr', `POST permissions for new user '${username}':`, perms);
    fetch(`${this.okapi.url}/perms/users`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token, 'Content-Type': 'application/json' }),
      body: JSON.stringify({ username, permissions: perms }),
    }).then((response) => {
      if (response.status >= 400) {
        this.log('xhr', 'Users. POST of users permissions failed.');
      } else {
        // nothing to do
      }
    });
  }

  collapseDetails() {
    this.setState({
      selectedItem: {},
    });
    this.props.history.push(`${this.props.match.path}${this.props.location.search}`);
  }

  render() {
    const { data, stripes } = this.props;
    const users = data.users || [];

    /* searchHeader is a 'custom pane header'*/
    const searchHeader = <FilterPaneSearch id="SearchField" onChange={this.onChangeSearch} onClear={this.onClearSearch} value={this.state.searchTerm} />;
    const resultMenu = <PaneMenu><button><Icon icon="bookmark" /></button></PaneMenu>;

    const resultsFormatter = {
      Active: user => user.active,
      Name: user => `${_.get(user, ['personal', 'last_name'], '')}, ${_.get(user, ['personal', 'first_name'], '')}`,
      'Patron Group': (user) => {
        const map = {
          on_campus: 'On-campus',
          off_campus: 'Off-campus',
          other: 'Other',
        };
        const maybe = map[user.patron_group];
        if (maybe) return maybe;
        const pg = data.patronGroups.filter(g => g.id === user.patron_group)[0];
        return pg ? pg.group : '?';
      },
      'User ID': user => user.username,
      Email: user => _.get(user, ['personal', 'email']),
    };

    const detailsPane = (
      this.props.stripes.hasPerm('users.item.get') ?
        <Route
          path={`${this.props.match.path}/view/:userid/:username`}
          render={props => <this.connectedViewUser stripes={stripes} paneWidth="44%" onClose={this.collapseDetails} {...props} />}
        /> :

Permission Error

Sorry - your user permissions do not allow access to this page.

);

    return (
      <Paneset>
        {/* Filter Pane */}
        <Pane defaultWidth="16%" header={searchHeader}>
          <FilterGroups config={filterConfig} filters={this.state.filters} onChangeFilter={this.onChangeFilter} />
          <FilterControlGroup label="Actions">
            <IfPermission {...this.props} perm="users.item.post">
              <IfPermission {...this.props} perm="login.item.post">
                <IfPermission {...this.props} perm="perms.users.item.post">
                  <Button fullWidth onClick={this.onClickAddNewUser}>New user</Button>
                </IfPermission>
              </IfPermission>
            </IfPermission>
          </FilterControlGroup>
        </Pane>
        {/* Results Pane */}
        <Pane
          defaultWidth="fill"
          paneTitle={

              Results

                {users.length} Result{users.length === 1 ? '' : 's'} Found

          }
          lastMenu={resultMenu}
        >
          <MultiColumnList
            contentData={users}
            selectedRow={this.state.selectedItem}
            rowMetadata={['id', 'username']}
            formatter={resultsFormatter}
            onRowClick={this.onSelectRow}
            onHeaderClick={this.onSort}
            visibleColumns={['Active', 'Name', 'Patron Group', 'User ID', 'Email']}
            sortOrder={this.state.sortOrder}
            isEmptyMessage={`No results found for "${this.state.searchTerm}". Please check your spelling and filters.`}
            columnMapping={{ 'User ID': 'username' }}
          />
        </Pane>

        {detailsPane}
        <Layer isOpen={data.addUserMode ? data.addUserMode.mode : false} label="Add New User Dialog">
          <UserForm
            initialValues={{ available_patron_groups: this.props.data.patronGroups }}
            onSubmit={(record) => { this.create(record); }}
            onCancel={this.onClickCloseNewUser}
          />
        </Layer>
      </Paneset>
    );
  }
}

export default Users;
<commit_msg>ESLint fixes (STRIPES-334), mostly indentation of JSX<commit_after>import _ from 'lodash';
// We have to remove node_modules/react to avoid having multiple copies loaded.
// eslint-disable-next-line import/no-unresolved
import React, { PropTypes } from 'react';
import Route from 'react-router-dom/Route';
import queryString from 'query-string';
import fetch from 'isomorphic-fetch';

import Pane from '@folio/stripes-components/lib/Pane';
import Paneset from '@folio/stripes-components/lib/Paneset';
import PaneMenu from '@folio/stripes-components/lib/PaneMenu';
import Button from '@folio/stripes-components/lib/Button';
import Icon from '@folio/stripes-components/lib/Icon';
import MultiColumnList from '@folio/stripes-components/lib/MultiColumnList';
import FilterPaneSearch from '@folio/stripes-components/lib/FilterPaneSearch';
import FilterControlGroup from '@folio/stripes-components/lib/FilterControlGroup';
import Layer from '@folio/stripes-components/lib/Layer';
import FilterGroups, { initialFilterState, onChangeFilter } from '@folio/stripes-components/lib/FilterGroups';
import transitionToParams from '@folio/stripes-components/util/transitionToParams';
import makeQueryFunction from '@folio/stripes-components/util/makeQueryFunction';
import IfPermission from '@folio/stripes-components/lib/IfPermission';

import UserForm from './UserForm';
import ViewUser from './ViewUser';

const filterConfig = [
  {
    label: 'User status',
    name: 'active',
    cql: 'active',
    values: [
      { name: 'Active', cql: 'true' },
      { name: 'Inactive', cql: 'false' },
    ],
  },
  {
    label: 'Patron group',
    name: 'pg',
    cql: 'patron_group',
    values: [
      { name: 'On-campus', cql: 'on_campus' },
      { name: 'Off-campus', cql: 'off_campus' },
      { name: 'Other', cql: 'other' },
    ],
  },
];

class Users extends React.Component {
  static contextTypes = {
    store: PropTypes.object,
  };

  static propTypes = {
    stripes: PropTypes.shape({
      logger: PropTypes.shape({
        log: PropTypes.func.isRequired,
      }).isRequired,
      connect: PropTypes.func.isRequired,
      hasPerm: PropTypes.func.isRequired,
    }).isRequired,
    data: PropTypes.object.isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
      search: PropTypes.string,
    }).isRequired,
    match: PropTypes.shape({
      path: PropTypes.string.isRequired,
    }).isRequired,
    mutator: PropTypes.shape({
      addUserMode: PropTypes.shape({
        replace: PropTypes.func,
      }),
      users: PropTypes.shape({
        POST: PropTypes.func,
      }),
    }).isRequired,
  };

  static manifest = Object.freeze({
    addUserMode: {},
    users: {
      type: 'okapi',
      records: 'users',
      recordsRequired: 30,
      perRequest: 10,
      path: 'users',
      GET: {
        params: {
          query: makeQueryFunction(
            'username=*',
            'username="$QUERY*" or personal.first_name="$QUERY*" or personal.last_name="$QUERY*"',
            {
              Active: 'active',
              Name: 'personal.last_name personal.first_name',
              'Patron Group': 'patron_group',
              'User ID': 'username',
              Email: 'personal.email',
            },
            filterConfig,
          ),
        },
        staticFallback: { params: {} },
      },
    },
    patronGroups: {
      type: 'okapi',
      path: 'groups',
      records: 'usergroups',
    },
  });

  constructor(props, context) {
    super(props);

    const query = props.location.search ? queryString.parse(props.location.search) : {};
    this.state = {
      filters: initialFilterState(filterConfig, query.filters),
      selectedItem: {},
      searchTerm: query.query || '',
      sortOrder: query.sort || '',
    };

    this.okapi = context.store.getState().okapi;

    this.onClearSearch = this.onClearSearch.bind(this);
    this.onSort = this.onSort.bind(this);
    this.onSelectRow = this.onSelectRow.bind(this);
    this.onClickAddNewUser = this.onClickAddNewUser.bind(this);
    this.onClickCloseNewUser = this.onClickCloseNewUser.bind(this);
    this.onChangeSearch = this.onChangeSearch.bind(this);
    this.performSearch = this.performSearch.bind(this); // For now, prefer instant response
    // this.performSearch = _.debounce(this.performSearch.bind(this), 250);

    this.onChangeFilter = onChangeFilter.bind(this);
    this.transitionToParams = transitionToParams.bind(this);

    this.collapseDetails = this.collapseDetails.bind(this);
    this.connectedViewUser = props.stripes.connect(ViewUser);
    const logger = props.stripes.logger;
    this.log = logger.log.bind(logger);
  }

  componentWillMount() {
    if (_.isEmpty(this.props.data.addUserMode)) this.props.mutator.addUserMode.replace({ mode: false });
  }

  onClearSearch() {
    this.log('action', 'cleared search');
    this.setState({ searchTerm: '' });
    this.props.history.push(this.props.location.pathname);
  }

  onSort(e, meta) {
    const sortOrder = meta.alias;
    this.log('action', `sorted by ${sortOrder}`);
    this.setState({ sortOrder });
    this.transitionToParams({ sort: sortOrder });
  }

  onSelectRow(e, meta) {
    const userId = meta.id;
    const username = meta.username;
    this.log('action', `clicked ${userId}, selected user =`, meta);
    this.setState({ selectedItem: meta });
    this.props.history.push(`/users/view/${userId}/${username}${this.props.location.search}`);
  }

  onClickAddNewUser(e) {
    if (e) e.preventDefault();
    this.log('action', 'clicked "add new user"');
    this.props.mutator.addUserMode.replace({ mode: true });
  }

  onClickCloseNewUser(e) {
    if (e) e.preventDefault();
    this.log('action', 'clicked "close new user"');
    this.props.mutator.addUserMode.replace({ mode: false });
  }

  onChangeSearch(e) {
    const query = e.target.value;
    this.setState({ searchTerm: query });
    this.performSearch(query);
  }

  performSearch(query) {
    this.log('action', `searched for '${query}'`);
    this.transitionToParams({ query });
  }

  updateFilters(filters) { // provided for onChangeFilter
    this.transitionToParams({ filters: Object.keys(filters).filter(key => filters[key]).join(',') });
  }

  create(data) {
    // extract creds object from user object
    const creds = Object.assign({}, data.creds, { username: data.username });
    if (data.creds) delete data.creds; // eslint-disable-line no-param-reassign
    // POST user record
    this.props.mutator.users.POST(data);
    // POST credentials, permission-user, permissions;
    this.postCreds(data.username, creds);
    this.onClickCloseNewUser();
  }

  postCreds(username, creds) {
    this.log('xhr', `POST credentials for new user '${username}':`, creds);
    fetch(`${this.okapi.url}/authn/credentials`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token, 'Content-Type': 'application/json' }),
      body: JSON.stringify(creds),
    }).then((response) => {
      if (response.status >= 400) {
        this.log('xhr', 'Users. POST of creds failed.');
      } else {
        this.postPerms(username, [
          'users.collection.get',       // so the user can search for his own user record after login
          'perms.permissions.get',      // so the user can fetch his own permissions after login
          'usergroups.collection.get',  // so patron groups can be listed in the Users module
          'module.trivial.enabled',     // so that at least one module is available to new users
        ]);
      }
    });
  }

  postPerms(username, perms) {
    this.log('xhr', `POST permissions for new user '${username}':`, perms);
    fetch(`${this.okapi.url}/perms/users`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.okapi.tenant, 'X-Okapi-Token': this.okapi.token, 'Content-Type': 'application/json' }),
      body: JSON.stringify({ username, permissions: perms }),
    }).then((response) => {
      if (response.status >= 400) {
        this.log('xhr', 'Users. POST of users permissions failed.');
      } else {
        // nothing to do
      }
    });
  }

  collapseDetails() {
    this.setState({
      selectedItem: {},
    });
    this.props.history.push(`${this.props.match.path}${this.props.location.search}`);
  }

  render() {
    const { data, stripes } = this.props;
    const users = data.users || [];

    /* searchHeader is a 'custom pane header'*/
    const searchHeader = <FilterPaneSearch id="SearchField" onChange={this.onChangeSearch} onClear={this.onClearSearch} value={this.state.searchTerm} />;
    const resultMenu = <PaneMenu><button><Icon icon="bookmark" /></button></PaneMenu>;

    const resultsFormatter = {
      Active: user => user.active,
      Name: user => `${_.get(user, ['personal', 'last_name'], '')}, ${_.get(user, ['personal', 'first_name'], '')}`,
      'Patron Group': (user) => {
        const map = {
          on_campus: 'On-campus',
          off_campus: 'Off-campus',
          other: 'Other',
        };
        const maybe = map[user.patron_group];
        if (maybe) return maybe;
        const pg = data.patronGroups.filter(g => g.id === user.patron_group)[0];
        return pg ? pg.group : '?';
      },
      'User ID': user => user.username,
      Email: user => _.get(user, ['personal', 'email']),
    };

    const detailsPane = (
      this.props.stripes.hasPerm('users.item.get') ?
        (<Route
          path={`${this.props.match.path}/view/:userid/:username`}
          render={props => <this.connectedViewUser stripes={stripes} paneWidth="44%" onClose={this.collapseDetails} {...props} />}
        />) :
        (

Permission Error

Sorry - your user permissions do not allow access to this page.

));

    return (
      <Paneset>
        {/* Filter Pane */}
        <Pane defaultWidth="16%" header={searchHeader}>
          <FilterGroups config={filterConfig} filters={this.state.filters} onChangeFilter={this.onChangeFilter} />
          <FilterControlGroup label="Actions">
            <IfPermission {...this.props} perm="users.item.post">
              <IfPermission {...this.props} perm="login.item.post">
                <IfPermission {...this.props} perm="perms.users.item.post">
                  <Button fullWidth onClick={this.onClickAddNewUser}>New user</Button>
                </IfPermission>
              </IfPermission>
            </IfPermission>
          </FilterControlGroup>
        </Pane>
        {/* Results Pane */}
        <Pane
          defaultWidth="fill"
          paneTitle={

              Results

                {users.length} Result{users.length === 1 ? '' : 's'} Found

          }
          lastMenu={resultMenu}
        >
          <MultiColumnList
            contentData={users}
            selectedRow={this.state.selectedItem}
            rowMetadata={['id', 'username']}
            formatter={resultsFormatter}
            onRowClick={this.onSelectRow}
            onHeaderClick={this.onSort}
            visibleColumns={['Active', 'Name', 'Patron Group', 'User ID', 'Email']}
            sortOrder={this.state.sortOrder}
            isEmptyMessage={`No results found for "${this.state.searchTerm}". Please check your spelling and filters.`}
            columnMapping={{ 'User ID': 'username' }}
          />
        </Pane>

        {detailsPane}
        <Layer isOpen={data.addUserMode ? data.addUserMode.mode : false} label="Add New User Dialog">
          <UserForm
            initialValues={{ available_patron_groups: this.props.data.patronGroups }}
            onSubmit={(record) => { this.create(record); }}
            onCancel={this.onClickCloseNewUser}
          />
        </Layer>
      </Paneset>
    );
  }
}

export default Users;
<|endoftext|>


<commit_before>
/*!
 * Waves v0.2.0
 * https://publicis-indonesia.github.io/Waves
 *
 * Copyright 2014 Publicis Metro Indonesia, PT. and other contributors
 * Released under the BSD license
 * https://github.com/publicis-indonesia/Waves/blob/master/LICENSE
 */

;(function(window) {
    'use strict';

    function Waves() {

        var $$ = document.querySelectorAll.bind(document);

        var Effect = {

            show: function(e) {

                var el = this;

                // Create ripple
                var ripple = document.createElement('div');
                ripple.className = ripple.className + 'waves-ripple';
                el.appendChild(ripple);

                // Get click coordinate and element witdh
                var relativeX   = (e.pageX - el.offsetLeft);
                var relativeY   = (e.pageY - el.offsetTop);
                var width       = el.clientWidth;

                // Attach data to element
                ripple.setAttribute('data-hold', Date.now());
                ripple.setAttribute('data-x', relativeX);
                ripple.setAttribute('data-y', relativeY);

                // Start ripple
                var positionStyle = 'top:'+relativeY+'px;left:'+relativeX+'px;';
                var flowStyle = 'border-width:'+width+'px;margin-top:-'+width+'px;margin-left:-'+width+'px;opacity:1;';

                ripple.className = ripple.className + ' waves-notransition';
                ripple.setAttribute('style', positionStyle);
                ripple.offsetHeight;
                ripple.className = ripple.className.replace('waves-notransition', '');
                ripple.setAttribute('style', positionStyle+flowStyle);

            },

            hide: function(e) {

                var el = this;
                var width = el.clientWidth;

                console.log();

                // Get first ripple
                var ripple = null;

                for (var a = 0; a < el.children.length; a++) {
                    if (el.children[a].className.indexOf('waves-ripple') !== -1) {
                        ripple = el.children[a];
                        continue;
                    }
                }

                if (!ripple) {
                    return false;
                }

                var relativeX   = ripple.getAttribute('data-x');
                var relativeY   = ripple.getAttribute('data-y');

                // Get delay beetween mousedown and mouse leave
                var diff = Date.now() - Number(ripple.getAttribute('data-hold'));
                var delay = 500 - diff;

                if (delay < 0) {
                    delay = 0;
                }

                // Fade out ripple after delay
                setTimeout(function() {

                    var style = 'top:'+relativeY+'px;left:'+relativeX+'px;border-width:'+width+'px;margin-top:-'+width+'px;margin-left:-'+width+'px;opacity:0;';

                    ripple.setAttribute('style', style);

                    setTimeout(function() {
                        try {
                            el.removeChild(ripple);
                        } catch(e) {
                            return false;
                        }
                    }, 300);

                }, delay);

            },
        };

        return {
            displayEffect: function() {

                Array.prototype.forEach.call($$('.waves-effect'), function(i) {

                    i.addEventListener('mousedown', Effect.show, false);
                    i.addEventListener('mouseup', Effect.hide, false);
                    i.addEventListener('mouseleave', Effect.hide, false);

                });

            }
        }
    }

    window.Waves = Waves;

})(window);<commit_msg>Add partial support for touch event<commit_after>
/*!
 * Waves v0.2.0
 * https://publicis-indonesia.github.io/Waves
 *
 * Copyright 2014 Publicis Metro Indonesia, PT. and other contributors
 * Released under the BSD license
 * https://github.com/publicis-indonesia/Waves/blob/master/LICENSE
 */

;(function(window) {
    'use strict';

    function Waves() {

        var $$ = document.querySelectorAll.bind(document);

        var Effect = {

            show: function(e) {

                var el = this;

                // Create ripple
                var ripple = document.createElement('div');
                ripple.className = ripple.className + 'waves-ripple';
                el.appendChild(ripple);

                // Get click coordinate and element witdh
                var relativeX   = (e.pageX - el.offsetLeft);
                var relativeY   = (e.pageY - el.offsetTop);
                var width       = el.clientWidth;

                // Attach data to element
                ripple.setAttribute('data-hold', Date.now());
                ripple.setAttribute('data-x', relativeX);
                ripple.setAttribute('data-y', relativeY);

                // Start ripple
                var positionStyle = 'top:'+relativeY+'px;left:'+relativeX+'px;';
                var flowStyle = 'border-width:'+width+'px;margin-top:-'+width+'px;margin-left:-'+width+'px;opacity:1;';

                ripple.className = ripple.className + ' waves-notransition';
                ripple.setAttribute('style', positionStyle);
                ripple.offsetHeight;
                ripple.className = ripple.className.replace('waves-notransition', '');
                ripple.setAttribute('style', positionStyle+flowStyle);

            },

            hide: function(e) {

                var el = this;
                var width = el.clientWidth;

                console.log();

                // Get first ripple
                var ripple = null;

                for (var a = 0; a < el.children.length; a++) {
                    if (el.children[a].className.indexOf('waves-ripple') !== -1) {
                        ripple = el.children[a];
                        continue;
                    }
                }

                if (!ripple) {
                    return false;
                }

                var relativeX   = ripple.getAttribute('data-x');
                var relativeY   = ripple.getAttribute('data-y');

                // Get delay beetween mousedown and mouse leave
                var diff = Date.now() - Number(ripple.getAttribute('data-hold'));
                var delay = 500 - diff;

                if (delay < 0) {
                    delay = 0;
                }

                // Fade out ripple after delay
                setTimeout(function() {

                    var style = 'top:'+relativeY+'px;left:'+relativeX+'px;border-width:'+width+'px;margin-top:-'+width+'px;margin-left:-'+width+'px;opacity:0;';

                    ripple.setAttribute('style', style);

                    setTimeout(function() {
                        try {
                            el.removeChild(ripple);
                        } catch(e) {
                            return false;
                        }
                    }, 300);

                }, delay);

            },
        };

        return {
            displayEffect: function() {

                Array.prototype.forEach.call($$('.waves-effect'), function(i) {

                    if (window.Touch) {
                        i.addEventListener('touchstart', Effect.show, false);
                        i.addEventListener('touchend', Effect.hide, false);
                    }

                    i.addEventListener('mousedown', Effect.show, false);
                    i.addEventListener('mouseup', Effect.hide, false);
                    i.addEventListener('mouseleave', Effect.hide, false);

                });

            }
        }
    }

    window.Waves = Waves;

})(window);<|endoftext|>


<commit_before>/**
 *@Author: chad.ding
 *@Copyright: 2017-2018 DMF
 *@Date: 2017-05-25 22:22:27
 */

var express = require('express');
var fs = require('fs');
var app = express();

app.get('/api/channel/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/channel_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/queue/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/queue_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/vdp/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/vdp_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/subscribe/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/subscribe_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/params/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/params_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/domain/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/domain_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/console/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/console_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/cluster/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/cluster_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/chart/line', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/line_chart_data.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/chart/bar', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/bar_chart_data.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/chart/pie', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/pie_chart_data.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/chart/panel', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/panel_chart_data.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.listen(8080);

console.log('Server running on 8080');
<commit_msg>remove useless code<commit_after>/**
 *@Author: chad.ding
 *@Copyright: 2017-2018 DMF
 *@Date: 2017-05-25 22:22:27
 */

var express = require('express');
var fs = require('fs');
var app = express();

app.get('/api/channel/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/channel_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/queue/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/queue_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/vdp/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/vdp_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/subscribe/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/subscribe_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/params/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/params_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/domain/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/domain_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/console/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/console_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/cluster/list', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/cluster_list.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/chart/line', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/line_chart_data.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/chart/bar', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/bar_chart_data.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.get('/api/chart/pie', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

    fs.readFile(__dirname + '/data/pie_chart_data.json', { encoding: 'utf-8' }, function(err, data) {
        if (err) {
            console.error(err);
            return;
        }
        res.end(data);
    });
});

app.listen(8080);
console.log('Server running on 8080');
<|endoftext|>


<commit_before>var Actor = function(args) {
  this.context = args.context;
  this.x = args.startX;
  this.y = args.startY;
  this.name = args.name;
  this.radius = 6;
  this.direction = args.direction;
  this.speed = PAC_MOVE_DELAY;
  this.mouthPos = 0.0;
  this.mouthIsOpening = true;
  this.moveIntervalID = false;
  this.keyStates = {
    up: false,
    down: false,
    left: false,
    right: false
  }
}
Actor.prototype.getClearDimensions = function() {
  return {
    x: this.x - this.radius,
    y: this.y - this.radius,
    w: 2 * this.radius,
    h: 2 * this.radius
  }
};
Actor.prototype.move = function() {
  this.mouthPos = Math.floor((this.mouthPos + ((this.mouthIsOpening ? 1 : -1) * 0.08)) * 100) / 100;
  if(this.mouthPos <= 0) {
    this.mouthIsOpening = true;
  }
  else if(this.mouthPos >= 0.4) {
    this.mouthIsOpening = false;
  }

  switch(this.direction) {
    case "up": this.y--; break;
    case "down": this.y++; break;
    case "left": this.x--; break;
    case "right": this.x++; break;
  }

  if(this.x + 8 <= 0) {
    if(this.direction == "left") {
      this.x = BOARD_WIDTH;
    }
  }
  else if(this.x - 8 > BOARD_WIDTH) {
    if(this.direction == "right") {
      this.x = 0 - 8;
    }
  }
  return {x: this.x, y: this.y}
};
Actor.prototype.moveTowardCenter = function() {
  var xInTile = this.x % 8;
  var yInTile = this.y % 8;
  if((this.direction == "up" || this.direction == "down") && xInTile != 4) {
    this.x += xInTile < 4 ? 1 : -1;
  }
  else if((this.direction == "left" || this.direction == "right") && yInTile != 4) {
    this.y += yInTile < 4 ? 1 : -1;
  }
};
Actor.prototype.handleKeyUp = function(keyPressed) {
  this.keyStates[keyPressed] = false;
};
<commit_msg>Add Actor.getRenderArgs to return args for BrowserView.renderPac<commit_after>var Actor = function(args) {
  this.context = args.context;
  this.x = args.startX;
  this.y = args.startY;
  this.name = args.name;
  this.radius = 6;
  this.direction = args.direction;
  this.speed = PAC_MOVE_DELAY;
  this.mouthPos = 0.0;
  this.mouthIsOpening = true;
  this.moveIntervalID = false;
  this.keyStates = {
    up: false,
    down: false,
    left: false,
    right: false
  }
}
Actor.prototype.getClearDimensions = function() {
  return {
    x: this.x - this.radius,
    y: this.y - this.radius,
    w: 2 * this.radius,
    h: 2 * this.radius
  }
};
Actor.prototype.getRenderArgs = function() {
  return {
    x: this.x,
    y: this.y,
    direction: this.direction,
    color: nameToColor[this.name],
    radius: this.radius,
    mouthPos: this.mouthPos
  }
};
Actor.prototype.move = function() {
  this.mouthPos = Math.floor((this.mouthPos + ((this.mouthIsOpening ? 1 : -1) * 0.08)) * 100) / 100;
  if(this.mouthPos <= 0) {
    this.mouthIsOpening = true;
  }
  else if(this.mouthPos >= 0.4) {
    this.mouthIsOpening = false;
  }

  switch(this.direction) {
    case "up": this.y--; break;
    case "down": this.y++; break;
    case "left": this.x--; break;
    case "right": this.x++; break;
  }

  if(this.x + 8 <= 0) {
    if(this.direction == "left") {
      this.x = BOARD_WIDTH;
    }
  }
  else if(this.x - 8 > BOARD_WIDTH) {
    if(this.direction == "right") {
      this.x = 0 - 8;
    }
  }
  return {x: this.x, y: this.y}
};
Actor.prototype.moveTowardCenter = function() {
  var xInTile = this.x % 8;
  var yInTile = this.y % 8;
  if((this.direction == "up" || this.direction == "down") && xInTile != 4) {
    this.x += xInTile < 4 ? 1 : -1;
  }
  else if((this.direction == "left" || this.direction == "right") && yInTile != 4) {
    this.y += yInTile < 4 ? 1 : -1;
  }
};
Actor.prototype.handleKeyUp = function(keyPressed) {
  this.keyStates[keyPressed] = false;
};
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var fs = require("fs");

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.get('/a/threads', function (req, res) {
   res.type('application/json');
   fs.readFile( __dirname + "/" + "tag_anime.json", 'utf8', function (err, data) {
     console.log(data);
      res.jsonp( data );
   });
})

app.get('/', function (req, res) {
   res.type('text/html');
   res.sendfile('tag.html');
})
app.get('/css/estilo.css', function (req, res) {
   res.type('text/css');
   res.sendfile('css/estilo.css');
})

app.use('..',express.static('css'));
app.use('..',express.static('js'));

var port = process.env.PORT || 3000;
var server = app.listen(port, function () {})
<commit_msg>Tentando adicionar arquivo css<commit_after>var express = require('express');
var app = express();
var fs = require("fs");

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.get('/a/threads', function (req, res) {
   res.type('application/json');
   fs.readFile( __dirname + "/" + "tag_anime.json", 'utf8', function (err, data) {
     console.log(data);
      res.jsonp( data );
   });
})

app.get('/', function (req, res) {
   res.type('text/html');
   res.sendfile('tag.html');
})
app.get('/css/estilo.css', function (req, res) {
   res.type('text/css');
   res.sendfile('css/estilo.css');
})

app.get('/js/app.js', function (req, res) {
   res.type('text/javascript');
   res.sendfile('js/app.js');
})

var port = process.env.PORT || 3000;
var server = app.listen(port, function () {})
<|endoftext|>