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

     /**
      * A generic character
      * @param {Phaser.Game} game   The phaser game instance
      * @param {Number} x           The X-coordinate on which to spawn this character
      * @param {Number} y           The Y-coordinate on which to spawn this character
      * @param {String} texture     The texture key to use on this character
      */
    Game.Character = function(game, x, y, texture, controller) {
        Phaser.Sprite.call(this, game, x, y, texture);

        this.anchor.set(0.5);
        this.game.physics.enable(this, Phaser.Physics.ARCADE);

        this.body.drag = new Phaser.Point(1500, 0);
        // this.body.collideWorldBounds = true;
        this.body.maxVelocity = new Phaser.Point(3000, 2000);

        /**
         * The desired drag for the character while walking on the ground
         * @type {Phaser.Point}
         */
        this.desiredDrag = this.body.drag.clone();

        /**
         * The desired drag for the character while in the air
         * @type {Phaser.Point}
         */
        this.desiredAirDrag = new Phaser.Point(300, 0);

        /**
         * The texture key used in the making of this character
         * @type {String}
         */
        this.textureKey = texture;

        /**
         * The maximum amount of jumps to jump
         * @type {Number}
         */
        this.maxJumps = 1;

        /**
         * The velocity in which the character can walk
         * @type {Number}
         */
        this.walkingVelocity = 500;

        /**
         * The controller that handles this sprite
         * @type {Game.Controller}
         */
        this.controller = controller;

        /**
         * The acceleration when walking
         * @type {Number}
         */
        this.walkingAcc = 2000;

        /**
         * The amount to refill this.jumpMeter with
         * @type {Number}
         */
        this.fullJumpMeter = 20000;

        /**
         * The amount that reduces the jumpmeter every tick that jump key is being held down
         * @type {Number}
         */
        this.jumpFactor = 0.7;

        /**
         * The jump meter, that is how much jump-energy there is left
         * @type {Number}
         */
        this.jumpMeter = this.fullJumpMeter;

        /**
         * The current amount of jumps jumped since reset
         * @type {Number}
         */
        this.currentJumps = 0;

        /**
         * Calculated states the character can exist in
         * @type {Object} states
         * @type {Object} states.airborn    If the character is in the air
         * @type {Object} states.walking    If the character is walking
         * @type {Object} states.tryWalking If the character is trying to walk, i.e. pressing right or left on controller
         * @type {Object} states.falling    If the character is airborn with positive Y-velocity
         * @type {Object} states.rising     If the character is airborn with negative Y-velocity
         * @type {Object} states.still      If the character is completely still
         * @type {Object} states.ducking    If the character is ducking, that is, pressing the duck key
         */
        this.states = {
            'airborn': false,
            'walking': false,
            'tryWalking': false,
            'falling': false,
            'rising': false,
            'still': false,
            'ducking': false
        };

        /**
         * Whether or not to automatically flip the character horizontally when walking to the left
         * @type {Boolean}
         */
        this.autoFlip = true;

        /**
         * The jump sound effect
         * @type {Phaser.Audio}
         */
        this.jumpSFX = this.game.add.audio('jump');

        /**
         * If the character is in god mode or not. God mode allows for flying
         * @type {Boolean}
         */
        this.godMode = false;

        /**
         * Internal cache
         * @type {Object}
         */
        this._cache = {
            prevY: -1
        };

        /**
         * An emitter that emits particles when submerged
         * @type {Phaser.Particles.Arcade}
         */
        this.bubbleEmitter = this.game.add.emitter(0, 0);
        this.bubbleEmitter.makeParticles('bubbles', [0,1,2,3,4,5,6], 20);
        this.bubbleEmitter.gravity = -3750;
        this.bubbleEmitter.maxRotation = 0;
        this.bubbleEmitter.minRotation = 0;
        this.bubbleEmitter.start(false, 5000);
        this.bubbleEmitter.on = false;

        /**
         * An emitter that emits particles when hitting and/or running ont he ground
         * @type {Phaser.Particles.Arcade}
         */
        this.groundEmitter = this.game.add.emitter(this.width * 0.5, this.height);
        this.groundEmitter.makeParticles('UI', [20,22,23], 100);
        this.groundEmitter.setXSpeed(-500, 500);
        this.groundEmitter.setYSpeed(-1000, -500);

        return this;
    };

    Game.Character.prototype = Object.create(Phaser.Sprite.prototype);
    Game.Character.prototype.constructor = Game.Character;

    /**
     * Resets the jump
     * @return {Undefined}
     */
    Game.Character.prototype.resetJump = function() {
        var deltaVel = this._cache.prevY - this.body.velocity.y;
        if (deltaVel > 1500) {
            // Max deltaVel is 2000. 20 is the lowest amount of particles. Max amount of particles will amount to ~70
            var particles = 20 + (deltaVel - 1500) / 10;
            this.groundEmitter.explode(1000, particles);
        }

        this.currentJumps = 0;
        this.jumpMeter = this.fullJumpMeter;
        this.flicked = false;
    };

    /**
     * Internal method used to handle the jump key
     * @return {undefined}
     */
    Game.Character.prototype._jump = function() {
        if (this.submerged || this.climbing) {
            if (this.controller.jump.isDown) {
                this.body.velocity.y = -400;
            }
            return;
        }

        if (this.controller.jump.isDown) {
            if (!this.jumpWasDown) {
                this.currentJumps += 1;

                if (this.currentJumps < this.maxJumps) {
                    this.body.velocity.y = 0;
                }
            }

            if (Math.floor(this.jumpMeter) > 0 && this.currentJumps < this.maxJumps) {
                if (this.body.onFloor() || !this.jumpWasDown) {
                    this.jumpSFX.playFrom(this.position);
                }

                this.body.acceleration.y -= this.jumpMeter;
                this.jumpMeter *= this.jumpFactor;
            }
        } else {
            if (this.jumpWasDown) {
                if (this.maxJumps >= this.currentJumps) {
                    this.jumpMeter = this.fullJumpMeter;
                } else {
                    this.jumpMeter = 0;
                }
            }
        }

        this.jumpWasDown = this.controller.jump.isDown;
    };

    /**
     * Internal method to calculate the this.states object
     * @return {Undefined}
     */
    Game.Character.prototype._calculateStates = function() {
        this.states.airborn = !this.body.onFloor();
        this.states.falling = this.states.airborn && this.body.velocity.y > 0;
        this.states.rising = this.states.airborn && this.body.velocity.y < 0;

        if (this.controller) {
            this.states.tryWalking = (this.controller.right.isDown || this.controller.left.isDown);
            this.states.walking = this.states.tryWalking && !this.states.airborn;
            this.states.still = (this.body.touching.down || this.body.onFloor()) && Math.abs(this.body.velocity.x) < 40;
            this.states.ducking = this.controller.down.isDown;
        } else {
            this.states.walking = undefined;
            this.states.still = undefined;
            this.states.ducking = undefined;
        }
    };

    Game.Character.prototype._controllerHandler = function() {
        if (this.controller.update) {
            this.controller.update();
        }

        // Walking Action
        if (this.controller.right.isDown) {
            this.body.acceleration.x = this.walkingAcc;

            if (this.autoFlip) {
                this.scale.x = 1;
            }
        } else if (this.controller.left.isDown) {
            this.body.acceleration.x = -this.walkingAcc;

            if (this.autoFlip) {
                this.scale.x = -1;
            }
        } else {
            this.body.acceleration.setTo(0);
        }

        // Jumping action
        if (!this.godMode) {
            this._jump();
        } else {
            if (this.controller.jump.isDown) {
                this.body.acceleration.y = -2000;
            } else if (this.controller.down.isDown) {
                this.body.acceleration.y = 2000;
            } else {
                this.body.velocity.y *= 0.95;
            }
        }
    };

    Game.Character.prototype._setCache = function() {
        this._cache.prevY = this.body.velocity.y;
    };

    Game.Character.prototype.updateEmitters = function() {
        this.bubbleEmitter.emitX = this.position.x;
        this.bubbleEmitter.emitY = this.position.y;
        this.groundEmitter.emitX = this.position.x;
        this.groundEmitter.emitY = this.position.y;
    };

    Game.Character.prototype.update = function() {
        // Calculate states
        this._calculateStates();

        // Zero accleration every tick, for easier adding of acceleration and so that body.drag have an affect
        this.body.acceleration.setTo(0);

        // Update all emitters to follow player position
        this.updateEmitters();

        // If there is a controller, handle it
        if (this.controller) {
            this._controllerHandler();
        }

        if (this.states.airborn) {
            if (!this.body.drag.equals(this.desiredAirDrag)) {
                this.body.drag.setTo(this.desiredAirDrag.x, this.desiredAirDrag.y);
            }
        } else {
            if (!this.body.drag.equals(this.desiredDrag)) {
                this.body.drag.setTo(this.desiredDrag.x, this.desiredDrag.y);
            }
        }

        //If submerged, reduce speed
        if (this.submerged || this.climbing) {
            this.body.velocity.y *= 0.85;
            this.body.velocity.x *= 0.95;
        }

        // If on floor reset jump
        if (this.body.onFloor() || this.body.touching.down) {
            this.resetJump();
        }

        // Other restrictions etc.

        // Restrict walking speed
        if (this.states.tryWalking && !this.godMode && !this.flicked) {
            this.body.velocity.x = Math.clamp(this.body.velocity.x, -this.walkingVelocity, this.walkingVelocity);
        }

        // Set state on bubble emitter
        this.bubbleEmitter.on = this.submerged;

        this._setCache();
    };

})();
<commit_msg>Fixed bug where i redeclared an inherited property an existing variable<commit_after>(function() {
    'use strict';

     /**
      * A generic character
      * @param {Phaser.Game} game   The phaser game instance
      * @param {Number} x           The X-coordinate on which to spawn this character
      * @param {Number} y           The Y-coordinate on which to spawn this character
      * @param {String} texture     The texture key to use on this character
      */
    Game.Character = function(game, x, y, texture, controller) {
        Phaser.Sprite.call(this, game, x, y, texture);

        this.anchor.set(0.5);
        this.game.physics.enable(this, Phaser.Physics.ARCADE);

        this.body.drag = new Phaser.Point(1500, 0);
        // this.body.collideWorldBounds = true;
        this.body.maxVelocity = new Phaser.Point(3000, 2000);

        /**
         * The desired drag for the character while walking on the ground
         * @type {Phaser.Point}
         */
        this.desiredDrag = this.body.drag.clone();

        /**
         * The desired drag for the character while in the air
         * @type {Phaser.Point}
         */
        this.desiredAirDrag = new Phaser.Point(300, 0);

        /**
         * The texture key used in the making of this character
         * @type {String}
         */
        this.textureKey = texture;

        /**
         * The maximum amount of jumps to jump
         * @type {Number}
         */
        this.maxJumps = 1;

        /**
         * The velocity in which the character can walk
         * @type {Number}
         */
        this.walkingVelocity = 500;

        /**
         * The controller that handles this sprite
         * @type {Game.Controller}
         */
        this.controller = controller;

        /**
         * The acceleration when walking
         * @type {Number}
         */
        this.walkingAcc = 2000;

        /**
         * The amount to refill this.jumpMeter with
         * @type {Number}
         */
        this.fullJumpMeter = 20000;

        /**
         * The amount that reduces the jumpmeter every tick that jump key is being held down
         * @type {Number}
         */
        this.jumpFactor = 0.7;

        /**
         * The jump meter, that is how much jump-energy there is left
         * @type {Number}
         */
        this.jumpMeter = this.fullJumpMeter;

        /**
         * The current amount of jumps jumped since reset
         * @type {Number}
         */
        this.currentJumps = 0;

        /**
         * Calculated states the character can exist in
         * @type {Object} states
         * @type {Object} states.airborn    If the character is in the air
         * @type {Object} states.walking    If the character is walking
         * @type {Object} states.tryWalking If the character is trying to walk, i.e. pressing right or left on controller
         * @type {Object} states.falling    If the character is airborn with positive Y-velocity
         * @type {Object} states.rising     If the character is airborn with negative Y-velocity
         * @type {Object} states.still      If the character is completely still
         * @type {Object} states.ducking    If the character is ducking, that is, pressing the duck key
         */
        this.states = {
            'airborn': false,
            'walking': false,
            'tryWalking': false,
            'falling': false,
            'rising': false,
            'still': false,
            'ducking': false
        };

        /**
         * Whether or not to automatically flip the character horizontally when walking to the left
         * @type {Boolean}
         */
        this.autoFlip = true;

        /**
         * The jump sound effect
         * @type {Phaser.Audio}
         */
        this.jumpSFX = this.game.add.audio('jump');

        /**
         * If the character is in god mode or not. God mode allows for flying
         * @type {Boolean}
         */
        this.godMode = false;

        /**
         * Internal cache
         * @type {Object}
         */
        this._data = {
            prevY: -1
        };

        /**
         * An emitter that emits particles when submerged
         * @type {Phaser.Particles.Arcade}
         */
        this.bubbleEmitter = this.game.add.emitter(0, 0);
        this.bubbleEmitter.makeParticles('bubbles', [0,1,2,3,4,5,6], 20);
        this.bubbleEmitter.gravity = -3750;
        this.bubbleEmitter.maxRotation = 0;
        this.bubbleEmitter.minRotation = 0;
        this.bubbleEmitter.start(false, 5000);
        this.bubbleEmitter.on = false;

        /**
         * An emitter that emits particles when hitting and/or running ont he ground
         * @type {Phaser.Particles.Arcade}
         */
        this.groundEmitter = this.game.add.emitter(this.width * 0.5, this.height);
        this.groundEmitter.makeParticles('UI', [20,22,23], 100);
        this.groundEmitter.setXSpeed(-500, 500);
        this.groundEmitter.setYSpeed(-1000, -500);

        return this;
    };

    Game.Character.prototype = Object.create(Phaser.Sprite.prototype);
    Game.Character.prototype.constructor = Game.Character;

    /**
     * Resets the jump
     * @return {Undefined}
     */
    Game.Character.prototype.resetJump = function() {
        var deltaVel = this._data.prevY - this.body.velocity.y;
        if (deltaVel > 1500) {
            // Max deltaVel is 2000. 20 is the lowest amount of particles. Max amount of particles will amount to ~70
            var particles = 20 + (deltaVel - 1500) / 10;
            this.groundEmitter.explode(1000, particles);
        }

        this.currentJumps = 0;
        this.jumpMeter = this.fullJumpMeter;
        this.flicked = false;
    };

    /**
     * Internal method used to handle the jump key
     * @return {undefined}
     */
    Game.Character.prototype._jump = function() {
        if (this.submerged || this.climbing) {
            if (this.controller.jump.isDown) {
                this.body.velocity.y = -400;
            }
            return;
        }

        if (this.controller.jump.isDown) {
            if (!this.jumpWasDown) {
                this.currentJumps += 1;

                if (this.currentJumps < this.maxJumps) {
                    this.body.velocity.y = 0;
                }
            }

            if (Math.floor(this.jumpMeter) > 0 && this.currentJumps < this.maxJumps) {
                if (this.body.onFloor() || !this.jumpWasDown) {
                    this.jumpSFX.playFrom(this.position);
                }

                this.body.acceleration.y -= this.jumpMeter;
                this.jumpMeter *= this.jumpFactor;
            }
        } else {
            if (this.jumpWasDown) {
                if (this.maxJumps >= this.currentJumps) {
                    this.jumpMeter = this.fullJumpMeter;
                } else {
                    this.jumpMeter = 0;
                }
            }
        }

        this.jumpWasDown = this.controller.jump.isDown;
    };

    /**
     * Internal method to calculate the this.states object
     * @return {Undefined}
     */
    Game.Character.prototype._calculateStates = function() {
        this.states.airborn = !this.body.onFloor();
        this.states.falling = this.states.airborn && this.body.velocity.y > 0;
        this.states.rising = this.states.airborn && this.body.velocity.y < 0;

        if (this.controller) {
            this.states.tryWalking = (this.controller.right.isDown || this.controller.left.isDown);
            this.states.walking = this.states.tryWalking && !this.states.airborn;
            this.states.still = (this.body.touching.down || this.body.onFloor()) && Math.abs(this.body.velocity.x) < 40;
            this.states.ducking = this.controller.down.isDown;
        } else {
            this.states.walking = undefined;
            this.states.still = undefined;
            this.states.ducking = undefined;
        }
    };

    Game.Character.prototype._controllerHandler = function() {
        if (this.controller.update) {
            this.controller.update();
        }

        // Walking Action
        if (this.controller.right.isDown) {
            this.body.acceleration.x = this.walkingAcc;

            if (this.autoFlip) {
                this.scale.x = 1;
            }
        } else if (this.controller.left.isDown) {
            this.body.acceleration.x = -this.walkingAcc;

            if (this.autoFlip) {
                this.scale.x = -1;
            }
        } else {
            this.body.acceleration.setTo(0);
        }

        // Jumping action
        if (!this.godMode) {
            this._jump();
        } else {
            if (this.controller.jump.isDown) {
                this.body.acceleration.y = -2000;
            } else if (this.controller.down.isDown) {
                this.body.acceleration.y = 2000;
            } else {
                this.body.velocity.y *= 0.95;
            }
        }
    };

    Game.Character.prototype._setData = function() {
        this._data.prevY = this.body.velocity.y;
    };

    Game.Character.prototype.updateEmitters = function() {
        this.bubbleEmitter.emitX = this.position.x;
        this.bubbleEmitter.emitY = this.position.y;
        this.groundEmitter.emitX = this.position.x;
        this.groundEmitter.emitY = this.position.y;
    };

    Game.Character.prototype.update = function() {
        // Calculate states
        this._calculateStates();

        // Zero accleration every tick, for easier adding of acceleration and so that body.drag have an affect
        this.body.acceleration.setTo(0);

        // Update all emitters to follow player position
        this.updateEmitters();

        // If there is a controller, handle it
        if (this.controller) {
            this._controllerHandler();
        }

        if (this.states.airborn) {
            if (!this.body.drag.equals(this.desiredAirDrag)) {
                this.body.drag.setTo(this.desiredAirDrag.x, this.desiredAirDrag.y);
            }
        } else {
            if (!this.body.drag.equals(this.desiredDrag)) {
                this.body.drag.setTo(this.desiredDrag.x, this.desiredDrag.y);
            }
        }

        //If submerged, reduce speed
        if (this.submerged || this.climbing) {
            this.body.velocity.y *= 0.85;
            this.body.velocity.x *= 0.95;
        }

        // If on floor reset jump
        if (this.body.onFloor() || this.body.touching.down) {
            this.resetJump();
        }

        // Other restrictions etc.

        // Restrict walking speed
        if (this.states.tryWalking && !this.godMode && !this.flicked) {
            this.body.velocity.x = Math.clamp(this.body.velocity.x, -this.walkingVelocity, this.walkingVelocity);
        }

        // Set state on bubble emitter
        this.bubbleEmitter.on = this.submerged;

        this._setData();
    };

})();
<|endoftext|>


<commit_before>/*
 * This file is part of Invenio.
 * Copyright (C) 2016-2021 CERN.
 *
 * Invenio is free software; you can redistribute it and/or modify it
 * under the terms of the MIT License; see LICENSE file for more details.
 */

import React, { useState } from "react";
import {
  Input,
  Card,
  Container,
  Checkbox,
  Grid,
  Label,
  List,
  Icon,
  Item,
  Button,
  Segment,
} from "semantic-ui-react";
import _truncate from "lodash/truncate";
import { createSearchAppInit } from "@js/invenio_search_ui";
import {
  SearchAppFacets,
  SearchAppResultsPane,
} from "@js/invenio_search_ui/components";
import {
  BucketAggregation,
  SearchBar,
  Count,
  Sort,
  ResultsList,
  Pagination,
  ResultsPerPage,
} from "react-searchkit";
import { i18next } from "@translations/invenio_communities/i18next";

const facetsValues = {
  type: {
    organization: "Organization",
    event: "Event",
    topic: "Topic",
    project: "Project",
  },
};

function ResultsGridItemTemplate({ result, index }) {
  return (
    <Card fluid key={index} href={`/communities/${result.metadata.id}`}>
      <Card.Content>
        <Card.Header>{result.metadata.title}</Card.Header>
        <Card.Description>

        </Card.Description>
      </Card.Content>
    </Card>
  );
}

function ResultsItemTemplate({ result, index }) {
  return (
    <Item key={index}>
      <Item.Content>
        <Button
          compact
          size="small"
          floated="right"
          href={`/communities/${result.id}`}
        >
          <Icon name="eye" />
          {i18next.t("View")}
        </Button>
        <Item.Header href={`/communities/${result.id}`}>
          {result.metadata.title}
        </Item.Header>
        <Item.Description>

        </Item.Description>
      </Item.Content>
    </Item>
  );
}

export const CommunitiesResults = ({
  sortOptions,
  paginationOptions,
  currentResultsState,
}) => {
  const { total } = currentResultsState.data;
  return (
    total && (
      <Grid>
        <Grid.Row>
          <Grid.Column width={16}>
            <Segment>
              <Grid>
                <Grid.Row
                  verticalAlign="middle"
                  className="small padding-tb-5 deposit-result-header"
                >
                  <Grid.Column width={4}>
                    <Count
                      label={() => (
                        <>
                          {i18next.t("{{total}} result(s) found", {
                            total: total,
                          })}
                        </>
                      )}
                    />
                  </Grid.Column>
                  <Grid.Column
                    width={12}
                    textAlign="right"
                    className="padding-r-5"
                  >
                    {sortOptions && (
                      <Sort
                        values={sortOptions}
                        label={(cmp) => (
                          <>
                            {i18next.t("Sort by {{cmp}}", {
                              cmp: cmp,
                            })}
                          </>
                        )}
                      />
                    )}
                  </Grid.Column>
                </Grid.Row>
                <Grid.Row>
                  <Grid.Column>
                    <ResultsList />
                  </Grid.Column>
                </Grid.Row>
              </Grid>
            </Segment>
          </Grid.Column>
        </Grid.Row>
        <Grid.Row verticalAlign="middle">
          <Grid.Column width={4}></Grid.Column>
          <Grid.Column width={8} textAlign="center">
            <Pagination
              options={{
                size: "mini",
                showFirst: false,
                showLast: false,
              }}
            />
          </Grid.Column>
          <Grid.Column textAlign="right" width={4}>
            <ResultsPerPage
              values={paginationOptions.resultsPerPage}
              label={(cmp) => (
                <>
                  {i18next.t("{{cmp}} results per page", {
                    cmp: cmp,
                  })}
                </>
              )}
            />
          </Grid.Column>
        </Grid.Row>
      </Grid>
    )
  );
};

export const CommunitiesSearchBarElement = ({
  placeholder: passedPlaceholder,
  queryString,
  onInputChange,
  executeSearch,
}) => {
  const placeholder = passedPlaceholder || i18next.t("Search");
  const onBtnSearchClick = () => {
    executeSearch();
  };
  const onKeyPress = (event) => {
    if (event.key === "Enter") {
      executeSearch();
    }
  };
  return (
    <Input
      action={{
        icon: "search",
        onClick: onBtnSearchClick,
        className: "search",
      }}
      fluid
      placeholder={placeholder}
      onChange={(event, { value }) => {
        onInputChange(value);
      }}
      value={queryString}
      onKeyPress={onKeyPress}
    />
  );
};

const CommunitiesFacets = ({ aggs, currentResultsState }) => {
  return (
    <>
      {aggs.map((agg) => {
        return (

            <BucketAggregation title={agg.title} agg={agg} />

        );
      })}
    </>
  );
};

const CommunitiesFacetsValues = ({
  bucket,
  isSelected,
  onFilterClicked,
  getChildAggCmps,
}) => {
  const childAggCmps = getChildAggCmps(bucket);
  const [isActive, setisActive] = useState(false);
  const hasChildren = childAggCmps && childAggCmps.props.buckets.length > 0;
  const keyField = bucket.key_as_string ? bucket.key_as_string : bucket.key;
  return (
    <List.Item key={bucket.key}>

        <List.Content className="facet-count">
          {bucket.doc_count}
        </List.Content>
        {hasChildren ? (
          <i
            className={`angle ${isActive ? "down" : "right"} icon`}
            onClick={() => setisActive(!isActive)}
          >
        ) : null}
        <Checkbox
          label={bucket.label || facetsConfig[keyField] || keyField}
          value={keyField}
          onClick={() => onFilterClicked(keyField)}
          checked={isSelected}
        />

      <div className={`content facet-content ${isActive ? "active" : ""}`}>
        {childAggCmps}

    </List.Item>
  );
};

const RDMBucketAggregationElement = ({ title, containerCmp }) => {
  return (
    <Card className="borderless-facet">
      <Card.Content>
        <Card.Header>{title}</Card.Header>
      </Card.Content>
      <Card.Content>{containerCmp}</Card.Content>
    </Card>
  );
};
export const CommunitiesSearchLayout = (props) => (
  <Container>
    <Grid>
      <Grid.Row columns={3}>
        <Grid.Column width={4} />
        <Grid.Column width={8}>
          <SearchBar placeholder={i18next.t("Search communities...")} />
        </Grid.Column>
        <Grid.Column width={4}>
          <Button
            color="green"
            icon="upload"
            href="/communities/new"
            content={i18next.t("New community")}
            floated="right"
          />
        </Grid.Column>
      </Grid.Row>
      <Grid.Row>
        <Grid.Column width={4}>
          <SearchAppFacets aggs={props.config.aggs} />
        </Grid.Column>
        <Grid.Column width={12}>
          <SearchAppResultsPane layoutOptions={props.config.layoutOptions} />
        </Grid.Column>
      </Grid.Row>
    </Grid>
  </Container>
);

const defaultComponents = {
  "BucketAggregation.element": RDMBucketAggregationElement,
  "BucketAggregationValues.element": CommunitiesFacetsValues,
  "SearchApp.facets": CommunitiesFacets,
  "ResultsList.item": ResultsItemTemplate,
  "ResultsGrid.item": ResultsGridItemTemplate,
  "SearchApp.layout": CommunitiesSearchLayout,
  "SearchBar.element": CommunitiesSearchBarElement,
  "SearchApp.results": CommunitiesResults,
};
const domContainer = document.getElementById("communities-search");
const facetsConfig = JSON.parse(domContainer.dataset.facetsConfig);

// Auto-initialize search app
const initSearchApp = createSearchAppInit(defaultComponents);
<commit_msg>theming: refactor search facets<commit_after>/*
 * This file is part of Invenio.
 * Copyright (C) 2016-2021 CERN.
 *
 * Invenio is free software; you can redistribute it and/or modify it
 * under the terms of the MIT License; see LICENSE file for more details.
 */

import React, { useState } from "react";
import {
  Input,
  Card,
  Container,
  Checkbox,
  Grid,
  Label,
  List,
  Icon,
  Item,
  Button,
  Segment,
  Accordion
} from "semantic-ui-react";
import { createSearchAppInit } from "@js/invenio_search_ui";
import {
  SearchAppFacets,
  SearchAppResultsPane,
} from "@js/invenio_search_ui/components";
import {
  BucketAggregation,
  SearchBar,
  Count,
  Sort,
  ResultsList,
  Pagination,
  ResultsPerPage,
} from "react-searchkit";
import { i18next } from "@translations/invenio_communities/i18next";

function ResultsGridItemTemplate({ result, index }) {
  return (
    <Card fluid key={index} href={`/communities/${result.metadata.id}`}>
      <Card.Content>
        <Card.Header>{result.metadata.title}</Card.Header>
        <Card.Description>

        </Card.Description>
      </Card.Content>
    </Card>
  );
}

function ResultsItemTemplate({ result, index }) {
  return (
    <Item key={index}>
      <Item.Content>
        <Button
          compact
          size="small"
          floated="right"
          icon
          labelPosition="left"
          href={`/communities/${result.id}`}
        >
          <Icon name="eye" />
          {i18next.t("View")}
        </Button>
        <Item.Header href={`/communities/${result.id}`}>
          {result.metadata.title}
        </Item.Header>
        <Item.Description>

        </Item.Description>
      </Item.Content>
    </Item>
  );
}

export const CommunitiesResults = ({
  sortOptions,
  paginationOptions,
  currentResultsState,
}) => {
  const { total } = currentResultsState.data;
  return (
    total && (
      <Grid>
        <Grid.Row>
          <Grid.Column width={16}>
            <Segment>
              <Grid>
                <Grid.Row
                  verticalAlign="middle"
                  className="small padding-tb-5 deposit-result-header"
                >
                  <Grid.Column width={4}>
                    <Count
                      label={() => (
                        <>
                          {i18next.t("{{total}} result(s) found", {
                            total: total,
                          })}
                        </>
                      )}
                    />
                  </Grid.Column>
                  <Grid.Column
                    width={12}
                    textAlign="right"
                    className="padding-r-5"
                  >
                    {sortOptions && (
                      <Sort
                        values={sortOptions}
                        label={(cmp) => (
                          <>
                            {i18next.t("Sort by {{cmp}}", {
                              cmp: cmp,
                            })}
                          </>
                        )}
                      />
                    )}
                  </Grid.Column>
                </Grid.Row>
                <Grid.Row>
                  <Grid.Column>
                    <ResultsList />
                  </Grid.Column>
                </Grid.Row>
              </Grid>
            </Segment>
          </Grid.Column>
        </Grid.Row>
        <Grid.Row verticalAlign="middle">
          <Grid.Column width={4}></Grid.Column>
          <Grid.Column width={8} textAlign="center">
            <Pagination
              options={{
                size: "mini",
                showFirst: false,
                showLast: false,
              }}
            />
          </Grid.Column>
          <Grid.Column textAlign="right" width={4}>
            <ResultsPerPage
              values={paginationOptions.resultsPerPage}
              label={(cmp) => (
                <>
                  {i18next.t("{{cmp}} results per page", {
                    cmp: cmp,
                  })}
                </>
              )}
            />
          </Grid.Column>
        </Grid.Row>
      </Grid>
    )
  );
};

export const CommunitiesSearchBarElement = ({
  placeholder: passedPlaceholder,
  queryString,
  onInputChange,
  executeSearch,
}) => {
  const placeholder = passedPlaceholder || i18next.t("Search");
  const onBtnSearchClick = () => {
    executeSearch();
  };
  const onKeyPress = (event) => {
    if (event.key === "Enter") {
      executeSearch();
    }
  };
  return (
    <Input
      action={{
        icon: "search",
        onClick: onBtnSearchClick,
        className: "search",
      }}
      fluid
      placeholder={placeholder}
      onChange={(event, { value }) => {
        onInputChange(value);
      }}
      value={queryString}
      onKeyPress={onKeyPress}
    />
  );
};

const CommunitiesFacets = ({ aggs, currentResultsState }) => {
  return (
    <>
      {aggs.map((agg) => {
        return (

            <BucketAggregation title={agg.title} agg={agg} />

        );
      })}
    </>
  );
};

export const RDMCommunityParentFacetValue = ({
  bucket,
  keyField,
  isSelected,
  childAggCmps,
  onFilterClicked,
}) => {
  const [isActive, setIsActive] = useState(false);

  return (
    <Accordion className="rdm-multi-facet">
      <Accordion.Title onClick={() => {}} key={`panel-${bucket.label}`}
      active={isActive}
      className="facet-wrapper parent"
      >
        <List.Content className="facet-wrapper">
        <Icon name="angle right" onClick={() => setIsActive(!isActive)}/>
        <Checkbox
            label={bucket.label || keyField}
            id={`${keyField}-facet-checkbox`}
            aria-describedby={`${keyField}-count`}
            value={keyField}
            checked={isSelected}
            onClick={() => onFilterClicked(keyField)}
          />
          <Label circular id={`${keyField}-count`} className="facet-count">
            {bucket.doc_count}

        </List.Content>
      </Accordion.Title>
      <Accordion.Content active={isActive}>{childAggCmps}</Accordion.Content>
    </Accordion>
  );
};

export const RDMCommunityFacetValue = ({
  bucket,
  keyField,
  isSelected,
  onFilterClicked,
}) => {
  return (
    <>
      <List.Content className="facet-wrapper">
        <Checkbox
          onClick={() => onFilterClicked(keyField)}
          label={bucket.label || keyField}
          id={`${keyField}-facet-checkbox`}
          aria-describedby={`${keyField}-count`}
          value={keyField}
          checked={isSelected}
        />
        <Label circular id={`${keyField}-count`} className="facet-count">
          {bucket.doc_count}

      </List.Content>
    </>
  );
};

export const CommunitiesFacetsValues = ({
  bucket,
  isSelected,
  onFilterClicked,
  getChildAggCmps,
}) => {
  const childAggCmps = getChildAggCmps(bucket);
  const hasChildren = childAggCmps && childAggCmps.props.buckets.length > 0;
  const keyField = bucket.key_as_string ? bucket.key_as_string : bucket.key;
  return (
    <List.Item key={bucket.key}>
      {hasChildren ? (
        <RDMCommunityParentFacetValue
          bucket={bucket}
          keyField={keyField}
          isSelected={isSelected}
          childAggCmps={childAggCmps}
          onFilterClicked={onFilterClicked}
        />
      ) : (
        <RDMCommunityFacetValue
          bucket={bucket}
          keyField={keyField}
          isSelected={isSelected}
          onFilterClicked={onFilterClicked}
        />
      )}
    </List.Item>
  );
};

const RDMBucketAggregationElement = ({ title, containerCmp }) => {
  return (
    <Card className="borderless facet">
      <Card.Content>
        <Card.Header>{title}</Card.Header>
      </Card.Content>
      <Card.Content>{containerCmp}</Card.Content>
    </Card>
  );
};
export const CommunitiesSearchLayout = (props) => (
  <Container>
    <Grid>
      <Grid.Row columns={3}>
        <Grid.Column width={4} />
        <Grid.Column width={8}>
          <SearchBar placeholder={i18next.t("Search communities...")} />
        </Grid.Column>
        <Grid.Column width={4}>
          <Button
            color="green"
            icon="upload"
            href="/communities/new"
            content={i18next.t("New community")}
            floated="right"
          />
        </Grid.Column>
      </Grid.Row>
      <Grid.Row>
        <Grid.Column width={4}>
          <SearchAppFacets aggs={props.config.aggs} />
        </Grid.Column>
        <Grid.Column width={12}>
          <SearchAppResultsPane layoutOptions={props.config.layoutOptions} />
        </Grid.Column>
      </Grid.Row>
    </Grid>
  </Container>
);

const defaultComponents = {
  "BucketAggregation.element": RDMBucketAggregationElement,
  "BucketAggregationValues.element": CommunitiesFacetsValues,
  "SearchApp.facets": CommunitiesFacets,
  "ResultsList.item": ResultsItemTemplate,
  "ResultsGrid.item": ResultsGridItemTemplate,
  "SearchApp.layout": CommunitiesSearchLayout,
  "SearchBar.element": CommunitiesSearchBarElement,
  "SearchApp.results": CommunitiesResults,
};
const domContainer = document.getElementById("communities-search");
const facetsConfig = JSON.parse(domContainer.dataset.facetsConfig);

// Auto-initialize search app
const initSearchApp = createSearchAppInit(defaultComponents);
<|endoftext|>


<commit_before>// Copyright 2015 Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.

var layouts = require('../utils/constants').stereoLayouts;

/**
 * @class sTER
 * @module PNG
 * @submodule PNGChunks
 */
module.exports = {

	/**
	 * Gets the sequence
	 *
	 * @method getSequence
	 * @return {int}
	 */
	getSequence: function () {
		return 220;
	},

	/**
	 * Gets the stereo-layout mode
	 *
	 * @method getLayout
	 * @return {int}
	 */
	getLayout: function () {
		return this._layout || layouts.CROSS_FUSE;
	},

	/**
	 * Sets the stereo-layout mode
	 *
	 * @method setLayout
	 * @param {int} layout Mode of layout
	 */
	setLayout: function (layout) {
		if ([layouts.CROSS_FUSE, layouts.DIVERGING_FUSE].indexOf(layout) === -1) {
			throw new Error('Layout identifier ' + layout + ' is not valid.');
		}
		this._layout = layout;
	},

	/**
	 * Is layout in cross-fuse mode?
	 *
	 * @method isLayoutCrossFuse
	 * @return {boolean}
	 */
	isLayoutCrossFuse: function () {
		return (this._layout === layouts.CROSS_FUSE);
	},

	/**
	 * Is layout in diverging-fuse mode?
	 *
	 * @method isLayoutDivergingFuse
	 * @return {boolean}
	 */
	isLayoutDivergingFuse: function () {
		return (this._layout === layouts.DIVERGING_FUSE);
	},

	/**
	 * Parsing of chunk data
	 *
	 * Phase 1
	 *
	 * @method parse
	 * @param {BufferedStream} stream Data stream
	 * @param {int} length Length of chunk data
	 * @param {boolean} strict Should parsing be strict?
	 * @param {object} options Decoding options
	 */
	parse: function (stream, length, strict, options) {

		// Validation
		if (strict && (this.getFirstChunk(this.getType(), false) !== null)) {
			throw new Error('Only one ' + this.getType() + ' is allowed in the data.');
		}

		this.setLayout(stream.readUInt8());
	},

	/**
	 * Gathers chunk-data from decoded chunks
	 *
	 * Phase 5
	 *
	 * @static
	 * @method decodeData
	 * @param {object} data Data-object that will be used to export values
	 * @param {boolean} strict Should parsing be strict?
	 * @param {object} options Decoding options
	 */
	decodeData: function (data, strict, options) {

		var chunks = this.getChunksByType(this.getType());

		if (!chunks) {
			return ;
		}

		if (strict && (chunks.length !== 1)) {
			throw new Error('Not more than one chunk allowed for ' + this.getType() + '.');
		}

		data.volatile = data.volatile || {};
		data.volatile.stereo = {
			layout: chunks[0].getLayout()
		};
	},

	/**
	 * Returns a list of chunks to be added to the data-stream
	 *
	 * Phase 1
	 *
	 * @static
	 * @method encodeData
	 * @param {Buffer} image Image data
	 * @param {object} options Encoding options
	 * @return {Chunk[]} List of chunks to encode
	 */
	encodeData: function (image, options) {

		if (options.stereo) {

			var chunk = this.createChunk(this.getType(), this.getChunks());

			if (options.stereo.layout !== undefined) {
				chunk.setLayout(options.stereo.layout);
			}

			return [chunk];
		} else {
			return [];
		}
	},

	/**
	 * Composing of chunk data
	 *
	 * Phase 4
	 *
	 * @method compose
	 * @param {BufferedStream} stream Data stream
	 * @param {object} options Encoding options
	 */
	compose: function (stream, options) {
		stream.writeUInt8(this.getLayout());
	}
};
<commit_msg>Add comments to the sTER chunk<commit_after>// Copyright 2015 Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.

var layouts = require('../utils/constants').stereoLayouts;

/**
 * @class sTER
 * @module PNG
 * @submodule PNGChunks
 */
module.exports = {

	/**
	 * Gets the sequence
	 *
	 * @method getSequence
	 * @return {int}
	 */
	getSequence: function () {
		return 220;
	},

	/**
	 * Gets the stereo-layout mode
	 *
	 * @method getLayout
	 * @return {int}
	 */
	getLayout: function () {
		return this._layout || layouts.CROSS_FUSE;
	},

	/**
	 * Sets the stereo-layout mode
	 *
	 * @method setLayout
	 * @param {int} layout Mode of layout
	 */
	setLayout: function (layout) {
		if ([layouts.CROSS_FUSE, layouts.DIVERGING_FUSE].indexOf(layout) === -1) {
			throw new Error('Layout identifier ' + layout + ' is not valid.');
		}
		this._layout = layout;
	},

	/**
	 * Is layout in cross-fuse mode?
	 *
	 * In this mode, the left side of the image is for the right eye,
	 * and the right side of the image is for the left eye.
	 *
	 * @method isLayoutCrossFuse
	 * @return {boolean}
	 */
	isLayoutCrossFuse: function () {
		return (this._layout === layouts.CROSS_FUSE);
	},

	/**
	 * Is layout in diverging-fuse mode?
	 *
	 * In this mode, the left side of the image is for the left eye,
	 * and the right side of the image is for the right eye.
	 *
	 * @method isLayoutDivergingFuse
	 * @return {boolean}
	 */
	isLayoutDivergingFuse: function () {
		return (this._layout === layouts.DIVERGING_FUSE);
	},

	/**
	 * Parsing of chunk data
	 *
	 * Phase 1
	 *
	 * @method parse
	 * @param {BufferedStream} stream Data stream
	 * @param {int} length Length of chunk data
	 * @param {boolean} strict Should parsing be strict?
	 * @param {object} options Decoding options
	 */
	parse: function (stream, length, strict, options) {

		// Validation
		if (strict && (this.getFirstChunk(this.getType(), false) !== null)) {
			throw new Error('Only one ' + this.getType() + ' is allowed in the data.');
		}

		this.setLayout(stream.readUInt8());
	},

	/**
	 * Gathers chunk-data from decoded chunks
	 *
	 * Phase 5
	 *
	 * @static
	 * @method decodeData
	 * @param {object} data Data-object that will be used to export values
	 * @param {boolean} strict Should parsing be strict?
	 * @param {object} options Decoding options
	 */
	decodeData: function (data, strict, options) {

		var chunks = this.getChunksByType(this.getType());

		if (!chunks) {
			return ;
		}

		if (strict && (chunks.length !== 1)) {
			throw new Error('Not more than one chunk allowed for ' + this.getType() + '.');
		}

		data.volatile = data.volatile || {};
		data.volatile.stereo = {
			layout: chunks[0].getLayout()
		};
	},

	/**
	 * Returns a list of chunks to be added to the data-stream
	 *
	 * Phase 1
	 *
	 * @static
	 * @method encodeData
	 * @param {Buffer} image Image data
	 * @param {object} options Encoding options
	 * @return {Chunk[]} List of chunks to encode
	 */
	encodeData: function (image, options) {

		if (options.stereo) {

			var chunk = this.createChunk(this.getType(), this.getChunks());

			if (options.stereo.layout !== undefined) {
				chunk.setLayout(options.stereo.layout);
			}

			return [chunk];
		} else {
			return [];
		}
	},

	/**
	 * Composing of chunk data
	 *
	 * Phase 4
	 *
	 * @method compose
	 * @param {BufferedStream} stream Data stream
	 * @param {object} options Encoding options
	 */
	compose: function (stream, options) {
		stream.writeUInt8(this.getLayout());
	}
};
<|endoftext|>


<commit_before>import Pastel from 'pastel-lang';
import CraftyBlock from '../CraftyBlock/CraftyBlock.js';
import CraftyBlockSpec from '../CraftyBlock/CraftyBlockSpec.js';
import CraftyStore from '../../stores/CraftyStore.js';
import CraftyBlockMenu from './CraftyBlockMenu.js';
import CraftyBlockEvents from './CraftyBlockEvents.js';

/**
 * CraftyBlock Manager Class
 *
 * Manages existing and new blocks, and their positions
 *
 * @exports CraftyBlockManager
 */
export default class CraftyBlockManager {
    constructor(stage) {
        this.stage = stage;
        this.rootBlocks = [];
        this.menu = new CraftyBlockMenu();

        //  Add Block Event Listeners
        this.addBlockEventListeners();

        //  Load Saved Tree from CraftyStore
        this.loadTree();

        //  Add CraftyStore Listener for changes from editor
        CraftyStore.addChangeListener(caller => {
            if (caller == "editor") {
                this.loadTree();
            } else {
                console.log(`DEBUG::: Ignoring change from ${caller}`);
            }
        });

        //  React to block events that results in canvas change
        CraftyBlockEvents.on('canvaschange', _ => {
            let rootTree = new Pastel.Node();
            this.rootBlocks.forEach( block => {
                let tree = this.treefy(block);
                rootTree.addChild(tree);
            });
            CraftyStore.set('tree', rootTree);
            CraftyStore.emitChange("canvasmanager");
        });

        console.log("DEBUG::: Initialized CraftyBlockManager!");
    }

    addBlockEventListeners() {
        CraftyBlockEvents.on('dragready', onDragReady.bind(this));
        CraftyBlockEvents.on('dragstart', onDragStart.bind(this));
        CraftyBlockEvents.on('dragend', onDragEnd.bind(this));
        CraftyBlockEvents.on('clickblock', block => {
            if (block.folded) {
                block.unfold();
            } else {
                block.fold();
            }
            //this.menu.toggle(block);
            //this.stage.addChild(this.menu);
        });
        CraftyBlockEvents.on('clickdelete', block => {
            this.removeBlock(block);
            CraftyBlockEvents.emit('canvaschange');
        });

        function onDragReady(block) {
            //  if block is in sidebar, create copy
            block.originalAddress = this.getAddress(block);

            if (block.originalAddress == -1) {
                this.stage.sidebar.addChildAt(block.clone(),1);
                block.render();
            }
        }

        function onDragStart(block) {

            if (block.parent instanceof CraftyBlock) {
                block.parent.removeChildBlock(block);
            }

            this.stage.addChild(block);
            //  TODO: Disable auto render for this case
        }

        function onDragEnd(event) {
            //  called after block is placed according to corresponding mouse location
            let block = event.target;
            let targetBlock = event.targetBlock;
            let newAddress = this.getAddress(block);
            let validDrag = true;

            if(targetBlock) {
                console.log("Attaching block to target...");
                block.attachTo(targetBlock);
            }

            let isAddressEqual = (block.originalAddress.length == newAddress.length) && block.originalAddress.every( (element, index) => element === newAddress[index] );

            //  only interested in when block address changed
            if (!isAddressEqual) {

                //  when block is no longer a root block
                if (block.originalAddress.length == 1 && newAddress.length != 1) {
                    //  remove block from rootBlocks
                    this.rootBlocks.splice(this.rootBlocks.indexOf(block),1);
                }

                //  if the block is sidebar created
                if (block.originalAddress[0] == -1) {
                    //  check mouse location is inside sidebar
                    let relativeMousePosition = event.data.getLocalPosition(this.stage);
                    if (this.stage.sidebar.containsPosition(relativeMousePosition)) {
                        //  remove block out of existence
                        console.log("Mouse inside sidebar");
                        this.removeBlock(block);
                        validDrag = false;
                    }
                }
                //  if the block is valid, i.e. not removed from sidebar
                if (validDrag) {
                    //  if the block is on stage
                    if (newAddress[0] == -2) {
                        //  add to rootBlocks
                        this.rootBlocks.push(block);
                        //  no need to addToStage since it is already done during moving
                    }

                    //  emit canvas changed
                    CraftyBlockEvents.emit('canvaschange');
                }
            }

            block.originalAddress = undefined;
        }
    }

    /**
     * Loads saved tree from CraftyStore, blockifies trees, and positions the blocks
     */
    loadTree() {
        //  empty stage first
        this.emptyStage();

        let savedTree = CraftyStore.get('tree');

        this.rootBlocks = this.blockify(savedTree);

        const MARGIN_LEFT = 70;
        const MARGIN_TOP = 150;
        const SPACING = 15;
        let blockPosition = MARGIN_TOP;

        //  Set position of blocks and add to stage
        this.rootBlocks.forEach( block => {
            block.position.x = MARGIN_LEFT;
            block.position.y = blockPosition;
            this.stage.addChild(block);
            blockPosition += block.height + SPACING;
        });

        console.log("DEBUG::: block-loading saved tree is done!");
    }

    /**
     * Delete all crafty blocks on stage
     */
    emptyStage() {
        //  iterate in reverse order
        for (let i=this.rootBlocks.length-1; i>=0;i--) {
            this.removeBlock(this.rootBlocks[i]);
        }
    }

    /**
     * Removes a block out of existence
     */
    removeBlock(block) {
        if (block.parent instanceof CraftyBlock) {
            block.parent.removeChildBlock(block);
        } else {
            let index = this.rootBlocks.indexOf(block);
            if (index > -1) {
                this.rootBlocks.splice(index,1);
            }
            this.stage.removeChild(block);
        }
    }

    /**
     * Transform Crafty block into pastel's parsed tree
     */
    treefy(block) {
        let tree;

        let blockName = (block.type == CraftyBlock.PARAMETER) ? "{" + block.name + "}" : block.name;
        let token = new Pastel.Token(Pastel.Token.ID, blockName);

        if (block.type == CraftyBlock.FUNCTION) {
            tree = new Pastel.Node();
            let subtree = new Pastel.Node(token);

            tree.addChild(subtree);

            block.childBlocks.forEach( blocks => {
                tree.addChild(this.treefy(blocks[0]));
            });
        } else {
            tree = new Pastel.Node(token);
        }

        return tree;
    }

    /**
     * Transforms pastel's parsed tree from into Crafty blocks
     */
    blockify(node, index = 0) {
        //  for first try (whole stage), divide into groups and perform blockify on each subtree
        if (!node.parent) {
            //  for each children(subtree), map its blockified version
            //let blocks = node.children.map ( childNode => this.blockify(childNode) );
            let blocks = [];
            node.children.forEach( (childNode,index) => {
                let block = this.blockify(childNode);
                block.order = index;
                blocks.push(block);
            });

            return blocks;
        }
        else {
            //console.log("DEBUG::: blockifying...");
            if (!node.getData()) {
                let children = node.getChildren();
                let functionBlock;
                let childBlocks = [];
                children.forEach( (childNode, idx) => {
                    if (idx == 0) {
                        functionBlock = this.blockify(childNode, idx);
                    } else {
                        childBlocks.push(this.blockify(childNode, idx));
                    }
                });

                childBlocks.forEach( (block,index) => {
                    functionBlock.addChildBlock(block,index);
                });

                return functionBlock;
            }
            else {
                let blockInfo;
                if (index == 0) {
                    blockInfo = CraftyBlockSpec.functionWithName(node.getData().data);
                }
                else {
                    blockInfo = new CraftyBlockSpec(node.getData().data,CraftyBlock.CONSTANT);
                }
                return new CraftyBlock(blockInfo);
            }
        }
    }

    getAddress(block) {
        let address = [];
        if (block.parent instanceof CraftyBlock) {
            address = this.getAddress(block.parent);
            address.push(block.parent.getChildBlockIndex(block));
        } else {
            let sidebarIndex = this.stage.sidebar.children.indexOf(block);
            if (sidebarIndex > -1) {
                address.push(-1);
            }
            else {
                let stageIndex = this.rootBlocks.indexOf(block);
                if (stageIndex > -1) {
                    address.push(stageIndex);
                } else {
                    // -2 means is on stage temporarily
                    address.push(-2);
                }
            }
        }

        return address;
    }

    getBlockAt(address) {
        if (address[0] < 0) {
            throw new Error("Invalid address!!");
        }

        let targetBlock = this.rootBlocks[address.shift()];
        while (address.length > 0) {
            targetBlock = targetBlock.childBlocks[address.shift()][0];
        }

        return targetBlock;
    }
}
<commit_msg>Change block addition position<commit_after>import Pastel from 'pastel-lang';
import CraftyBlock from '../CraftyBlock/CraftyBlock.js';
import CraftyBlockSpec from '../CraftyBlock/CraftyBlockSpec.js';
import CraftyStore from '../../stores/CraftyStore.js';
import CraftyBlockMenu from './CraftyBlockMenu.js';
import CraftyBlockEvents from './CraftyBlockEvents.js';

/**
 * CraftyBlock Manager Class
 *
 * Manages existing and new blocks, and their positions
 *
 * @exports CraftyBlockManager
 */
export default class CraftyBlockManager {
    constructor(stage) {
        this.stage = stage;
        this.rootBlocks = [];
        this.menu = new CraftyBlockMenu();

        //  Add Block Event Listeners
        this.addBlockEventListeners();

        //  Load Saved Tree from CraftyStore
        this.loadTree();

        //  Add CraftyStore Listener for changes from editor
        CraftyStore.addChangeListener(caller => {
            if (caller == "editor") {
                this.loadTree();
            } else {
                console.log(`DEBUG::: Ignoring change from ${caller}`);
            }
        });

        //  React to block events that results in canvas change
        CraftyBlockEvents.on('canvaschange', _ => {
            let rootTree = new Pastel.Node();
            this.rootBlocks.forEach( block => {
                let tree = this.treefy(block);
                rootTree.addChild(tree);
            });
            CraftyStore.set('tree', rootTree);
            CraftyStore.emitChange("canvasmanager");
        });

        console.log("DEBUG::: Initialized CraftyBlockManager!");
    }

    addBlockEventListeners() {
        CraftyBlockEvents.on('dragready', onDragReady.bind(this));
        CraftyBlockEvents.on('dragstart', onDragStart.bind(this));
        CraftyBlockEvents.on('dragend', onDragEnd.bind(this));
        CraftyBlockEvents.on('clickblock', block => {
            if (block.folded) {
                block.unfold();
            } else {
                block.fold();
            }
            //this.menu.toggle(block);
            //this.stage.addChild(this.menu);
        });
        CraftyBlockEvents.on('clickdelete', block => {
            this.removeBlock(block);
            CraftyBlockEvents.emit('canvaschange');
        });

        function onDragReady(block) {
            //  if block is in sidebar, create copy
            block.originalAddress = this.getAddress(block);

            if (block.originalAddress == -1) {
                this.stage.sidebar.addChildAt(block.clone(),1);
                block.render();
            }
        }

        function onDragStart(block) {

            if (block.parent instanceof CraftyBlock) {
                block.parent.removeChildBlock(block);
            }

            this.stage.addChild(block);
            //  TODO: Disable auto render for this case
        }

        function onDragEnd(event) {
            //  called after block is placed according to corresponding mouse location
            let block = event.target;
            let targetBlock = event.targetBlock;
            let newAddress = this.getAddress(block);
            let validDrag = true;

            if(targetBlock) {
                console.log("Attaching block to target...");
                block.attachTo(targetBlock);
            }

            let isAddressEqual = (block.originalAddress.length == newAddress.length) && block.originalAddress.every( (element, index) => element === newAddress[index] );

            //  only interested in when block address changed
            if (!isAddressEqual) {

                //  when block is no longer a root block
                if (block.originalAddress.length == 1 && newAddress.length != 1) {
                    //  remove block from rootBlocks
                    this.rootBlocks.splice(this.rootBlocks.indexOf(block),1);
                }

                //  if the block is sidebar created
                if (block.originalAddress[0] == -1) {
                    //  check mouse location is inside sidebar
                    let relativeMousePosition = event.data.getLocalPosition(this.stage);
                    if (this.stage.sidebar.containsPosition(relativeMousePosition)) {
                        //  remove block out of existence
                        console.log("Mouse inside sidebar");
                        this.removeBlock(block);
                        validDrag = false;
                    }
                }
                //  if the block is valid, i.e. not removed from sidebar
                if (validDrag) {
                    //  if the block is on stage
                    if (newAddress[0] == -2) {
                        //  add to rootBlocks right after original root block position
                        this.rootBlocks.splice(block.originalAddress[0]+1,0,block);
                        //  no need to addToStage since it is already done during moving
                    }

                    //  emit canvas changed
                    CraftyBlockEvents.emit('canvaschange');
                }
            }

            block.originalAddress = undefined;
        }
    }

    /**
     * Loads saved tree from CraftyStore, blockifies trees, and positions the blocks
     */
    loadTree() {
        //  empty stage first
        this.emptyStage();

        let savedTree = CraftyStore.get('tree');

        this.rootBlocks = this.blockify(savedTree);

        const MARGIN_LEFT = 70;
        const MARGIN_TOP = 150;
        const SPACING = 15;
        let blockPosition = MARGIN_TOP;

        //  Set position of blocks and add to stage
        this.rootBlocks.forEach( block => {
            block.position.x = MARGIN_LEFT;
            block.position.y = blockPosition;
            this.stage.addChild(block);
            blockPosition += block.height + SPACING;
        });

        console.log("DEBUG::: block-loading saved tree is done!");
    }

    /**
     * Delete all crafty blocks on stage
     */
    emptyStage() {
        //  iterate in reverse order
        for (let i=this.rootBlocks.length-1; i>=0;i--) {
            this.removeBlock(this.rootBlocks[i]);
        }
    }

    /**
     * Removes a block out of existence
     */
    removeBlock(block) {
        if (block.parent instanceof CraftyBlock) {
            block.parent.removeChildBlock(block);
        } else {
            let index = this.rootBlocks.indexOf(block);
            if (index > -1) {
                this.rootBlocks.splice(index,1);
            }
            this.stage.removeChild(block);
        }
    }

    /**
     * Transform Crafty block into pastel's parsed tree
     */
    treefy(block) {
        let tree;

        let blockName = (block.type == CraftyBlock.PARAMETER) ? "{" + block.name + "}" : block.name;
        let token = new Pastel.Token(Pastel.Token.ID, blockName);

        if (block.type == CraftyBlock.FUNCTION) {
            tree = new Pastel.Node();
            let subtree = new Pastel.Node(token);

            tree.addChild(subtree);

            block.childBlocks.forEach( blocks => {
                tree.addChild(this.treefy(blocks[0]));
            });
        } else {
            tree = new Pastel.Node(token);
        }

        return tree;
    }

    /**
     * Transforms pastel's parsed tree from into Crafty blocks
     */
    blockify(node, index = 0) {
        //  for first try (whole stage), divide into groups and perform blockify on each subtree
        if (!node.parent) {
            //  for each children(subtree), map its blockified version
            //let blocks = node.children.map ( childNode => this.blockify(childNode) );
            let blocks = [];
            node.children.forEach( (childNode,index) => {
                let block = this.blockify(childNode);
                block.order = index;
                blocks.push(block);
            });

            return blocks;
        }
        else {
            //console.log("DEBUG::: blockifying...");
            if (!node.getData()) {
                let children = node.getChildren();
                let functionBlock;
                let childBlocks = [];
                children.forEach( (childNode, idx) => {
                    if (idx == 0) {
                        functionBlock = this.blockify(childNode, idx);
                    } else {
                        childBlocks.push(this.blockify(childNode, idx));
                    }
                });

                childBlocks.forEach( (block,index) => {
                    functionBlock.addChildBlock(block,index);
                });

                return functionBlock;
            }
            else {
                let blockInfo;
                if (index == 0) {
                    blockInfo = CraftyBlockSpec.functionWithName(node.getData().data);
                }
                else {
                    blockInfo = new CraftyBlockSpec(node.getData().data,CraftyBlock.CONSTANT);
                }
                return new CraftyBlock(blockInfo);
            }
        }
    }

    getAddress(block) {
        let address = [];
        if (block.parent instanceof CraftyBlock) {
            address = this.getAddress(block.parent);
            address.push(block.parent.getChildBlockIndex(block));
        } else {
            let sidebarIndex = this.stage.sidebar.children.indexOf(block);
            if (sidebarIndex > -1) {
                address.push(-1);
            }
            else {
                let stageIndex = this.rootBlocks.indexOf(block);
                if (stageIndex > -1) {
                    address.push(stageIndex);
                } else {
                    // -2 means is on stage temporarily
                    address.push(-2);
                }
            }
        }

        return address;
    }

    getBlockAt(address) {
        if (address[0] < 0) {
            throw new Error("Invalid address!!");
        }

        let targetBlock = this.rootBlocks[address.shift()];
        while (address.length > 0) {
            targetBlock = targetBlock.childBlocks[address.shift()][0];
        }

        return targetBlock;
    }
}
<|endoftext|>


<commit_before>import { FormattedRelative } from 'react-intl';
import React from 'react';
import PropTypes from 'prop-types';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import cn from 'classnames';
import s from './ProposalPreview.css';
import PollState from '../PollState';
import history from '../../history';
import ProposalState from '../ProposalState';
import { ICONS } from '../../constants';

import { getLastActivePoll } from '../../core/helpers';
// import { DOMParser } from 'xmldom';

class ProposalPreview extends React.Component {
  static propTypes = {
    proposal: PropTypes.shape({
      id: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired,
      state: PropTypes.string.isRequired,
      body: PropTypes.string.isRequired,
      publishedAt: PropTypes.string,
      pollOne: PropTypes.object,
      pollTwo: PropTypes.object,
      tags: PropTypes.arrayOf(PropTypes.object),
    }).isRequired,
    onClick: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    if (this.props.onClick) {
      const { id, state } = this.props.proposal;
      const poll = getLastActivePoll(state, this.props.proposal);
      this.props.onClick({ proposalId: id, pollId: poll.id });
    }
  }

  render() {
    if (!this.props.proposal) {
      return
;
    }
    // TODO move to state
    const poll = getLastActivePoll(
      this.props.proposal.state,
      this.props.proposal,
    );

    const pollPreview = [
      <svg key="0" viewBox="0 0 24 24" width="16px" height="16px" role="img">
        <path fill="none" stroke="#666" strokeWidth="2" d={ICONS.up} />
      </svg>,
      poll.upvotes,

        <PollState
          compact
          pollId={poll.id}
          allVoters={poll.allVoters}
          upvotes={poll.upvotes}
          downvotes={poll.downvotes}
          thresholdRef={poll.mode.thresholdRef}
          threshold={poll.threshold}
          unipolar={poll.mode.unipolar}
        />

,
    ];

    if (!poll.mode.unipolar) {
      pollPreview.push(poll.downvotes);
      pollPreview.push(
        <svg key="4" viewBox="0 0 24 24" width="16px" height="16px" role="img">
          <path
            fill="none"
            stroke="#666"
            strokeWidth="2"
            d={ICONS.up}
            transform="matrix(1 0 0 -1 0 24)"
          />,
        </svg>,
      );
    }

    return (

            {/* <PollPreview poll={poll} /> */}
            {

                {pollPreview}{' '}
                <ProposalState state={this.props.proposal.state} />

            }

                <FormattedRelative value={poll.endTime} />

{this.props.proposal.title}

              {/*

                {body}

 */}
              {/* eslint-disable jsx-a11y/no-static-element-interactions */}

                {this.props.proposal.tags &&
                  this.props.proposal.tags.map(
                    tag =>
                      tag && (
                         {
                            e.stopPropagation();
                            history.push(`/proposals/tagged/${tag.id}`);
                          }}
                          key={tag.id}
                          className={s.tag}
                        >{`${tag.displayName}`}
                      ),
                  )}

    );
    /* eslint-enable jsx-a11y/no-static-element-interactions */
  }
}

export default withStyles(s)(ProposalPreview);
<commit_msg>hotfix for feed<commit_after>import { FormattedRelative } from 'react-intl';
import React from 'react';
import PropTypes from 'prop-types';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import cn from 'classnames';
import s from './ProposalPreview.css';
import PollState from '../PollState';
import history from '../../history';
import ProposalState from '../ProposalState';
import { ICONS } from '../../constants';

import { getLastActivePoll } from '../../core/helpers';
// import { DOMParser } from 'xmldom';

class ProposalPreview extends React.Component {
  static propTypes = {
    proposal: PropTypes.shape({
      id: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired,
      state: PropTypes.string.isRequired,
      body: PropTypes.string.isRequired,
      publishedAt: PropTypes.string,
      pollOne: PropTypes.object,
      pollTwo: PropTypes.object,
      tags: PropTypes.arrayOf(PropTypes.object),
    }).isRequired,
    onClick: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    const { onClick, proposal } = this.props;
    if (onClick) {
      const { id, state } = proposal;
      const poll = getLastActivePoll(state, proposal);
      onClick({ proposalId: id, pollId: poll.id });
    }
  }

  render() {
    const { proposal } = this.props;
    if (!proposal) {
      return
;
    }
    // TODO move to state
    const poll = getLastActivePoll(proposal.state, proposal);

    if (!poll) {
      return
;
    }

    const pollPreview = [
      <svg key="0" viewBox="0 0 24 24" width="16px" height="16px" role="img">
        <path fill="none" stroke="#666" strokeWidth="2" d={ICONS.up} />
      </svg>,
      poll.upvotes,

        <PollState
          compact
          pollId={poll.id}
          allVoters={poll.allVoters}
          upvotes={poll.upvotes}
          downvotes={poll.downvotes}
          thresholdRef={poll.mode.thresholdRef}
          threshold={poll.threshold}
          unipolar={poll.mode.unipolar}
        />

,
    ];

    if (!poll.mode.unipolar) {
      pollPreview.push(poll.downvotes);
      pollPreview.push(
        <svg key="4" viewBox="0 0 24 24" width="16px" height="16px" role="img">
          <path
            fill="none"
            stroke="#666"
            strokeWidth="2"
            d={ICONS.up}
            transform="matrix(1 0 0 -1 0 24)"
          />,
        </svg>,
      );
    }

    return (

            {/* <PollPreview poll={poll} /> */}
            {

                {pollPreview} <ProposalState state={proposal.state} />

            }

                <FormattedRelative value={poll.endTime} />

{proposal.title}

              {/*

                {body}

 */}
              {/* eslint-disable jsx-a11y/no-static-element-interactions */}

                {proposal.tags &&
                  proposal.tags.map(
                    tag =>
                      tag && (
                         {
                            e.stopPropagation();
                            history.push(`/proposals/tagged/${tag.id}`);
                          }}
                          key={tag.id}
                          className={s.tag}
                        >{`${tag.displayName}`}
                      ),
                  )}

    );
    /* eslint-enable jsx-a11y/no-static-element-interactions */
  }
}

export default withStyles(s)(ProposalPreview);
<|endoftext|>


<commit_before>const fs = require('fs');
const glob = require('glob');
const isBuiltInModule = require('is-builtin-module');
const syncExec = require('sync-exec');
const ora = require('ora');
const logSymbols = require('log-symbols');
const argv = require('yargs').argv;
const request = require('sync-request');
const detective = require('detective');

/* Secure mode */

let secureMode = false;
if (argv.secure) secureMode = true;

/* File reader
 * Return contents of given file
 */
let readFile = (path) => {
    let content = fs.readFileSync(path, 'utf8');
    return content;
};

/* Get installed modules
 * Read dependencies array from package.json
 */

let getInstalledModules = () => {
    let content = JSON.parse(readFile('package.json'));
    let installedModules = [];

    for (let key of Object.keys(content.dependencies)) {
        installedModules.push({
            name: key,
            dev: false
        });
    }
    for (let key of Object.keys(content.devDependencies)) {
        installedModules.push({
            name: key,
            dev: true
        });
    }

    return installedModules;
};

/* Get all js files
 * Return path of all js files
 */
let getFiles = (path) => {
    return glob.sync("**/*.js", {'ignore': ['node_modules/**/*']});
};

/* Check for valid string - to stop malicious intentions */

let isValidModule = ({name, dev}) => {
    let regex = new RegExp("^([a-z0-9-_]{1,})$");
    return regex.test(name);
};

/* Find modules from file
 * Returns array of modules from a file
 */

let pattern = /require\((.*?)\)/g;

let getModulesFromFile = (path) => {
    let content = fs.readFileSync(path, 'utf8');
    let modules = detective(content);
    modules = modules.filter((module) => isValidModule(module));
    return modules;
};

/* Is test file?
 * [.spec.js, .test.js] are supported test file formats
 */

let isTestFile = (name) => {
    return (name.endsWith('.spec.js') || name.endsWith('.test.js'));
};

/* Dedup similar modules
 * Deduplicates list
 * Ignores/assumes type of the modules in list
*/

let deduplicateSimilarModules = (modules) => {
    let dedupedModules = [];
    let dedupedModuleNames = [];

    for (let module of modules) {
        if (dedupedModuleNames.indexOf(module.name) === -1) {
            dedupedModules.push(module);
            dedupedModuleNames.push(module.name);
        }
    }

    return dedupedModules;
};

/* Dedup modules
 * Divide modules into prod and dev
 * Deduplicates each list
 */

let deduplicate = (modules) => {
    let dedupedModules = [];

    let testModules = modules.filter(module => module.dev);
    dedupedModules = dedupedModules.concat(deduplicateSimilarModules(testModules));

    let prodModules = modules.filter(module => !module.dev);
    dedupedModules = dedupedModules.concat(deduplicateSimilarModules(prodModules));

    return dedupedModules;
};

/* Get used modules
 * Read all .js files and grep for modules
 */

let getUsedModules = () => {
    let files = getFiles();
    let usedModules = [];
    for (let fileName of files) {
        let modulesFromFile = getModulesFromFile(fileName);
        let dev = isTestFile(fileName);
        for (let name of modulesFromFile) usedModules.push({name, dev});
    }
    usedModules = deduplicate(usedModules);
    return usedModules;
};

/* Command runner
 * Run a given command
 */

let runCommand = (command) => {
    let response = syncExec(command);
    return !response.status; // status = 0 for success
};

/* Show pretty outputs
 * Use ora spinners to show what's going on
 */

let startSpinner = (message, type) => {
    let spinner = ora();
    spinner.text = message;
    spinner.color = type;
    spinner.start();
    return spinner;
};

let stopSpinner = (spinner, message, type) => {
    spinner.stop();
    if (!message) return;
    let symbol;
    if (type === 'red') symbol = logSymbols.error;
    else if (type === 'yellow') symbol = logSymbols.warning;
    else symbol = logSymbols.success;
    console.log(symbol, message);
};

/* Is module popular? - for secure mode */

const POPULARITY_THRESHOLD = 10000;
let isModulePopular = ({name, dev}) => {
    let url = 'https://api.npmjs.org/downloads/point/last-month/' + name;
    request('GET', url, (error, response, body) => {
        let downloads = JSON.parse(body).downloads;
        return (downloads > POPULARITY_THRESHOLD);
    });
};

/* Install module
 * Install given module
 */

let installModule = ({name, dev}) => {
    let spinner = startSpinner('Installing ' + name, 'green');
    if (secureMode && !isModulePopular(name)) {
        stopSpinner(spinner, name + ' not trusted', 'yellow');
        return;
    }

    let command = 'npm install ' + name + ' --save';
    let message = name + ' installed';

    if (dev) command += '-dev';
    if (dev) message += ' in devDependencies';

    let success = runCommand(command);
    if (success) stopSpinner(spinner, message, 'green');
    else stopSpinner(spinner, name + ' installation failed', 'yellow');
};

/* Uninstall module */

let uninstallModule = ({name, dev}) => {
    let spinner = startSpinner('Uninstalling ' + name, 'red');

    let command = 'npm uninstall ' + name + ' --save';
    let message = name + ' removed';

    if (dev) command += '-dev';
    if (dev) message += ' from devDependencies';

    runCommand(command);
    stopSpinner(spinner, message, 'red');
};

/* Remove built in/native modules */

let removeBuiltInModules = (modules) => {
    modules = modules.filter((module) => {
        return !isBuiltInModule(module.name);
    });
    return modules;
};

/* Remove local files that are required */

let removeLocalFiles = (modules) => {
    modules = modules.filter((module) => {
        return (module.name.indexOf('./') !== 0)
    });
    return modules;
};

/* Filter registry modules */

let filterRegistryModules = (modules) => {
    modules = removeBuiltInModules(modules);
    modules = removeLocalFiles(modules);
    return modules;
};

/* Get module names from array of module objects */

let getNamesFromModules = (modules) => {
    return modules.map(module => module.name);
};

/* Modules diff */

let diff = (first, second) => {
    let namesFromSecond = getNamesFromModules(second);
    return first.filter(module => namesFromSecond.indexOf(module.name) < 0);
};

/* Reinstall modules */

let reinstall = () => {
    let spinner = startSpinner('Cleaning up', 'green');
    runCommand('npm install');
    stopSpinner(spinner);
};

/* Public helper functions */

module.exports = {
    getInstalledModules,
    getUsedModules,
    filterRegistryModules,
    installModule,
    uninstallModule,
    diff,
    reinstall
};

<commit_msg>Cleaned up all unused vars<commit_after>const fs = require('fs');
const glob = require('glob');
const isBuiltInModule = require('is-builtin-module');
const syncExec = require('sync-exec');
const ora = require('ora');
const logSymbols = require('log-symbols');
const argv = require('yargs').argv;
const request = require('sync-request');
const detective = require('detective');

/* Secure mode */

let secureMode = false;
if (argv.secure) secureMode = true;

/* File reader
 * Return contents of given file
 */
let readFile = (path) => {
    let content = fs.readFileSync(path, 'utf8');
    return content;
};

/* Get installed modules
 * Read dependencies array from package.json
 */

let getInstalledModules = () => {
    let content = JSON.parse(readFile('package.json'));
    let installedModules = [];

    for (let key of Object.keys(content.dependencies)) {
        installedModules.push({
            name: key,
            dev: false
        });
    }
    for (let key of Object.keys(content.devDependencies)) {
        installedModules.push({
            name: key,
            dev: true
        });
    }

    return installedModules;
};

/* Get all js files
 * Return path of all js files
 */
let getFiles = () => {
    return glob.sync("**/*.js", {'ignore': ['node_modules/**/*']});
};

/* Check for valid string - to stop malicious intentions */

let isValidModule = ({name}) => {
    let regex = new RegExp("^([a-z0-9-_]{1,})$");
    return regex.test(name);
};

/* Find modules from file
 * Returns array of modules from a file
 */

let getModulesFromFile = (path) => {
    let content = fs.readFileSync(path, 'utf8');
    let modules = detective(content);
    modules = modules.filter((module) => isValidModule(module));
    return modules;
};

/* Is test file?
 * [.spec.js, .test.js] are supported test file formats
 */

let isTestFile = (name) => {
    return (name.endsWith('.spec.js') || name.endsWith('.test.js'));
};

/* Dedup similar modules
 * Deduplicates list
 * Ignores/assumes type of the modules in list
*/

let deduplicateSimilarModules = (modules) => {
    let dedupedModules = [];
    let dedupedModuleNames = [];

    for (let module of modules) {
        if (dedupedModuleNames.indexOf(module.name) === -1) {
            dedupedModules.push(module);
            dedupedModuleNames.push(module.name);
        }
    }

    return dedupedModules;
};

/* Dedup modules
 * Divide modules into prod and dev
 * Deduplicates each list
 */

let deduplicate = (modules) => {
    let dedupedModules = [];

    let testModules = modules.filter(module => module.dev);
    dedupedModules = dedupedModules.concat(deduplicateSimilarModules(testModules));

    let prodModules = modules.filter(module => !module.dev);
    dedupedModules = dedupedModules.concat(deduplicateSimilarModules(prodModules));

    return dedupedModules;
};

/* Get used modules
 * Read all .js files and grep for modules
 */

let getUsedModules = () => {
    let files = getFiles();
    let usedModules = [];
    for (let fileName of files) {
        let modulesFromFile = getModulesFromFile(fileName);
        let dev = isTestFile(fileName);
        for (let name of modulesFromFile) usedModules.push({name, dev});
    }
    usedModules = deduplicate(usedModules);
    return usedModules;
};

/* Command runner
 * Run a given command
 */

let runCommand = (command) => {
    let response = syncExec(command);
    return !response.status; // status = 0 for success
};

/* Show pretty outputs
 * Use ora spinners to show what's going on
 */

let startSpinner = (message, type) => {
    let spinner = ora();
    spinner.text = message;
    spinner.color = type;
    spinner.start();
    return spinner;
};

let stopSpinner = (spinner, message, type) => {
    spinner.stop();
    if (!message) return;
    let symbol;
    if (type === 'red') symbol = logSymbols.error;
    else if (type === 'yellow') symbol = logSymbols.warning;
    else symbol = logSymbols.success;
    console.log(symbol, message);
};

/* Is module popular? - for secure mode */

const POPULARITY_THRESHOLD = 10000;
let isModulePopular = ({name}) => {
    let url = 'https://api.npmjs.org/downloads/point/last-month/' + name;
    request('GET', url, (error, response, body) => {
        let downloads = JSON.parse(body).downloads;
        return (downloads > POPULARITY_THRESHOLD);
    });
};

/* Install module
 * Install given module
 */

let installModule = ({name, dev}) => {
    let spinner = startSpinner('Installing ' + name, 'green');
    if (secureMode && !isModulePopular(name)) {
        stopSpinner(spinner, name + ' not trusted', 'yellow');
        return;
    }

    let command = 'npm install ' + name + ' --save';
    let message = name + ' installed';

    if (dev) command += '-dev';
    if (dev) message += ' in devDependencies';

    let success = runCommand(command);
    if (success) stopSpinner(spinner, message, 'green');
    else stopSpinner(spinner, name + ' installation failed', 'yellow');
};

/* Uninstall module */

let uninstallModule = ({name, dev}) => {
    let spinner = startSpinner('Uninstalling ' + name, 'red');

    let command = 'npm uninstall ' + name + ' --save';
    let message = name + ' removed';

    if (dev) command += '-dev';
    if (dev) message += ' from devDependencies';

    runCommand(command);
    stopSpinner(spinner, message, 'red');
};

/* Remove built in/native modules */

let removeBuiltInModules = (modules) => {
    modules = modules.filter((module) => {
        return !isBuiltInModule(module.name);
    });
    return modules;
};

/* Remove local files that are required */

let removeLocalFiles = (modules) => {
    modules = modules.filter((module) => {
        return (module.name.indexOf('./') !== 0)
    });
    return modules;
};

/* Filter registry modules */

let filterRegistryModules = (modules) => {
    modules = removeBuiltInModules(modules);
    modules = removeLocalFiles(modules);
    return modules;
};

/* Get module names from array of module objects */

let getNamesFromModules = (modules) => {
    return modules.map(module => module.name);
};

/* Modules diff */

let diff = (first, second) => {
    let namesFromSecond = getNamesFromModules(second);
    return first.filter(module => namesFromSecond.indexOf(module.name) < 0);
};

/* Reinstall modules */

let reinstall = () => {
    let spinner = startSpinner('Cleaning up', 'green');
    runCommand('npm install');
    stopSpinner(spinner);
};

/* Public helper functions */

module.exports = {
    getInstalledModules,
    getUsedModules,
    filterRegistryModules,
    installModule,
    uninstallModule,
    diff,
    reinstall
};

<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import classNames from 'classnames';
import { compose, lifecycle } from 'recompose';
import _ from 'lodash/fp';

import Sidebar from '../../../presentational/Sidebar/Sidebar';
import toolbarSelector from './selectors';
import { setSidebarVisibility } from '../../../../ducks/set-sidebar-visibility';
import { closeSidebarOnUnmount, openSidebarOnMount } from '../../../../utils/HOCs/sidebar-handle';
import { mainPagesTitles } from '../../../../config/client-urls.constants'
import { formatNHSNumber } from '../../../../utils/table-helpers/table.utils'

const mapDispatchToProps = dispatch => ({ actions: bindActionCreators({ setSidebarVisibility }, dispatch) });

@connect(toolbarSelector, mapDispatchToProps)
@compose(lifecycle(closeSidebarOnUnmount), lifecycle(openSidebarOnMount))
class HeaderToolbar extends PureComponent {
  static propTypes = {
    isSidebarVisible: PropTypes.bool.isRequired,
    name: PropTypes.string.isRequired,
    gpName: PropTypes.string.isRequired,
    gpAddress: PropTypes.string.isRequired,
    dateOfBirth: PropTypes.string.isRequired,
    gender: PropTypes.string.isRequired,
    telephone: PropTypes.string.isRequired,
    userId: PropTypes.string.isRequired,
  };

  static contextTypes = {
    router: PropTypes.shape({
      history: PropTypes.object,
    }),
  };

  getState = hash => _.getOr(null, [hash])(mainPagesTitles);

  toggleSidebarVisibility = () => this.props.actions.setSidebarVisibility(!this.props.isSidebarVisible);

  render() {
    const { isSidebarVisible, name, gpName, gpAddress, dateOfBirth, gender, telephone, userId } = this.props;

    let breadcrumbs = null;
    const routingComponents = (this.context.router.route.location.pathname.split('?')[0]).split('/');
    let routerHash;
    do {
      routerHash = routingComponents.pop();
      breadcrumbs = this.getState(routerHash);
      if (breadcrumbs) break
    } while (routingComponents.length);

    return (

              <button className={classNames('btn-toggle-sidebar wrap-icon', { 'btn-toggle-sidebar-open': isSidebarVisible })} data-toggle="collapse" data-target="#sidebar-nav" aria-expanded="false" onClick={this.toggleSidebarVisibility}>

                Menu
              </button>

{name}

D.O.B. {dateOfBirth}

Phone: {telephone}

Gender: {gender}

NHS No. {formatNHSNumber(userId)}

{name}

Doctor: {gpName}

Address: {gpAddress}

        {isSidebarVisible && <Sidebar
          userId={userId}
          activeLink={routerHash}
        />}

    )
  }
}

export default HeaderToolbar;
<commit_msg>fix <HeaderToolbar /> and <Breadcrumbs /> when we load on the page different from patients-summary<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import classNames from 'classnames';
import { compose, lifecycle } from 'recompose';
import _ from 'lodash/fp';

import Sidebar from '../../../presentational/Sidebar/Sidebar';
import toolbarSelector from './selectors';
import { setSidebarVisibility } from '../../../../ducks/set-sidebar-visibility';
import { closeSidebarOnUnmount, openSidebarOnMount } from '../../../../utils/HOCs/sidebar-handle';
import { mainPagesTitles } from '../../../../config/client-urls.constants'
import { formatNHSNumber } from '../../../../utils/table-helpers/table.utils'
import { fetchPatientSummaryOnMount } from '../../../../utils/HOCs/fetch-patients.utils';
import { fetchPatientSummaryRequest } from '../../../../ducks/fetch-patient-summary.duck';

const mapDispatchToProps = dispatch => ({ actions: bindActionCreators({ setSidebarVisibility, fetchPatientSummaryRequest }, dispatch) });

@connect(toolbarSelector, mapDispatchToProps)
@compose(lifecycle(closeSidebarOnUnmount), lifecycle(openSidebarOnMount), lifecycle(fetchPatientSummaryOnMount))
class HeaderToolbar extends PureComponent {
  static propTypes = {
    isSidebarVisible: PropTypes.bool.isRequired,
    name: PropTypes.string.isRequired,
    gpName: PropTypes.string.isRequired,
    gpAddress: PropTypes.string.isRequired,
    dateOfBirth: PropTypes.string.isRequired,
    gender: PropTypes.string.isRequired,
    telephone: PropTypes.string.isRequired,
    userId: PropTypes.string.isRequired,
  };

  static contextTypes = {
    router: PropTypes.shape({
      history: PropTypes.object,
    }),
  };

  getState = hash => _.getOr(null, [hash])(mainPagesTitles);

  toggleSidebarVisibility = () => this.props.actions.setSidebarVisibility(!this.props.isSidebarVisible);

  render() {
    const { isSidebarVisible, name, gpName, gpAddress, dateOfBirth, gender, telephone, userId } = this.props;

    let breadcrumbs = null;
    const routingComponents = (this.context.router.route.location.pathname.split('?')[0]).split('/');
    let routerHash;
    do {
      routerHash = routingComponents.pop();
      breadcrumbs = this.getState(routerHash);
      if (breadcrumbs) break
    } while (routingComponents.length);

    return (

              <button className={classNames('btn-toggle-sidebar wrap-icon', { 'btn-toggle-sidebar-open': isSidebarVisible })} data-toggle="collapse" data-target="#sidebar-nav" aria-expanded="false" onClick={this.toggleSidebarVisibility}>

                Menu
              </button>

{name}

D.O.B. {dateOfBirth}

Phone: {telephone}

Gender: {gender}

NHS No. {formatNHSNumber(userId)}

{name}

Doctor: {gpName}

Address: {gpAddress}

        {isSidebarVisible && <Sidebar
          userId={userId}
          activeLink={routerHash}
        />}

    )
  }
}

export default HeaderToolbar;
<|endoftext|>