<commit_before>/* global window */
/* eslint-disable import/no-extraneous-dependencies */
import { addParameters } from '@storybook/client-api';
import { getCustomElements, isValidComponent, isValidMetaData } from '@storybook/web-components';
import React from 'react';
import { render } from 'lit-html';

function mapData(data) {
  return data.map(item => ({
    name: item.name,
    type: { summary: item.type },
    required: '',
    description: item.description,
    defaultValue: { summary: item.default },
  }));
}

function isEmpty(obj) {
  return Object.entries(obj).length === 0 && obj.constructor === Object;
}

addParameters({
  docs: {
    extractProps: tagName => {
      const customElements = getCustomElements();
      if (isValidComponent(tagName) && isValidMetaData(customElements)) {
        const metaData = customElements.tags.find(
          tag => tag.name.toUpperCase() === tagName.toUpperCase()
        );
        const sections = {};
        if (metaData.attributes) {
          sections.attributes = mapData(metaData.attributes);
        }
        if (metaData.properties) {
          sections.props = mapData(metaData.properties);
        }
        if (metaData.events) {
          sections.events = mapData(metaData.events);
        }
        if (metaData.slots) {
          sections.slots = mapData(metaData.slots);
        }
        if (metaData.cssProperties) {
          sections.css = mapData(metaData.cssProperties);
        }
        return isEmpty(sections) ? false : { sections };
      }
      return false;
    },
    extractComponentDescription: tagName => {
      const customElements = getCustomElements();
      if (isValidComponent(tagName) && isValidMetaData(customElements)) {
        const metaData = customElements.tags.find(
          tag => tag.name.toUpperCase() === tagName.toUpperCase()
        );
        if (metaData && metaData.description) {
          return metaData.description;
        }
      }
      return false;
    },
    inlineStories: true,
    prepareForInline: storyFn => {
      class Story extends React.Component {
        constructor(props) {
          super(props);
          this.wrapperRef = React.createRef();
        }

        componentDidMount() {
          render(storyFn(), this.wrapperRef.current);
        }

        render() {
          return React.createElement('div', { ref: this.wrapperRef });
        }
      }

      return React.createElement(Story);
    },
  },
});
<commit_msg>fix(web-components): update key to reflect specification<commit_after>/* global window */
/* eslint-disable import/no-extraneous-dependencies */
import { addParameters } from '@storybook/client-api';
import { getCustomElements, isValidComponent, isValidMetaData } from '@storybook/web-components';
import React from 'react';
import { render } from 'lit-html';

function mapData(data) {
  return data.map(item => ({
    name: item.name,
    type: { summary: item.type },
    required: '',
    description: item.description,
    defaultValue: { summary: item.defaultValue }
  }));
}

function isEmpty(obj) {
  return Object.entries(obj).length === 0 && obj.constructor === Object;
}

addParameters({
  docs: {
    extractProps: tagName => {
      const customElements = getCustomElements();
      if (isValidComponent(tagName) && isValidMetaData(customElements)) {
        const metaData = customElements.tags.find(
          tag => tag.name.toUpperCase() === tagName.toUpperCase()
        );
        const sections = {};
        if (metaData.attributes) {
          sections.attributes = mapData(metaData.attributes);
        }
        if (metaData.properties) {
          sections.props = mapData(metaData.properties);
        }
        if (metaData.events) {
          sections.events = mapData(metaData.events);
        }
        if (metaData.slots) {
          sections.slots = mapData(metaData.slots);
        }
        if (metaData.cssProperties) {
          sections.css = mapData(metaData.cssProperties);
        }
        return isEmpty(sections) ? false : { sections };
      }
      return false;
    },
    extractComponentDescription: tagName => {
      const customElements = getCustomElements();
      if (isValidComponent(tagName) && isValidMetaData(customElements)) {
        const metaData = customElements.tags.find(
          tag => tag.name.toUpperCase() === tagName.toUpperCase()
        );
        if (metaData && metaData.description) {
          return metaData.description;
        }
      }
      return false;
    },
    inlineStories: true,
    prepareForInline: storyFn => {
      class Story extends React.Component {
        constructor(props) {
          super(props);
          this.wrapperRef = React.createRef();
        }

        componentDidMount() {
          render(storyFn(), this.wrapperRef.current);
        }

        render() {
          return React.createElement('div', { ref: this.wrapperRef });
        }
      }

      return React.createElement(Story);
    },
  },
});
<|endoftext|>


<commit_before>import Params from "./params";
import CustomComponent from "./customComponent";
import ComponentPort from "./componentPort";

import * as Registry from "./registry";
import * as FeatureRenderer2D from "../view/render2D/featureRenderer2D";
import Port from "../library/port";

/**
 * This class contains the component abstraction used in the interchange format and the
 * high level device model of the microfluidic.
 */
export default class Component {
    /**
     * Default Constructor
     * @param {string} type
     * @param {Params} params
     * @param {String} name
     * @param {string} mint
     * @param {String} id
     */
    constructor(type, params, name, mint, id = Component.generateID()) {
        if (params instanceof Params) {
            this.__params = params;
        } else {
            console.error("Params not an instance of Params Object");
        }

        this.__name = name;
        this.__id = id;
        this.__type = type;
        this.__entity = mint;
        //This stores the features that are a part of the component
        this.__features = [];
        //TODO: Need to figure out how to effectively search through these
        this.__bounds = null;
        this.__placed = false;
        this.__ports = new Map();
        this._componentPortTRenders = new Map();

        // TODO - Figure out how to use this for generic components
        this._xspan = 0;
        this._yspan = 0;

        //Create and set the ports here itself

        let cleanparamdata = {};
        for (let key in this.__params.parameters) {
            cleanparamdata[key] = this.__params.parameters[key].getValue();
        }

        let ports = Registry.featureSet.getComponentPorts(cleanparamdata, this.__type);
        if (ports !== undefined && ports.length >= 0 && ports !== null) {
            for (let i in ports) {
                this.setPort(ports[i].label, ports[i]);
            }
        }
    }
    /**
     * Gets the ports of the component
     * @returns {Port} Returns ports of the component
     * @memberof Component
     */
    get ports() {
        return this.__ports;
    }
    /**
     * Sets the port of the component
     * @param {} value
     * @returns {void}
     * @memberof Component
     */
    set ports(value) {
        this.__ports = value;
    }
    /**
     * Gets the place of the component
     * @returns {Number} Returns the place of the component
     * @memberof Component
     */
    get placed() {
        return this.__placed;
    }
    /**
     * Sets the place
     * @param {Number} value
     * @returns {void}
     * @memberof Component
     */
    set placed(value) {
        this.__placed = value;
    }

    /**
     * Returns an array of strings that are the feature ids of the component
     * @return {Array} Returns an array with the features
     * @memberof Component
     */
    get features() {
        return this.__features;
    }

    /**
     * Generates a random id
     * @returns {String} Random ID string
     * @memberof component
     */
    static generateID() {
        return Registry.generateID();
    }

    /**
     * Sets the bounds i.e. the x,y position and the width and length of the component
     * @param {Object} bounds PaperJS Rectangle object associated with a Path.bounds property
     * @memberof Component
     * @returns {void}
     */
    setBounds(bounds) {
        this.__bounds = bounds;
        let topleftpt = bounds.topLeft;
        this.__params.position = [topleftpt.x, topleftpt.y];
        this.__params.xspan = bounds.width;
        this.__params.yspan = bounds.height;
    }

    /**
     * Updates the parameters stored by the component
     * @param {String} key Key to identify the parameter
     * @param {} value New value to be assign in the feature
     * @memberof Component
     * @returns {void}
     */
    updateParameter(key, value) {
        this.__params.updateParameter(key, value);

        for (let i in this.__features) {
            let featureidtochange = this.__features[i];

            //Get the feature id and modify it
            let feature = Registry.currentDevice.getFeatureByID(featureidtochange);
            feature.updateParameter(key, value);
        }

        //Update the ComponentPorts
        this.updateComponentPorts();
    }

    /**
     * Generates the object that needs to be serialzed into JSON for interchange format V1
     * @returns {Object} Object
     * @memberof Component
     */
    toInterchangeV1() {
        let output = {};
        output.id = this.__id;
        output.name = this.__name;
        output.entity = this.__entity;
        output.params = this.__params.toJSON();
        let bounds = this.getBoundingRectangle();
        output["x-span"] = bounds.width;
        output["y-span"] = bounds.height;
        let portdata = [];
        let map = this.ports;
        if (map !== null) {
            for (let key of map.keys()) {
                let p = map.get(key).toInterchangeV1();
                portdata.push(p);
            }
        }

        output.ports = portdata;
        output.layers = this.__findLayerReferences();
        return output;
    }

    __findLayerReferences(){
        let layers = Registry.currentDevice.getLayers();
        let layerrefs = [];
        let layer;
        for(let i in layers){
            layer = layers[i];
            //Check if the component is in layer then put it there
            let feature;
            for(let key in layer.features){
                feature = layer.features[key];
                if(feature.referenceID === this.getID()){
                    layerrefs.push(layer.id);
                }
            }
        }
        return layerrefs;
    }

    /**
     * Returns the ID of the component
     * @returns {string|*}
     * @memberof Component
     */
    getID() {
        return this.__id;
    }

    /**
     * Allows the user to set the name of the component
     * @param {string} name
     * @returns {void}
     * @memberof Component
     *
     */
    setName(name) {
        this.__name = name;
    }

    /**
     * Returns the name of the component
     * @returns {string}
     * @memberof Component
     */
    getName() {
        return this.__name;
    }

    /**
     * Gets the 3DuF Type of the component, this will soon be depreciated and merged with
     * the MINT references
     * @returns {string} Returns the type of component
     * @memberof Component
     */
    getType() {
        return this.__type;
    }

    /**
     * Returns an Array of size two containing the X and Y coordinates
     * @return {Array<number>}
     * @memberof Component
     */
    getPosition() {
        return this.__params.getValue("position");
    }

    /**
     * Returns the value of the parameter stored against the following key in the component params
     * @param {string} key Key to access the value
     * @returns {*} Returns the value or an error
     * @memberof Component
     */
    getValue(key) {
        try {
            return this.__params.getValue(key);
        } catch (err) {
            throw new Error("Unable to get value for key: " + key);
        }
    }

    /**
     * Gets the list of feature ids that are associated with this
     * component
     * @return {Array|*} Returns an array with the correspondings features of the component
     * @memberof Component
     *
     */
    getFeatureIDs() {
        return this.__features;
    }

    /**
     * Checks if the component has default parameters
     * @param {String} key Key to access the component
     * @returns {boolean} Returns true whether it has default parameters or not
     * @memberof Component
     */
    hasDefaultParam(key) {
        if (this.getDefaults().hasOwnProperty(key)) return true;
        else return false;
    }

    /**
     * Adds a feature that is associated with the component
     * @param {String} featureID String id of the feature
     * @memberof Component
     * @returns {void}
     */
    addFeatureID(featureID) {
        if (typeof featureID != "string" && !(featureID instanceof String)) {
            throw new Error("The reference object value can only be a string");
        }
        this.__features.push(featureID);
        //Now update bounds
        // this.__updateBounds();
    }

    /**
     * This method updates the bounds of the component
     * @private
     * @memberof Component
     * @returns {void}
     */
    __updateBounds() {
        let bounds = null;
        let feature = null;
        let renderedfeature = null;
        for (var i in this.__features) {
            // gets teh feature defined by the id
            feature = Registry.currentDevice.getFeatureByID(this.__features[i]);
            console.log(feature);
            renderedfeature = FeatureRenderer2D.renderFeature(feature);
            console.log("rendered:");
            console.log(renderedfeature);
            if (bounds === null) {
                bounds = renderedfeature.bounds;
            } else {
                bounds = bounds.unite(renderedfeature.bounds);
            }
        }
        this.__bounds = bounds;
    }

    /**
     * Gets the params associated with the component
     * @return {Params} Returns the params associated with the component
     * @memberof Component
     */
    getParams() {
        return this.__params;
    }

    /**
     * Returns a paper.Rectangle object that defines the bounds of the component
     * @return {Object}
     * @memberof Component
     */
    getBoundingRectangle() {
        if (this.features.length === 0 || this.features === null || this.features === undefined) {
            console.error("No features associated with the component");
        }
        let bounds = null;
        for (let i in this.features) {
            let featureid = this.features[i];
            let render = Registry.viewManager.view.getRenderedFeature(featureid);
            if (bounds && render) {
                bounds = bounds.unite(render.bounds);
            } else {
                bounds = render.bounds;
            }
        }

        return bounds;
    }

    /**
     * Updates the coordinates of the component and all the other features
     * @param {Array} center
     * @memberof Component
     * @returns {void}
     */
    updateComponentPosition(center) {
        //This was not calling the right method earlier
        this.__params.updateParameter("position", center);
        for (let i in this.__features) {
            let featureidtochange = this.__features[i];

            let feature = Registry.currentDevice.getFeatureByID(featureidtochange);
            // feature.updateParameter('position', center);
            feature.updateParameter("position", center);
        }
    }

    /**
     * Replicates the component at the given positions
     * @param {Number} xpos Integer location of X
     * @param {Number} ypos Integer location of Y
     * @param {string} name Name of the replicated component
     * @return {Component}
     * @memberof Component
     */
    replicate(xpos, ypos, name = Registry.currentDevice.generateNewName(this.__type)) {
        //TODO: Fix this ridiculous chain of converting params back and forth, there should be an easier way
        //Converting all the params into raw values
        // let paramvalues = {};
        // for(let key in this.__params.parameters){
        //     paramvalues[key] = this.getValue(key);
        // }

        let definition = Registry.featureSet.getDefinition(this.__type);
        //Clean Param Data
        let cleanparamdata = {};
        for (let key in this.__params.parameters) {
            cleanparamdata[key] = this.__params.parameters[key].getValue();
        }
        let replicaparams = new Params(cleanparamdata, definition.unique, definition.heritable);
        let ret = new Component(this.__type, replicaparams, name, this.__entity);
        console.log("Checking what the new component params are:", ret.__params);
        //Generate New features
        for (let i in this.features) {
            let feature = Registry.currentDevice.getFeatureByID(this.features[i]);
            console.log("test", this.getPosition()[0], this.getPosition()[1], this.getPosition());
            let replica = feature.replicate(this.getPosition()[0], this.getPosition()[1]);
            replica.referenceID = ret.getID();
            ret.features.push(replica.getID());

            //TODO: add new feature to the layer in which the current feature is in
            let currentlayer = Registry.currentDevice.getLayerFromFeatureID(this.features[i]);
            currentlayer.addFeature(replica);
        }
        console.warn("TODO: Generate renders for the new Features for this new component");
        ret.updateComponentPosition([xpos, ypos]);
        return ret;
    }

    /**
     * Returns the center position of the component as a 2D vector
     * @return {Array}
     * @memberof Component
     */
    getCenterPosition() {
        let bounds = this.getBoundingRectangle();
        return [bounds.center.x, bounds.center.y];
    }

    /**
     * Returns the topleft position of the component as a 2D vector
     * @return {Array}
     * @memberof Component
     */
    getTopLeftPosition() {
        let bounds = this.getBoundingRectangle();
        return [bounds.topLeft.x, bounds.topLeft.y];
    }

    /**
     * This method is used to import the component from Interchange V1 JSON
     * @param {} json
     * @returns {*}
     * @memberof component
     */
    static fromInterchangeV1(json) {
        // let set;
        // if (json.hasOwnProperty("set")) set = json.set;
        // else set = "Basic";
        // //TODO: This will have to change soon when the thing is updated
        // throw new Error("Need to implement Interchange V1 Import for component object");
        let iscustomcompnent = false;
        let name = json.name;
        let id = json.id;
        let entity = json.entity;
        this.xspan = this._xspan;
        this.yspan = this._yspan;

        let params = {};
        if (entity === "TEST MINT") {
            console.warn("Found legacy invalid entity string", entity);
            entity = name.split("_")[0]; //'^.*?(?=_)'

            console.log("new entity:", entity);
        }

        iscustomcompnent = Registry.viewManager.customComponentManager.hasDefinition(entity);

        let definition;

        if (iscustomcompnent) {
            definition = CustomComponent.defaultParameterDefinitions();
        } else {
            definition = Registry.featureSet.getDefinition(entity);
            if(definition === null){
                throw Error("Could not find definition for type: "+ entity);
            }
        }

        // console.log(definition);
        let type;
        let value;
        for (let key in json.params) {
            // console.log("key:", key, "value:", json.params[key]);
            if (Object.prototype.hasOwnProperty.call(definition.heritable, key)) {
                type = definition.heritable[key];
            } else if (Object.prototype.hasOwnProperty.call(definition.unique, key)) {
                type = definition.unique[key];
            }
            // let paramobject = Parameter.generateComponentParameter(key, json.params[key]);
            //Check if the value type is float and convert the value from string
            value = json.params[key];
            if (type === "Float" && typeof value == "string") {
                value = parseFloat(value);
            }

            // let paramobject = new Parameter(type, value);
            params[key] = value;
        }

        //Do another check and see if position is present or not
        if (!Object.prototype.hasOwnProperty.call(params, 'position')) {
            params["position"] = [0.0, 0.0];
        }

        let paramstoadd = new Params(params, definition.unique, definition.heritable);
        let typestring = Registry.featureSet.getTypeForMINT(entity);
        let component = new Component(typestring, paramstoadd, name, entity, id);

        //Deserialize the component ports
        let portdata = new Map();
        for (let i in json.ports) {
            let componentport = ComponentPort.fromInterchangeV1(json.ports[i]);
            portdata.set(componentport.label, componentport);
        }

        component.ports = portdata;

        return component;
    }
    /**
     * Set port for the component
     * @param {string} label
     * @param {Port} port
     * @memberof Component
     * @returns {void}
     */
    setPort(label, port) {
        this.__ports.set(label, port);
    }
    /**
     * Gets the rotation of the component
     * @returns {Number} Returns the degree of rotation
     * @memberof Component
     */
    getRotation() {
        if (this.__params.hasParam("rotation")) {
            return this.getValue("rotation");
        } else if (this.__params.hasParam("orientation")) {
            let orientation = this.getValue("orientation");
            if (orientation === "V") {
                return 0;
            } else {
                return 270;
            }
        } else {
            console.warn("No rotation was found for component: ", this);
            return 0;
        }
    }
    /**
     *
     * @param {string} label
     * @param {*} render
     * @returns {void}
     * @memberof Component
     */
    attachComponentPortRender(label, render) {
        this._componentPortTRenders.set(label, render);
    }

    /**
     * Updates the Component Ports to have the latest location information
     * @memberof Component
     * @returns {void}
     */
    updateComponentPorts() {
        //updating the Component Ports

        let params = this.getParams().toMap();

        let cleanparamdata = {};

        for (let key of params.keys()) {
            cleanparamdata[key] = params.get(key);
        }

        let ports = Registry.featureSet.getComponentPorts(cleanparamdata, this.getType());

        for (let i in ports) {
            this.setPort(ports[i].label, ports[i]);
        }
    }
}
<commit_msg>Removed unecessary import from component.js<commit_after>import Params from "./params";
import CustomComponent from "./customComponent";
import ComponentPort from "./componentPort";

import * as Registry from "./registry";
import * as FeatureRenderer2D from "../view/render2D/featureRenderer2D";

/**
 * This class contains the component abstraction used in the interchange format and the
 * high level device model of the microfluidic.
 */
export default class Component {
    /**
     * Default Constructor
     * @param {string} type
     * @param {Params} params
     * @param {String} name
     * @param {string} mint
     * @param {String} id
     */
    constructor(type, params, name, id = Component.generateID()) {
        if (params instanceof Params) {
            this.__params = params;
        } else {
            console.error("Params not an instance of Params Object");
        }

        this.__name = name;
        this.__id = id;
        this.__type = type;
        this.__entity = type;
        //This stores the features that are a part of the component
        this.__features = [];
        //TODO: Need to figure out how to effectively search through these
        this.__bounds = null;
        this.__placed = false;
        this.__ports = new Map();
        this._componentPortTRenders = new Map();

        // TODO - Figure out how to use this for generic components
        this._xspan = 0;
        this._yspan = 0;

        //Create and set the ports here itself

        let cleanparamdata = {};
        for (let key in this.__params.parameters) {
            cleanparamdata[key] = this.__params.parameters[key].getValue();
        }

        let ports = Registry.featureSet.getComponentPorts(cleanparamdata, this.__type);
        if (ports !== undefined && ports.length >= 0 && ports !== null) {
            for (let i in ports) {
                this.setPort(ports[i].label, ports[i]);
            }
        }
    }
    /**
     * Gets the ports of the component
     * @returns {Port} Returns ports of the component
     * @memberof Component
     */
    get ports() {
        return this.__ports;
    }
    /**
     * Sets the port of the component
     * @param {} value
     * @returns {void}
     * @memberof Component
     */
    set ports(value) {
        this.__ports = value;
    }
    /**
     * Gets the place of the component
     * @returns {Number} Returns the place of the component
     * @memberof Component
     */
    get placed() {
        return this.__placed;
    }
    /**
     * Sets the place
     * @param {Number} value
     * @returns {void}
     * @memberof Component
     */
    set placed(value) {
        this.__placed = value;
    }

    /**
     * Returns an array of strings that are the feature ids of the component
     * @return {Array} Returns an array with the features
     * @memberof Component
     */
    get features() {
        return this.__features;
    }

    /**
     * Generates a random id
     * @returns {String} Random ID string
     * @memberof component
     */
    static generateID() {
        return Registry.generateID();
    }

    /**
     * Sets the bounds i.e. the x,y position and the width and length of the component
     * @param {Object} bounds PaperJS Rectangle object associated with a Path.bounds property
     * @memberof Component
     * @returns {void}
     */
    setBounds(bounds) {
        this.__bounds = bounds;
        let topleftpt = bounds.topLeft;
        this.__params.position = [topleftpt.x, topleftpt.y];
        this.__params.xspan = bounds.width;
        this.__params.yspan = bounds.height;
    }

    /**
     * Updates the parameters stored by the component
     * @param {String} key Key to identify the parameter
     * @param {} value New value to be assign in the feature
     * @memberof Component
     * @returns {void}
     */
    updateParameter(key, value) {
        this.__params.updateParameter(key, value);

        for (let i in this.__features) {
            let featureidtochange = this.__features[i];

            //Get the feature id and modify it
            let feature = Registry.currentDevice.getFeatureByID(featureidtochange);
            feature.updateParameter(key, value);
        }

        //Update the ComponentPorts
        this.updateComponentPorts();
    }

    /**
     * Generates the object that needs to be serialzed into JSON for interchange format V1
     * @returns {Object} Object
     * @memberof Component
     */
    toInterchangeV1() {
        let output = {};
        output.id = this.__id;
        output.name = this.__name;
        output.entity = this.__entity;
        output.params = this.__params.toJSON();
        let bounds = this.getBoundingRectangle();
        output["x-span"] = bounds.width;
        output["y-span"] = bounds.height;
        let portdata = [];
        let map = this.ports;
        if (map !== null) {
            for (let key of map.keys()) {
                let p = map.get(key).toInterchangeV1();
                portdata.push(p);
            }
        }

        output.ports = portdata;
        output.layers = this.__findLayerReferences();
        return output;
    }

    __findLayerReferences(){
        let layers = Registry.currentDevice.getLayers();
        let layerrefs = [];
        let layer;
        for(let i in layers){
            layer = layers[i];
            //Check if the component is in layer then put it there
            let feature;
            for(let key in layer.features){
                feature = layer.features[key];
                if(feature.referenceID === this.getID()){
                    layerrefs.push(layer.id);
                }
            }
        }
        return layerrefs;
    }

    /**
     * Returns the ID of the component
     * @returns {string|*}
     * @memberof Component
     */
    getID() {
        return this.__id;
    }

    /**
     * Allows the user to set the name of the component
     * @param {string} name
     * @returns {void}
     * @memberof Component
     *
     */
    setName(name) {
        this.__name = name;
    }

    /**
     * Returns the name of the component
     * @returns {string}
     * @memberof Component
     */
    getName() {
        return this.__name;
    }

    /**
     * Gets the 3DuF Type of the component, this will soon be depreciated and merged with
     * the MINT references
     * @returns {string} Returns the type of component
     * @memberof Component
     */
    getType() {
        return this.__type;
    }

    /**
     * Returns an Array of size two containing the X and Y coordinates
     * @return {Array<number>}
     * @memberof Component
     */
    getPosition() {
        return this.__params.getValue("position");
    }

    /**
     * Returns the value of the parameter stored against the following key in the component params
     * @param {string} key Key to access the value
     * @returns {*} Returns the value or an error
     * @memberof Component
     */
    getValue(key) {
        try {
            return this.__params.getValue(key);
        } catch (err) {
            throw new Error("Unable to get value for key: " + key);
        }
    }

    /**
     * Gets the list of feature ids that are associated with this
     * component
     * @return {Array|*} Returns an array with the correspondings features of the component
     * @memberof Component
     *
     */
    getFeatureIDs() {
        return this.__features;
    }

    /**
     * Checks if the component has default parameters
     * @param {String} key Key to access the component
     * @returns {boolean} Returns true whether it has default parameters or not
     * @memberof Component
     */
    hasDefaultParam(key) {
        if (this.getDefaults().hasOwnProperty(key)) return true;
        else return false;
    }

    /**
     * Adds a feature that is associated with the component
     * @param {String} featureID String id of the feature
     * @memberof Component
     * @returns {void}
     */
    addFeatureID(featureID) {
        if (typeof featureID != "string" && !(featureID instanceof String)) {
            throw new Error("The reference object value can only be a string");
        }
        this.__features.push(featureID);
        //Now update bounds
        // this.__updateBounds();
    }

    /**
     * This method updates the bounds of the component
     * @private
     * @memberof Component
     * @returns {void}
     */
    __updateBounds() {
        let bounds = null;
        let feature = null;
        let renderedfeature = null;
        for (var i in this.__features) {
            // gets teh feature defined by the id
            feature = Registry.currentDevice.getFeatureByID(this.__features[i]);
            console.log(feature);
            renderedfeature = FeatureRenderer2D.renderFeature(feature);
            console.log("rendered:");
            console.log(renderedfeature);
            if (bounds === null) {
                bounds = renderedfeature.bounds;
            } else {
                bounds = bounds.unite(renderedfeature.bounds);
            }
        }
        this.__bounds = bounds;
    }

    /**
     * Gets the params associated with the component
     * @return {Params} Returns the params associated with the component
     * @memberof Component
     */
    getParams() {
        return this.__params;
    }

    /**
     * Returns a paper.Rectangle object that defines the bounds of the component
     * @return {Object}
     * @memberof Component
     */
    getBoundingRectangle() {
        if (this.features.length === 0 || this.features === null || this.features === undefined) {
            console.error("No features associated with the component");
        }
        let bounds = null;
        for (let i in this.features) {
            let featureid = this.features[i];
            let render = Registry.viewManager.view.getRenderedFeature(featureid);
            if (bounds && render) {
                bounds = bounds.unite(render.bounds);
            } else {
                bounds = render.bounds;
            }
        }

        return bounds;
    }

    /**
     * Updates the coordinates of the component and all the other features
     * @param {Array} center
     * @memberof Component
     * @returns {void}
     */
    updateComponentPosition(center) {
        //This was not calling the right method earlier
        this.__params.updateParameter("position", center);
        for (let i in this.__features) {
            let featureidtochange = this.__features[i];

            let feature = Registry.currentDevice.getFeatureByID(featureidtochange);
            // feature.updateParameter('position', center);
            feature.updateParameter("position", center);
        }
    }

    /**
     * Replicates the component at the given positions
     * @param {Number} xpos Integer location of X
     * @param {Number} ypos Integer location of Y
     * @param {string} name Name of the replicated component
     * @return {Component}
     * @memberof Component
     */
    replicate(xpos, ypos, name = Registry.currentDevice.generateNewName(this.__type)) {
        //TODO: Fix this ridiculous chain of converting params back and forth, there should be an easier way
        //Converting all the params into raw values
        // let paramvalues = {};
        // for(let key in this.__params.parameters){
        //     paramvalues[key] = this.getValue(key);
        // }

        let definition = Registry.featureSet.getDefinition(this.__type);
        //Clean Param Data
        let cleanparamdata = {};
        for (let key in this.__params.parameters) {
            cleanparamdata[key] = this.__params.parameters[key].getValue();
        }
        let replicaparams = new Params(cleanparamdata, definition.unique, definition.heritable);
        let ret = new Component(this.__type, replicaparams, name, this.__entity);
        console.log("Checking what the new component params are:", ret.__params);
        //Generate New features
        for (let i in this.features) {
            let feature = Registry.currentDevice.getFeatureByID(this.features[i]);
            console.log("test", this.getPosition()[0], this.getPosition()[1], this.getPosition());
            let replica = feature.replicate(this.getPosition()[0], this.getPosition()[1]);
            replica.referenceID = ret.getID();
            ret.features.push(replica.getID());

            //TODO: add new feature to the layer in which the current feature is in
            let currentlayer = Registry.currentDevice.getLayerFromFeatureID(this.features[i]);
            currentlayer.addFeature(replica);
        }
        console.warn("TODO: Generate renders for the new Features for this new component");
        ret.updateComponentPosition([xpos, ypos]);
        return ret;
    }

    /**
     * Returns the center position of the component as a 2D vector
     * @return {Array}
     * @memberof Component
     */
    getCenterPosition() {
        let bounds = this.getBoundingRectangle();
        return [bounds.center.x, bounds.center.y];
    }

    /**
     * Returns the topleft position of the component as a 2D vector
     * @return {Array}
     * @memberof Component
     */
    getTopLeftPosition() {
        let bounds = this.getBoundingRectangle();
        return [bounds.topLeft.x, bounds.topLeft.y];
    }

    /**
     * This method is used to import the component from Interchange V1 JSON
     * @param {} json
     * @returns {*}
     * @memberof component
     */
    static fromInterchangeV1(json) {
        // let set;
        // if (json.hasOwnProperty("set")) set = json.set;
        // else set = "Basic";
        // //TODO: This will have to change soon when the thing is updated
        // throw new Error("Need to implement Interchange V1 Import for component object");
        let iscustomcompnent = false;
        let name = json.name;
        let id = json.id;
        let entity = json.entity;
        this.xspan = this._xspan;
        this.yspan = this._yspan;

        let params = {};
        if (entity === "TEST MINT") {
            console.warn("Found legacy invalid entity string", entity);
            entity = name.split("_")[0]; //'^.*?(?=_)'

            console.log("new entity:", entity);
        }

        iscustomcompnent = Registry.viewManager.customComponentManager.hasDefinition(entity);

        let definition;

        if (iscustomcompnent) {
            definition = CustomComponent.defaultParameterDefinitions();
        } else {
            definition = Registry.featureSet.getDefinition(entity);
            if(definition === null){
                throw Error("Could not find definition for type: "+ entity);
            }
        }

        // console.log(definition);
        let type;
        let value;
        for (let key in json.params) {
            // console.log("key:", key, "value:", json.params[key]);
            if (Object.prototype.hasOwnProperty.call(definition.heritable, key)) {
                type = definition.heritable[key];
            } else if (Object.prototype.hasOwnProperty.call(definition.unique, key)) {
                type = definition.unique[key];
            }
            // let paramobject = Parameter.generateComponentParameter(key, json.params[key]);
            //Check if the value type is float and convert the value from string
            value = json.params[key];
            if (type === "Float" && typeof value == "string") {
                value = parseFloat(value);
            }

            // let paramobject = new Parameter(type, value);
            params[key] = value;
        }

        //Do another check and see if position is present or not
        if (!Object.prototype.hasOwnProperty.call(params, 'position')) {
            params["position"] = [0.0, 0.0];
        }

        let paramstoadd = new Params(params, definition.unique, definition.heritable);
        let typestring = Registry.featureSet.getTypeForMINT(entity);
        let component = new Component(typestring, paramstoadd, name, entity, id);

        //Deserialize the component ports
        let portdata = new Map();
        for (let i in json.ports) {
            let componentport = ComponentPort.fromInterchangeV1(json.ports[i]);
            portdata.set(componentport.label, componentport);
        }

        component.ports = portdata;

        return component;
    }
    /**
     * Set port for the component
     * @param {string} label
     * @param {Port} port
     * @memberof Component
     * @returns {void}
     */
    setPort(label, port) {
        this.__ports.set(label, port);
    }
    /**
     * Gets the rotation of the component
     * @returns {Number} Returns the degree of rotation
     * @memberof Component
     */
    getRotation() {
        if (this.__params.hasParam("rotation")) {
            return this.getValue("rotation");
        } else if (this.__params.hasParam("orientation")) {
            let orientation = this.getValue("orientation");
            if (orientation === "V") {
                return 0;
            } else {
                return 270;
            }
        } else {
            console.warn("No rotation was found for component: ", this);
            return 0;
        }
    }
    /**
     *
     * @param {string} label
     * @param {*} render
     * @returns {void}
     * @memberof Component
     */
    attachComponentPortRender(label, render) {
        this._componentPortTRenders.set(label, render);
    }

    /**
     * Updates the Component Ports to have the latest location information
     * @memberof Component
     * @returns {void}
     */
    updateComponentPorts() {
        //updating the Component Ports

        let params = this.getParams().toMap();

        let cleanparamdata = {};

        for (let key of params.keys()) {
            cleanparamdata[key] = params.get(key);
        }

        let ports = Registry.featureSet.getComponentPorts(cleanparamdata, this.getType());

        for (let i in ports) {
            this.setPort(ports[i].label, ports[i]);
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Delete jQuery<commit_after><|endoftext|>


<commit_before><commit_msg>avoid newline after pressing enter key<commit_after><|endoftext|>


<commit_before>'use strict';

import { LOAD_DATA, SHOW_MONSTER, HIDE_MONSTER, USE_MONSTER, REMOVE_MONSTER } from './actions';

const initialState = {
  monsters: [],
  visibleStatBlock: null,
  visibleStatBlockId: -1,
  usedMonsters: []
};

function getById (array, id) {
  let i = 0;
  let length = array.length;
  for (i; i < length; i++) {
    if (array[i].id === id) { return array[i]; }
  }
  return null;
}

function deleteById (array, id) {
  let i = 0;
  let length = array.length;
  let newArray = [];
  for (i; i < length; i++) {
    if (array[i].id === id) { continue; }
    newArray.push(array[i]);
  }
  return newArray;
}

function summonerApp(state = initialState, action) {
  var usedMonsters;
  switch(action.type) {
    case LOAD_DATA:
      return Object.assign({}, state, { monsters: action.monsters });
    break;
    case SHOW_MONSTER:
      return Object.assign({}, state, { visibleStatBlock: action.visibleStatBlock, visibleStatBlockId: action.visibleStatBlockId });
    break;
    case USE_MONSTER:
      usedMonsters = state.usedMonsters.concat([getById(state.allMonsters, action.id)]);
      return Object.assign({}, state, {usedMonsters: usedMonsters});
    break;
    case REMOVE_MONSTER:
      usedMonsters = deleteById(state.usedMonsters, action.id);
      return Object.assign({}, state, {usedMonsters: usedMonsters});
    break;
    default:
      return state;
    break;
  };
}

export default summonerApp;
<commit_msg>Use passed stat block<commit_after>'use strict';

import { LOAD_DATA, SHOW_MONSTER, HIDE_MONSTER, USE_MONSTER, REMOVE_MONSTER } from './actions';

const initialState = {
  monsters: [],
  visibleStatBlock: null,
  visibleStatBlockId: -1,
  usedMonsters: []
};

function getById (array, id) {
  let i = 0;
  let length = array.length;
  for (i; i < length; i++) {
    if (array[i].id === id) { return array[i]; }
  }
  return null;
}

function deleteById (array, id) {
  let i = 0;
  let length = array.length;
  let newArray = [];
  for (i; i < length; i++) {
    if (array[i].id === id) { continue; }
    newArray.push(array[i]);
  }
  return newArray;
}

function summonerApp(state = initialState, action) {
  var usedMonsters;
  switch(action.type) {
    case LOAD_DATA:
      return Object.assign({}, state, { monsters: action.monsters });
    break;
    case SHOW_MONSTER:
      return Object.assign({}, state, { visibleStatBlock: action.visibleStatBlock, visibleStatBlockId: action.visibleStatBlockId });
    break;
    case USE_MONSTER:
      usedMonsters = state.usedMonsters.concat([action.monster]);
      return Object.assign({}, state, {usedMonsters: usedMonsters});
    break;
    case REMOVE_MONSTER:
      usedMonsters = deleteById(state.usedMonsters, action.id);
      return Object.assign({}, state, {usedMonsters: usedMonsters});
    break;
    default:
      return state;
    break;
  };
}

export default summonerApp;
<|endoftext|>


<commit_before>import {find, findIndex} from 'lodash'
import {Link} from 'react-router-dom';

import KeySelector from 'app/common/KeySelector';

import './SetViewer.scss';

class SetViewer extends PreactComponent {
	state = {
		isLoading: false,
		mode:      ''
	};

	editModeOff = () => this.setState( { mode: '' } );
	editModeOn = () => this.setState( { mode: 'edit' } );

	onDeleteSet = () => {

		if ( this.props.onRemoveSet ) {
			this.props.onRemoveSet();
		}

	};

	onMoveSongDown = songId => {

		const index = findIndex( this.props.songs, { _id: songId } );

		if ( index > -1 ) {
			if ( this.props.onSongMove ) {
				this.props.onSongMove( songId, index + 1 );
			}
		}

	};

	onMoveSongUp = songId => {

		const index = findIndex( this.props.songs, { _id: songId } );

		if ( index > -1 ) {
			if ( this.props.onSongMove ) {
				this.props.onSongMove( songId, index - 1 );
			}
		}

	};

	changeKey = ( songId, amount ) => {

		if ( this.props.onChangeKey ) {
			this.props.onChangeKey( songId, amount );
		}

	};

	removeSong = songId => {

		if ( this.props.onRemoveSong ) {
			this.props.onRemoveSong( songId );
		}

	};

	transposeDown = song => { this.changeKey( song._id, -1 ); };
	transposeUp = song => { this.changeKey( song._id, 1 ); };

	render( { set, songs }, { mode } ) {

		return set && (

														{set.title}

												{mode === 'edit' ? [

,

														<a className="button is-primary"
											   			href={`/songs/add-to-set/${set._id}`}>
															Add songs

,

											] : (

											)}

							{songs.length ?
								songs.map( this._createRow )
								:

This set has no songs

									<a className="button is-primary"
									   href={`/songs/add-to-set/${set._id}`}>
										Add songs

							}

		);
	}

	_createRow = song => {

		const { set } = this.props;
		const { mode } = this.state;
		const setSong = find( set.songs, s => s._id === song._id );
		const key = setSong ? setSong.key : song.key;

		return (

					<Link to={`/sets/${set._id}/songs/${song._id}`}>
						{song.title}
					</Link>

						{mode === 'edit' && [
							 this.transposeDown( song )}>

						]}

						<KeySelector
							onSelect={( key, amount ) => this.changeKey( song._id, amount )}
							value={key}
						/>

						{mode === 'edit' && [

							 this.transposeUp( song )}>

						]}

				{mode === 'edit' && (

						 this.onMoveSongUp( song._id )}
							title="move this song up the list">

						 this.removeSong( song._id )}
							title="remove this song from the list">

				)}

		);

	};
}

export default SetViewer;
<commit_msg>setlist header button improvements<commit_after>import {find, findIndex} from 'lodash'
import {Link} from 'react-router-dom';

import KeySelector from 'app/common/KeySelector';

import './SetViewer.scss';

class SetViewer extends PreactComponent {
	state = {
		isLoading: false,
		mode:      ''
	};

	editModeOff = () => this.setState( { mode: '' } );
	editModeOn = () => this.setState( { mode: 'edit' } );

	onDeleteSet = () => {

		if ( this.props.onRemoveSet ) {
			this.props.onRemoveSet();
		}

	};

	onMoveSongDown = songId => {

		const index = findIndex( this.props.songs, { _id: songId } );

		if ( index > -1 ) {
			if ( this.props.onSongMove ) {
				this.props.onSongMove( songId, index + 1 );
			}
		}

	};

	onMoveSongUp = songId => {

		const index = findIndex( this.props.songs, { _id: songId } );

		if ( index > -1 ) {
			if ( this.props.onSongMove ) {
				this.props.onSongMove( songId, index - 1 );
			}
		}

	};

	changeKey = ( songId, amount ) => {

		if ( this.props.onChangeKey ) {
			this.props.onChangeKey( songId, amount );
		}

	};

	removeSong = songId => {

		if ( this.props.onRemoveSong ) {
			this.props.onRemoveSong( songId );
		}

	};

	transposeDown = song => { this.changeKey( song._id, -1 ); };
	transposeUp = song => { this.changeKey( song._id, 1 ); };

	render( { set, songs }, { mode } ) {

		return set && (

												{set.title}

												{set.author}

											{mode === 'edit' ? [

												] : (

											)}

												<a className="button"
													href={`/songs/add-to-set/${set._id}`}>
													Add songs

												{mode === 'edit' ? [

											] : (

											)}

							{songs.length ?
								songs.map( this._createRow )
								:

This set has no songs

									<a className="button is-primary"
									   href={`/songs/add-to-set/${set._id}`}>
										Add songs

							}

		);
	}

	_createRow = song => {

		const { set } = this.props;
		const { mode } = this.state;
		const setSong = find( set.songs, s => s._id === song._id );
		const key = setSong ? setSong.key : song.key;

		return (

					<Link to={`/sets/${set._id}/songs/${song._id}`}>
						{song.title}
					</Link>

						{mode === 'edit' && [
							 this.transposeDown( song )}>

						]}

						<KeySelector
							onSelect={( key, amount ) => this.changeKey( song._id, amount )}
							value={key}
						/>

						{mode === 'edit' && [

							 this.transposeUp( song )}>

						]}

				{mode === 'edit' && (

						 this.onMoveSongUp( song._id )}
							title="move this song up the list">

				)}
				{mode === 'edit' && (

						 this.removeSong( song._id )}
							title="remove this song from the list">

				)}

		);

	};
}

export default SetViewer;
<|endoftext|>


<commit_before>import { api } from '@steemit/steem-js';
import { ifHive } from 'app/utils/Community';
import stateCleaner from 'app/redux/stateCleaner';
import xhr from 'axios/index';
import demo from './demo';

export async function callBridge(method, params) {
    console.log(
        'call bridge',
        method,
        params && JSON.stringify(params).substring(0, 200)
    );

    return new Promise(function(resolve, reject) {
        api.call('bridge.' + method, params, function(err, data) {
            if (err) reject(err);
            else resolve(data);
        });
    });
}

export const _list_temp = [
    85272851,
    85292735,
    85290072,
    85280316,
    85290308,
    85240810,
    85278992,
    85276721,
    85288504,
    85303662,
    85287965,
    85283636,
    85273231,
    85306764,
    85287931,
    85299660,
    85290948,
    85293459,
    85293772,
    85276721,
    85276833,
    85293901,
    85289063,
    85294452,
    85293865,
    85305460,
    85295587,
    85312343,
    85308672,
    85311375,
    85321625,
    85311891,
    85318584,
    85308914,
    85321248,
    85325744,
    85325042,
    85323948,
    85325276,
    85326597,
    85321023,
    85324738,
    85334812,
    85316255,
    85336207,
    85323051,
    85209019,
    85343852,
    85335737,
    85341505,
    85315644,
];

export const _user_list = [
    'roelandp',
    'blocktrades',
    'anyx',
    'ausbitbank',
    'gtg',
    'themarkymark',
    'lukestokes.mhth',
    'therealwolf',
    'netuoso',
    'innerhive',
];

export async function getStateAsync(url, observer, ssr = false) {
    if (observer === undefined) observer = null;

    const { page, tag, sort, key } = parsePath(url);

    console.log('GSA', url, observer, ssr);
    let state = {
        accounts: {},
        community: {},
        content: {},
        discussion_idx: {},
        profiles: {},
    };
    let _blist = [];

    if (ssr) {
        // _blist = await getBlackList();
        _blist = _blist.concat(_list_temp);
        state['blacklist'] = _blist;
    }

    // load `content` and `discussion_idx`
    if (page == 'posts' || page == 'account') {
        let posts = await loadPosts(sort, tag, observer, ssr);
        let _content = ssr
            ? filter(posts['content'], _blist, _user_list)
            : posts['content'];
        state['content'] = _content;
        state['discussion_idx'] = posts['discussion_idx'];
    } else if (page == 'thread') {
        const posts = await loadThread(key[0], key[1]);
        state['content'] = posts['content'];
    } else {
        // no-op
    }

    // append `community` key
    if (tag && ifHive(tag)) {
        try {
            state['community'][tag] = await callBridge('get_community', {
                name: tag,
                observer: observer,
            });
        } catch (e) {}
    }

    // for SSR, load profile on any profile page or discussion thread author
    const account =
        tag && tag[0] == '@'
            ? tag.slice(1)
            : page == 'thread' ? key[0].slice(1) : null;
    if (ssr && account) {
        // TODO: move to global reducer?
        const profile = await callBridge('get_profile', { account });
        if (profile && profile['name']) {
            state['profiles'][account] = profile;
        }
    }

    if (ssr) {
        // append `topics` key
        state['topics'] = await callBridge('get_trending_topics', {
            limit: 12,
        });
    }

    const cleansed = stateCleaner(state);
    return cleansed;
}

export async function getBlackList() {
    const res = await xhr
        .get('http://39.105.221.87:8081/steemit/blacklist', { timeout: 3000 })
        .catch(e => console.log('error', e));
    console.log('blacklist', res && res.data);
    return (res && res.data && res.data.data) || [];
}

function filter(posts, blacklist, userlist) {
    let content = {};
    for (var key in posts) {
        if (
            blacklist.indexOf(posts[key].post_id) === -1 &&
            userlist.indexOf(posts[key].author) === -1
        ) {
            content[key] = posts[key];
        }
    }
    return content;
}

async function loadThread(account, permlink) {
    const author = account.slice(1);
    const content = await callBridge('get_discussion', { author, permlink });
    return { content };
}

async function loadPosts(sort, tag, observer, ssr) {
    const account = tag && tag[0] == '@' ? tag.slice(1) : null;

    let posts;
    if (account) {
        const params = { sort, account, observer };
        posts = await callBridge('get_account_posts', params);
    } else {
        const params = { sort, tag, observer };
        posts = await callBridge('get_ranked_posts', params);
        if (!tag) {
            posts = [].concat(demo, posts);
        }
    }
    // console.log('----posts.length----',posts.length)
    let content = {};
    let keys = [];
    for (var idx in posts) {
        const post = posts[idx];
        const key = post['author'] + '/' + post['permlink'];
        content[key] = post;
        keys.push(key);
    }

    let discussion_idx = {};
    discussion_idx[tag] = {};
    discussion_idx[tag][sort] = keys;

    return { content, discussion_idx };
}

function parsePath(url) {
    // strip off query string
    url = url.split('?')[0];

    // strip off leading and trailing slashes
    if (url.length > 0 && url[0] == '/') url = url.substring(1, url.length);
    if (url.length > 0 && url[url.length - 1] == '/')
        url = url.substring(0, url.length - 1);

    // blank URL defaults to `trending`
    if (url === '') url = 'trending';

    const part = url.split('/');
    const parts = part.length;
    const sorts = [
        'trending',
        'promoted',
        'hot',
        'created',
        'payout',
        'payout_comments',
        'muted',
    ];
    const acct_tabs = [
        'blog',
        'feed',
        'posts',
        'comments',
        'replies',
        'payout',
    ];

    let page = null;
    let tag = null;
    let sort = null;
    let key = null;

    if (parts == 1 && sorts.includes(part[0])) {
        page = 'posts';
        sort = part[0];
        tag = '';
    } else if (parts == 2 && sorts.includes(part[0])) {
        page = 'posts';
        sort = part[0];
        tag = part[1];
    } else if (parts == 3 && part[1][0] == '@') {
        page = 'thread';
        tag = part[0];
        key = [part[1], part[2]];
    } else if (parts == 1 && part[0][0] == '@') {
        page = 'account';
        sort = 'blog';
        tag = part[0];
    } else if (parts == 2 && part[0][0] == '@') {
        if (acct_tabs.includes(part[1])) {
            page = 'account';
            sort = part[1];
        } else {
            // settings, followers, notifications, etc (no-op)
        }
        tag = part[0];
    } else {
        // no-op URL
    }

    return { page, tag, sort, key };
}
<commit_msg>deduplication fix<commit_after>import { api } from '@steemit/steem-js';
import { ifHive } from 'app/utils/Community';
import stateCleaner from 'app/redux/stateCleaner';
import xhr from 'axios/index';
import demo from './demo';

export async function callBridge(method, params) {
    console.log(
        'call bridge',
        method,
        params && JSON.stringify(params).substring(0, 200)
    );

    return new Promise(function(resolve, reject) {
        api.call('bridge.' + method, params, function(err, data) {
            if (err) reject(err);
            else resolve(data);
        });
    });
}

export const _list_temp = [
    85272851,
    85292735,
    85290072,
    85280316,
    85290308,
    85240810,
    85278992,
    85276721,
    85288504,
    85303662,
    85287965,
    85283636,
    85273231,
    85306764,
    85287931,
    85299660,
    85290948,
    85293459,
    85293772,
    85276721,
    85276833,
    85293901,
    85289063,
    85294452,
    85293865,
    85305460,
    85295587,
    85312343,
    85308672,
    85311375,
    85321625,
    85311891,
    85318584,
    85308914,
    85321248,
    85325744,
    85325042,
    85323948,
    85325276,
    85326597,
    85321023,
    85324738,
    85334812,
    85316255,
    85336207,
    85323051,
    85209019,
    85343852,
    85335737,
    85341505,
    85315644,
    85357437,
    85330639,
    85359441,
    85357235,
    85354823,
    85360899,
    85358726,
    85354772,
    85362926,
    85359018,
    85364554,
    85365012,
    85354766,
    85359759,
];

export const _user_list = [
    'roelandp',
    'blocktrades',
    'anyx',
    'ausbitbank',
    'gtg',
    'themarkymark',
    'lukestokes.mhth',
    'therealwolf',
    'netuoso',
    'innerhive',
    'z8teyb289qav9z',
    'ngc',
];

export async function getStateAsync(url, observer, ssr = false) {
    if (observer === undefined) observer = null;

    const { page, tag, sort, key } = parsePath(url);

    console.log('GSA', url, observer, ssr);
    let state = {
        accounts: {},
        community: {},
        content: {},
        discussion_idx: {},
        profiles: {},
    };
    let _blist = [];

    if (ssr) {
        // _blist = await getBlackList();
        _blist = _blist.concat(_list_temp);
        state['blacklist'] = _blist;
    }

    // load `content` and `discussion_idx`
    if (page == 'posts' || page == 'account') {
        let posts = await loadPosts(sort, tag, observer, ssr);
        let _content = ssr
            ? filter(posts['content'], _blist, _user_list)
            : posts['content'];
        state['content'] = _content;
        state['discussion_idx'] = posts['discussion_idx'];
    } else if (page == 'thread') {
        const posts = await loadThread(key[0], key[1]);
        state['content'] = posts['content'];
    } else {
        // no-op
    }

    // append `community` key
    if (tag && ifHive(tag)) {
        try {
            state['community'][tag] = await callBridge('get_community', {
                name: tag,
                observer: observer,
            });
        } catch (e) {}
    }

    // for SSR, load profile on any profile page or discussion thread author
    const account =
        tag && tag[0] == '@'
            ? tag.slice(1)
            : page == 'thread' ? key[0].slice(1) : null;
    if (ssr && account) {
        // TODO: move to global reducer?
        const profile = await callBridge('get_profile', { account });
        if (profile && profile['name']) {
            state['profiles'][account] = profile;
        }
    }

    if (ssr) {
        // append `topics` key
        state['topics'] = await callBridge('get_trending_topics', {
            limit: 12,
        });
    }

    const cleansed = stateCleaner(state);
    return cleansed;
}

export async function getBlackList() {
    const res = await xhr
        .get('http://39.105.221.87:8081/steemit/blacklist', { timeout: 3000 })
        .catch(e => console.log('error', e));
    console.log('blacklist', res && res.data);
    return (res && res.data && res.data.data) || [];
}

function filter(posts, blacklist, userlist) {
    let content = {};
    for (var key in posts) {
        if (
            blacklist.indexOf(posts[key].post_id) === -1 &&
            userlist.indexOf(posts[key].author) === -1
        ) {
            content[key] = posts[key];
        }
    }
    return content;
}

async function loadThread(account, permlink) {
    const author = account.slice(1);
    const content = await callBridge('get_discussion', { author, permlink });
    return { content };
}

async function loadPosts(sort, tag, observer, ssr) {
    const account = tag && tag[0] == '@' ? tag.slice(1) : null;

    let posts;
    if (account) {
        const params = { sort, account, observer };
        posts = await callBridge('get_account_posts', params);
    } else {
        const params = { sort, tag, observer };
        posts = await callBridge('get_ranked_posts', params);
        if (!tag) {
            posts = [].concat(demo, posts);
        }
    }
    // console.log('----posts.length----',posts.length)
    let content = {};
    let keys = [];
    for (var idx in posts) {
        const post = posts[idx];
        const key = post['author'] + '/' + post['permlink'];
        content[key] = post;
        keys.indexOf(key) == -1 && keys.push(key);
    }

    let discussion_idx = {};
    discussion_idx[tag] = {};
    discussion_idx[tag][sort] = keys;

    return { content, discussion_idx };
}

function parsePath(url) {
    // strip off query string
    url = url.split('?')[0];

    // strip off leading and trailing slashes
    if (url.length > 0 && url[0] == '/') url = url.substring(1, url.length);
    if (url.length > 0 && url[url.length - 1] == '/')
        url = url.substring(0, url.length - 1);

    // blank URL defaults to `trending`
    if (url === '') url = 'trending';

    const part = url.split('/');
    const parts = part.length;
    const sorts = [
        'trending',
        'promoted',
        'hot',
        'created',
        'payout',
        'payout_comments',
        'muted',
    ];
    const acct_tabs = [
        'blog',
        'feed',
        'posts',
        'comments',
        'replies',
        'payout',
    ];

    let page = null;
    let tag = null;
    let sort = null;
    let key = null;

    if (parts == 1 && sorts.includes(part[0])) {
        page = 'posts';
        sort = part[0];
        tag = '';
    } else if (parts == 2 && sorts.includes(part[0])) {
        page = 'posts';
        sort = part[0];
        tag = part[1];
    } else if (parts == 3 && part[1][0] == '@') {
        page = 'thread';
        tag = part[0];
        key = [part[1], part[2]];
    } else if (parts == 1 && part[0][0] == '@') {
        page = 'account';
        sort = 'blog';
        tag = part[0];
    } else if (parts == 2 && part[0][0] == '@') {
        if (acct_tabs.includes(part[1])) {
            page = 'account';
            sort = part[1];
        } else {
            // settings, followers, notifications, etc (no-op)
        }
        tag = part[0];
    } else {
        // no-op URL
    }

    return { page, tag, sort, key };
}
<|endoftext|>


<commit_before>import PropTypes from "prop-types";
import React, { useCallback, useMemo } from "react";
import { useHistory } from "react-router-dom";
import styled from "styled-components";

import { routeConfig } from "@agir/front/app/routes.config";
import style from "@agir/front/genericComponents/_variables.scss";
import { parseDiscountCodes } from "@agir/groups/groupPage/utils";

import Button from "@agir/front/genericComponents/Button";
import Card from "@agir/front/genericComponents/Card";
import Collapsible from "@agir/front/genericComponents/Collapsible.js";
import { Column, Hide, Row } from "@agir/front/genericComponents/grid";
import FeatherIcon from "@agir/front/genericComponents/FeatherIcon";
import Link from "@agir/front/app/Link";

const GroupIcon = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  margin: 0;
  padding: 0;
  border-radius: 100%;
  background-color: ${(props) => props.theme.black50};
  color: ${(props) => props.theme.black1000};
`;

const Label = styled.span`
  font-size: 13px;
  display: inline-block;
  padding: 6px 10px;
  border: 1px solid #dfdfdf;
  margin-right: 8px;
  margin-bottom: 4px;
  line-height: 16px;
  border-radius: 20px;
  ${({ main }) =>
    main
      ? `
    background: #EEEEEE;
    border: 0;
    `
      : ""}
`;

const DiscountCodesSection = styled.section`
  margin: 1.5rem 0 0;

  & > * {
    color: ${style.black700};
    margin: 0.5rem 0;
  }

  ul {
    padding: 0;
  }

  li {
    list-style: none;
  }

  li span {
    font-weight: 400;
  }
`;

const StyledGroupButton = styled(Button)`
  height: 48px;
  font-size: 16px;
  font-weight: 500;
  border-radius: ${style.borderRadius};
  margin-top: 16px;
`;

let GroupButton = ({
  as = "a",
  to,
  href,
  children,
  color = "default",
  icon,
}) => (
  <Column width={["33%", "content"]} collapse={500}>
    <Button as={as} to={to} href={href} color={color} icon={icon} small>
      {children}
    </Button>
  </Column>
);
GroupButton.propTypes = {
  as: PropTypes.string,
  to: PropTypes.string,
  href: PropTypes.string,
  children: PropTypes.node,
  color: PropTypes.string,
  icon: PropTypes.string,
};

const GroupCard = ({
  id,
  name,
  description,
  eventCount,
  membersCount,
  isMember,
  isManager,
  typeLabel,
  labels,
  routes,
  discountCodes,
  displayGroupLogo,
  displayType,
  displayDescription,
  displayMembership,
  isEmbedded = false,
}) => {
  const history = useHistory();
  const handleClick = useCallback(
    (e) => {
      if (["A", "BUTTON"].includes(e.target.tagName.toUpperCase())) {
        return;
      }
      id &&
        routeConfig.groupDetails &&
        history.push(routeConfig.groupDetails.getLink({ groupPk: id }));
    },
    [history, id]
  );
  const parsedDiscountCodes = useMemo(
    () => parseDiscountCodes(discountCodes),
    [discountCodes]
  );

  return (
    <Card onClick={isEmbedded ? undefined : handleClick}>
      <Row gutter={6}>
        {displayGroupLogo && (
          <Column collapse={0}>
            <Link to={routeConfig.groupDetails.getLink({ groupPk: id })}>
              <GroupIcon>
                <FeatherIcon name="users" />
              </GroupIcon>
            </Link>
          </Column>
        )}
        <Column collapse={0} grow>

            <Link
              style={{
                color: "inherit",
                textDecoration: "none",
              }}
              to={routeConfig.groupDetails.getLink({ groupPk: id })}
            >
              {name}
            </Link>

            {eventCount} événement{eventCount > 1 ? "s" : ""} •{" "}
            {membersCount} membre{membersCount > 1 ? "s" : ""}

        </Column>
      </Row>

      {displayType && typeLabel && (

          {typeLabel}
          {labels &&
            labels.map((label, index) => {label})}

      )}

      {displayDescription && description && (

          <Collapsible
            maxHeight={100}
            expanderLabel="Lire la suite"
            dangerouslySetInnerHTML={{ __html: description }}
            fadingOverflow
          />

      )}

      {parsedDiscountCodes && parsedDiscountCodes.length > 0 && (
        <DiscountCodesSection>

Codes matériels :

            {parsedDiscountCodes.map(({ code, expiration }) => (

                {code} (expire {expiration})

            ))}

        </DiscountCodesSection>
      )}
      <Row gutter={6} style={{ marginTop: "1.5rem" }}>
        {!isEmbedded && !isMember && (

        )}

        {routes.fund && (
          <GroupButton href={routes.fund} icon="fast-forward">
            Financer
          </GroupButton>
        )}
        {isManager && (

        )}
      </Row>
      {displayMembership && isMember && (

          <FeatherIcon small inline name="check" /> Vous êtes membre du groupe

      )}
    </Card>
  );
};

GroupCard.propTypes = {
  name: PropTypes.string.isRequired,
  description: PropTypes.string,
  eventCount: PropTypes.number,
  membersCount: PropTypes.number,
  isMember: PropTypes.bool,
  isManager: PropTypes.bool,
  typeLabel: PropTypes.string,
  labels: PropTypes.arrayOf(PropTypes.string),
  routes: PropTypes.object,
  displayGroupLogo: PropTypes.bool,
  displayType: PropTypes.bool,
  displayDescription: PropTypes.bool,
  displayMembership: PropTypes.bool,
  ...DiscountCodesSection.propTypes,
  isEmbedded: PropTypes.bool,
};

GroupCard.defaultProps = {
  displayGroupLogo: true,
  displayType: true,
  displayDescription: true,
  displayMembership: true,
  isManager: false,
};

export default GroupCard;
<commit_msg>fix: GroupCard button alignment<commit_after>import PropTypes from "prop-types";
import React, { useCallback, useMemo } from "react";
import { useHistory } from "react-router-dom";
import styled from "styled-components";

import { routeConfig } from "@agir/front/app/routes.config";
import style from "@agir/front/genericComponents/_variables.scss";
import { parseDiscountCodes } from "@agir/groups/groupPage/utils";

import Button from "@agir/front/genericComponents/Button";
import Card from "@agir/front/genericComponents/Card";
import Collapsible from "@agir/front/genericComponents/Collapsible.js";
import { Column, Hide, Row } from "@agir/front/genericComponents/grid";
import FeatherIcon from "@agir/front/genericComponents/FeatherIcon";
import Spacer from "@agir/front/genericComponents/Spacer";
import Link from "@agir/front/app/Link";

const GroupIcon = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  margin: 0;
  padding: 0;
  border-radius: 100%;
  background-color: ${(props) => props.theme.black50};
  color: ${(props) => props.theme.black1000};
`;

const Label = styled.span`
  font-size: 13px;
  display: inline-block;
  padding: 6px 10px;
  border: 1px solid #dfdfdf;
  margin-right: 8px;
  margin-bottom: 4px;
  line-height: 16px;
  border-radius: 20px;
  ${({ main }) =>
    main
      ? `
    background: #EEEEEE;
    border: 0;
    `
      : ""}
`;

const DiscountCodesSection = styled.section`
  margin: 1.5rem 0 0;

  & > * {
    color: ${style.black700};
    margin: 0.5rem 0;
  }

  ul {
    padding: 0;
  }

  li {
    list-style: none;
  }

  li span {
    font-weight: 400;
  }
`;

const StyledGroupButton = styled(Button)`
  height: 48px;
  font-size: 16px;
  font-weight: 500;
  border-radius: ${style.borderRadius};
  margin-top: 16px;

  && + && {
    margin-left: 0;
  }
`;

let GroupButton = ({
  as = "a",
  to,
  href,
  children,
  color = "default",
  icon,
}) => (
  <Column width={["33%", "content"]} collapse={500}>
    <Button as={as} to={to} href={href} color={color} icon={icon} small>
      {children}
    </Button>
  </Column>
);
GroupButton.propTypes = {
  as: PropTypes.string,
  to: PropTypes.string,
  href: PropTypes.string,
  children: PropTypes.node,
  color: PropTypes.string,
  icon: PropTypes.string,
};

const GroupCard = ({
  id,
  name,
  description,
  eventCount,
  membersCount,
  isMember,
  isManager,
  typeLabel,
  labels,
  routes,
  discountCodes,
  displayGroupLogo,
  displayType,
  displayDescription,
  displayMembership,
  isEmbedded = false,
}) => {
  const history = useHistory();
  const handleClick = useCallback(
    (e) => {
      if (["A", "BUTTON"].includes(e.target.tagName.toUpperCase())) {
        return;
      }
      id &&
        routeConfig.groupDetails &&
        history.push(routeConfig.groupDetails.getLink({ groupPk: id }));
    },
    [history, id]
  );
  const parsedDiscountCodes = useMemo(
    () => parseDiscountCodes(discountCodes),
    [discountCodes]
  );

  return (
    <Card onClick={isEmbedded ? undefined : handleClick}>
      <Row gutter={6}>
        {displayGroupLogo && (
          <Column collapse={0}>
            <Link to={routeConfig.groupDetails.getLink({ groupPk: id })}>
              <GroupIcon>
                <FeatherIcon name="users" />
              </GroupIcon>
            </Link>
          </Column>
        )}
        <Column collapse={0} grow>

            <Link
              style={{
                color: "inherit",
                textDecoration: "none",
              }}
              to={routeConfig.groupDetails.getLink({ groupPk: id })}
            >
              {name}
            </Link>

            {eventCount} événement{eventCount > 1 ? "s" : ""} •{" "}
            {membersCount} membre{membersCount > 1 ? "s" : ""}

        </Column>
      </Row>

      {displayType && typeLabel && (

          {typeLabel}
          {labels &&
            labels.map((label, index) => {label})}

      )}

      {displayDescription && description && (

          <Collapsible
            maxHeight={100}
            expanderLabel="Lire la suite"
            dangerouslySetInnerHTML={{ __html: description }}
            fadingOverflow
          />

      )}

      {parsedDiscountCodes && parsedDiscountCodes.length > 0 && (
        <DiscountCodesSection>

Codes matériels :

            {parsedDiscountCodes.map(({ code, expiration }) => (

                {code} (expire {expiration})

            ))}

        </DiscountCodesSection>
      )}
      <Row gutter={6} style={{ marginTop: "1.5rem" }}>
        {[
          !isEmbedded && !isMember && (

          ),
          ,
          routes.fund && (
            <GroupButton key="fund" href={routes.fund} icon="fast-forward">
              Financer
            </GroupButton>
          ),
          isManager && (

          ),
        ]
          .filter(Boolean)
          .map((button, i) => (
            <React.Fragment key={button.key + i}>
              {i > 0 && <Spacer size="0.5rem" />}
              {button}
            </React.Fragment>
          ))}
      </Row>
      {displayMembership && isMember && (

          <FeatherIcon small inline name="check" /> Vous êtes membre du groupe

      )}
    </Card>
  );
};

GroupCard.propTypes = {
  name: PropTypes.string.isRequired,
  description: PropTypes.string,
  eventCount: PropTypes.number,
  membersCount: PropTypes.number,
  isMember: PropTypes.bool,
  isManager: PropTypes.bool,
  typeLabel: PropTypes.string,
  labels: PropTypes.arrayOf(PropTypes.string),
  routes: PropTypes.object,
  displayGroupLogo: PropTypes.bool,
  displayType: PropTypes.bool,
  displayDescription: PropTypes.bool,
  displayMembership: PropTypes.bool,
  ...DiscountCodesSection.propTypes,
  isEmbedded: PropTypes.bool,
};

GroupCard.defaultProps = {
  displayGroupLogo: true,
  displayType: true,
  displayDescription: true,
  displayMembership: true,
  isManager: false,
};

export default GroupCard;
<|endoftext|>


<commit_before><commit_msg>Output Broccoli build times<commit_after><|endoftext|>


<commit_before>
module.exports = {
  types: [
    {value: 'feat',     name: 'feat:     A new feature'},
    {value: 'update',   name: 'update:   Updates feature'},
    {value: 'fix',      name: 'fix:      A bug fix'},
    {value: 'docs',     name: 'docs:     Documentation only changes'},
    {value: 'style',    name: 'style:    Changes that do not affect the meaning of the code\n            (white-space, formatting, missing semi-colons, etc)'},
    {value: 'refactor', name: 'refactor: A code change that neither fixes a bug nor adds a feature'},
    {value: 'perf',     name: 'perf:     A code change that improves performance'},
    {value: 'test',     name: 'test:     Adding missing tests'},
    {value: 'chore',    name: 'chore:    Changes to the build process or auxiliary tools\n            and libraries such as documentation generation'},
    {value: 'revert',   name: 'revert:   Revert to a commit'},
    {value: 'WIP',      name: 'WIP:      Work in progress'},
    {value: 'init',     name: 'init:     Initial commit'}
  ],
  // scopes: {Array of Strings}: Specify the scopes for your particular project.
  // Eg.: for some banking system: ["acccounts", "payments"].
  // For another travelling application: ["bookings", "search", "profile"]
  scopes: [
    {name: 'src'},
    {name: '__internals__'},
    {name: '__tests__'},
    {name: 'root'}
  ],
  // scopeOverrides: {Object where key contains a Array of String}:
  // Use this when you want to override scopes for a specific commit type.
  // Example bellow specify scopes when type is fix:
  scopeOverrides: {
    fix: [
      {name: 'merge'},
      {name: 'style'},
      {name: 'unitTest'}
    ]
  },
  // allowCustomScopes: {boolean, default false}: adds the option custom to
  // scope selection so you can still typea scope if you need.
  allowCustomScopes: true,
  // allowBreakingChanges: {Array of Strings: default none}. List of commit
  // types you would like to the question breaking change prompted. Eg.: ['feat', 'fix']
  allowBreakingChanges: ['feat', 'fix']
};
<commit_msg>chore(root): remove scopeOverride from cz-config<commit_after>
module.exports = {
  types: [
    {value: 'feat',     name: 'feat:     A new feature'},
    {value: 'update',   name: 'update:   Updates feature'},
    {value: 'fix',      name: 'fix:      A bug fix'},
    {value: 'docs',     name: 'docs:     Documentation only changes'},
    {value: 'style',    name: 'style:    Changes that do not affect the meaning of the code\n            (white-space, formatting, missing semi-colons, etc)'},
    {value: 'refactor', name: 'refactor: A code change that neither fixes a bug nor adds a feature'},
    {value: 'perf',     name: 'perf:     A code change that improves performance'},
    {value: 'test',     name: 'test:     Adding missing tests'},
    {value: 'chore',    name: 'chore:    Changes to the build process or auxiliary tools\n            and libraries such as documentation generation'},
    {value: 'revert',   name: 'revert:   Revert to a commit'},
    {value: 'WIP',      name: 'WIP:      Work in progress'},
    {value: 'init',     name: 'init:     Initial commit'}
  ],
  // scopes: {Array of Strings}: Specify the scopes for your particular project.
  // Eg.: for some banking system: ["acccounts", "payments"].
  // For another travelling application: ["bookings", "search", "profile"]
  scopes: [
    {name: 'src'},
    {name: '__internals__'},
    {name: '__tests__'},
    {name: 'root'}
  ],
  // scopeOverrides: {Object where key contains a Array of String}:
  // Use this when you want to override scopes for a specific commit type.
  // Example bellow specify scopes when type is fix:
  scopeOverrides: {},
  // allowCustomScopes: {boolean, default false}: adds the option custom to
  // scope selection so you can still typea scope if you need.
  allowCustomScopes: true,
  // allowBreakingChanges: {Array of Strings: default none}. List of commit
  // types you would like to the question breaking change prompted. Eg.: ['feat', 'fix']
  allowBreakingChanges: ['feat', 'fix']
};
<|endoftext|>


<commit_before><commit_msg>get information test<commit_after><|endoftext|>


<commit_before>import { without } from 'substance'

import {
  AbstractAPI, DynamicCollection,
  StringModel, TextModel, FlowContentModel
} from '../kit'

import ContribsModel from './models/ContribsModel'
import MetaModel from './models/MetaModel'
import renderEntity from './shared/renderEntity'
import TranslateableModel from './models/TranslateableModel'

import { REQUIRED_PROPERTIES } from './ArticleConstants'

// TODO: this should come from configuration
const COLLECTIONS = {
  'author': 'authors',
  'editor': 'editors'
}

export default class ArticleAPI extends AbstractAPI {
  constructor (articleSession, modelRegistry) {
    super()

    this.modelRegistry = modelRegistry
    this.articleSession = articleSession
    this.article = articleSession.getDocument()
  }

  /*
    Get corresponding model for a given node. This used for most block content types (e.g. Figure, Heading etc.)
  */
  getModel (type, node) {
    let ModelClass = this.modelRegistry[type]
    // HACK: trying to retrieve the node if it is not given
    if (!node) {
      node = this.article.get(type)
    }
    if (ModelClass) {
      return new ModelClass(this, node)
    } else if (node) {
      return this._getModelForNode(node)
    }
  }

  getModelById (id) {
    let node = this.article.get(id)
    if (node) {
      // now check if there is a custom model for this type
      let ModelClass = this.modelRegistry[node.type]
      // TODO: we could go and check if there is a component
      // registered for any of the parent types
      if (!ModelClass) {
        let superTypes = node.getSchema().getSuperTypes()
        for (let superType of superTypes) {
          ModelClass = this.modelRegistry[superType]
          if (ModelClass) break
        }
      }
      if (ModelClass) {
        return new ModelClass(this, node)
      }
      let model = this._getModelForNode(node)
      if (model) {
        return model
      }
    }
  }

  _getNode (nodeId) {
    return this.article.get(nodeId)
  }

  getEntitiesByType (type) {
    // TODO: this needs to be done in a different way:
    // nodes have specific place now, e.g. 'author' nodes are located in 'authors'
    // That means rather than using the general 'type' index we should get the appropriate collection
    // let entityIds = this.article.findByType(type)
    // return entityIds.map(entityId => this._getModelById(entityId))
    return []
  }

  // TODO: this should be configurable. As it is similar to HTML conversion
  // we could use the converter registry for this
  renderEntity (model) {
    let entity = this.getArticle().get(model.id)
    return renderEntity(entity)
  }

  getArticle () {
    return this.article
  }

  getArticleSession () {
    return this.articleSession
  }

  addItemToCollection (item, collection) {
    this.articleSession.transaction(tx => {
      let node = tx.create(item)
      tx.get(collection._node.id).appendChild(tx.get(node.id))
      tx.selection = null
    })
  }

  removeItemFromCollection (item, collection) {
    this.articleSession.transaction(tx => {
      tx.get(collection._node.id).removeChild(tx.get(item.id))
      tx.delete(item.id)
      tx.selection = null
    })
  }

  addAuthor (person = {}) {
    this._addPerson(person, 'authors')
  }

  addEditor (person = {}) {
    this._addPerson(person, 'editors')
  }

  _addPerson (person = {}, type) {
    const newNode = Object.assign({}, person, {
      type: 'person'
    })
    let node
    this.articleSession.transaction(tx => {
      node = tx.create(newNode)
      const articleRecord = tx.get('article-record')
      let length = articleRecord[type].length
      tx.update(['article-record', type], { type: 'insert', pos: length, value: node.id })
      tx.selection = null
    })
    return this.getModel(node.type, node)
  }

  getAuthors () {
    return this._getPersons('authors')
  }

  getAuthorsModel () {
    return this.getModel('authors')
  }

  getEditors () {
    return this._getPersons('editors')
  }

  _getPersons (prop) {
    // TODO: authors and editors are now in article/metadata/authors and article/metadata/editors
    return []
  }

  deleteAuthor (personId) {
    return this._deletePerson(personId, 'authors')
  }

  deleteEditor (personId) {
    return this._deletePerson(personId, 'editors')
  }

  _deletePerson (personId, type) {
    // FIXME: persons are now in a different place
    // let node
    // this.articleSession.transaction((tx) => {
    //   node = tx.delete(personId)
    //   const articleRecord = tx.get('article-record')
    //   let pos = articleRecord[type].indexOf(personId)
    //   if (pos !== -1) {
    //     tx.update(['article-record', type], { type: 'delete', pos: pos })
    //   }
    //   tx.selection = null
    // })
    // return this.getModel(node.type, node)
  }

  addReferences (refs) {
    const refContribProps = ['authors', 'editors', 'inventors', 'sponsors', 'translators']
    const articleSession = this.articleSession
    articleSession.transaction(tx => {
      const refList = tx.find('ref-list')
      refs.forEach(ref => {
        refContribProps.forEach(propName => {
          if (ref[propName]) {
            let refContribs = ref[propName].map(contrib => {
              contrib.type = 'ref-contrib'
              const node = tx.create(contrib)
              return node.id
            })
            ref[propName] = refContribs
          }
        })
        const node = tx.create(ref)
        const refEl = tx.createElement('ref').attr('rid', node.id)
        refList.append(refEl)
      })
      tx.selection = null
    })
  }

  deleteReference (item, collection) {
    const articleSession = this.articleSession
    articleSession.transaction(tx => {
      const xrefIndex = tx.getIndex('xrefs')
      const xrefs = xrefIndex.get(item.id)
      tx.get(collection._node.id).removeChild(tx.get(item.id))
      xrefs.forEach(xrefId => {
        let xref = tx.get(xrefId)
        let idrefs = xref.attr('rid').split(' ')
        idrefs = without(idrefs, item.id)
        xref.setAttribute('rid', idrefs.join(' '))
      })
      tx.delete(item.id)
      tx.selection = null
    })
  }

  /*
    @return {StringModel} Model for the language code of article's main language
  */
  getOriginalLanguageCode () {
    let article = this.getArticle().getRootNode()
    return new StringModel(this, [article.id, 'attributes', 'xml:lang'])
  }

  getCollectionForType (type) {
    // TODO: need to rethink this.
    // ATM we are registering special model classes for collections,
    // which are named after the single entity they contain,
    // e.g. 'authors' is a the collection of author nodes (which are person nodes essentially).
    // This is currently pretty confusing. It would be better to have
    // specific node in the model for that.
    // Still, we would need a mechanism to specify which node is acting as the parent
    // collection for which node.
    let collectionType = COLLECTIONS[type]
    // I don't like this implicit mapping.
    if (!collectionType) {
      collectionType = type + 's'
    }
    let model = this.getModel(collectionType)
    if (!model) {
      console.error(`No collection specified for type '${type}'. Using DynamicCollection. You should register a collection for this type explicitly.`)
      model = new DynamicCollection(this, type)
    }
    return model
  }

  getSchema (type) {
    return this.article.getSchema().getNodeSchema(type)
  }

  getArticleTitle () {
    let titleNode = this.getArticle().get('title')
    return new TextModel(this, titleNode.getPath())
  }

  getArticleAbstract () {
    let abstract = this.getArticle().get('abstract')
    return new FlowContentModel(this, abstract.getContentPath())
  }

  getArticleBody () {
    let body = this.getArticle().get('body')
    return new FlowContentModel(this, body.getContentPath())
  }

  getContribs () {
    let articleMeta = this.getArticle().find('article-meta')
    return new ContribsModel(articleMeta, this._getContext())
  }

  getMeta () {
    let articleMeta = this.getArticle().find('article-meta')
    return new MetaModel(articleMeta, this._getContext())
  }

  getFootnotes () {
    // TODO: this should return a Model for article.get('footnotes')
    return []
  }

  getFigures () {
    let figs = this.getArticle().get('body').findAll('fig')
    return figs.map(fig => this.getModel(fig.type, fig))
  }

  getTranslatables () {
    const translatableItems = ['title', 'abstract']
    const article = this.getArticle()
    const models = translatableItems.map(item => new TranslateableModel(this, article.get(item)))
    return models
  }

  addTranslation (model, languageCode) {
    const isText = model._node.isText()
    const articleSession = this.articleSession
    articleSession.transaction(tx => {
      let item = {
        language: languageCode
      }
      if (isText) {
        item.type = 'text-translation'
      } else {
        item.type = 'container-translation'
      }
      let node = tx.create(item)
      // HACK: trying to avoid selection errors of empty container
      if (!isText) node.append(tx.create({type: 'p'}))
      let length = tx.get([model.id, 'translations']).length
      tx.update([model.id, 'translations'], { type: 'insert', pos: length, value: node.id })
      tx.selection = null
    })
  }

  deleteTranslation (translatableModel, translationModel) {
    const articleSession = this.articleSession
    articleSession.transaction(tx => {
      let translatable = tx.get(translatableModel.id)
      let pos = translatable.translations.indexOf(translationModel.id)
      if (pos !== -1) {
        tx.update([translatableModel.id, 'translations'], { type: 'delete', pos: pos })
      }
      tx.delete(translationModel.id)
      tx.selection = null
    })
  }

  _getContext () {
    return this._context
  }

  /*
    TODO: In the future it should be necessary to expose those managers, instead
    API's should be used to access information.
  */
  getFigureManager () {
    return this.getArticleSession().getFigureManager()
  }

  getFootnoteManager () {
    return this.getArticleSession().getFootnoteManager()
  }

  getReferenceManager () {
    return this.getArticleSession().getReferenceManager()
  }

  getTableManager () {
    return this.getArticleSession().getTableManager()
  }

  get doc () {
    console.error('DEPRECATED: use api.getArticle() instead.')
    return this.getArticle()
  }

  _getDocument () {
    return this.getArticleSession().getDocument()
  }

  _getDocumentSession () {
    return this.getArticleSession()
  }

  _isPropertyRequired (type, propertyName) {
    let REQUIRED = REQUIRED_PROPERTIES[type]
    if (REQUIRED) return REQUIRED.has(propertyName)
    return false
  }
}
<commit_msg>Remove obsolete person apis.<commit_after>import { without } from 'substance'

import {
  AbstractAPI, DynamicCollection,
  StringModel, TextModel, FlowContentModel
} from '../kit'

import ContribsModel from './models/ContribsModel'
import MetaModel from './models/MetaModel'
import renderEntity from './shared/renderEntity'
import TranslateableModel from './models/TranslateableModel'

import { REQUIRED_PROPERTIES } from './ArticleConstants'

// TODO: this should come from configuration
const COLLECTIONS = {
  'author': 'authors',
  'editor': 'editors'
}

export default class ArticleAPI extends AbstractAPI {
  constructor (articleSession, modelRegistry) {
    super()

    this.modelRegistry = modelRegistry
    this.articleSession = articleSession
    this.article = articleSession.getDocument()
  }

  /*
    Get corresponding model for a given node. This used for most block content types (e.g. Figure, Heading etc.)
  */
  getModel (type, node) {
    let ModelClass = this.modelRegistry[type]
    // HACK: trying to retrieve the node if it is not given
    if (!node) {
      node = this.article.get(type)
    }
    if (ModelClass) {
      return new ModelClass(this, node)
    } else if (node) {
      return this._getModelForNode(node)
    }
  }

  getModelById (id) {
    let node = this.article.get(id)
    if (node) {
      // now check if there is a custom model for this type
      let ModelClass = this.modelRegistry[node.type]
      // TODO: we could go and check if there is a component
      // registered for any of the parent types
      if (!ModelClass) {
        let superTypes = node.getSchema().getSuperTypes()
        for (let superType of superTypes) {
          ModelClass = this.modelRegistry[superType]
          if (ModelClass) break
        }
      }
      if (ModelClass) {
        return new ModelClass(this, node)
      }
      let model = this._getModelForNode(node)
      if (model) {
        return model
      }
    }
  }

  _getNode (nodeId) {
    return this.article.get(nodeId)
  }

  getEntitiesByType (type) {
    // TODO: this needs to be done in a different way:
    // nodes have specific place now, e.g. 'author' nodes are located in 'authors'
    // That means rather than using the general 'type' index we should get the appropriate collection
    // let entityIds = this.article.findByType(type)
    // return entityIds.map(entityId => this._getModelById(entityId))
    return []
  }

  // TODO: this should be configurable. As it is similar to HTML conversion
  // we could use the converter registry for this
  renderEntity (model) {
    let entity = this.getArticle().get(model.id)
    return renderEntity(entity)
  }

  getArticle () {
    return this.article
  }

  getArticleSession () {
    return this.articleSession
  }

  addItemToCollection (item, collection) {
    this.articleSession.transaction(tx => {
      let node = tx.create(item)
      tx.get(collection._node.id).appendChild(tx.get(node.id))
      tx.selection = null
    })
  }

  removeItemFromCollection (item, collection) {
    this.articleSession.transaction(tx => {
      tx.get(collection._node.id).removeChild(tx.get(item.id))
      tx.delete(item.id)
      tx.selection = null
    })
  }

  getAuthorsModel () {
    return this.getModel('authors')
  }

  addReferences (refs) {
    const refContribProps = ['authors', 'editors', 'inventors', 'sponsors', 'translators']
    const articleSession = this.articleSession
    articleSession.transaction(tx => {
      const refList = tx.find('ref-list')
      refs.forEach(ref => {
        refContribProps.forEach(propName => {
          if (ref[propName]) {
            let refContribs = ref[propName].map(contrib => {
              contrib.type = 'ref-contrib'
              const node = tx.create(contrib)
              return node.id
            })
            ref[propName] = refContribs
          }
        })
        const node = tx.create(ref)
        const refEl = tx.createElement('ref').attr('rid', node.id)
        refList.append(refEl)
      })
      tx.selection = null
    })
  }

  deleteReference (item, collection) {
    const articleSession = this.articleSession
    articleSession.transaction(tx => {
      const xrefIndex = tx.getIndex('xrefs')
      const xrefs = xrefIndex.get(item.id)
      tx.get(collection._node.id).removeChild(tx.get(item.id))
      xrefs.forEach(xrefId => {
        let xref = tx.get(xrefId)
        let idrefs = xref.attr('rid').split(' ')
        idrefs = without(idrefs, item.id)
        xref.setAttribute('rid', idrefs.join(' '))
      })
      tx.delete(item.id)
      tx.selection = null
    })
  }

  /*
    @return {StringModel} Model for the language code of article's main language
  */
  getOriginalLanguageCode () {
    let article = this.getArticle().getRootNode()
    return new StringModel(this, [article.id, 'attributes', 'xml:lang'])
  }

  getCollectionForType (type) {
    // TODO: need to rethink this.
    // ATM we are registering special model classes for collections,
    // which are named after the single entity they contain,
    // e.g. 'authors' is a the collection of author nodes (which are person nodes essentially).
    // This is currently pretty confusing. It would be better to have
    // specific node in the model for that.
    // Still, we would need a mechanism to specify which node is acting as the parent
    // collection for which node.
    let collectionType = COLLECTIONS[type]
    // I don't like this implicit mapping.
    if (!collectionType) {
      collectionType = type + 's'
    }
    let model = this.getModel(collectionType)
    if (!model) {
      console.error(`No collection specified for type '${type}'. Using DynamicCollection. You should register a collection for this type explicitly.`)
      model = new DynamicCollection(this, type)
    }
    return model
  }

  getSchema (type) {
    return this.article.getSchema().getNodeSchema(type)
  }

  getArticleTitle () {
    let titleNode = this.getArticle().get('title')
    return new TextModel(this, titleNode.getPath())
  }

  getArticleAbstract () {
    let abstract = this.getArticle().get('abstract')
    return new FlowContentModel(this, abstract.getContentPath())
  }

  getArticleBody () {
    let body = this.getArticle().get('body')
    return new FlowContentModel(this, body.getContentPath())
  }

  getContribs () {
    let articleMeta = this.getArticle().find('article-meta')
    return new ContribsModel(articleMeta, this._getContext())
  }

  getMeta () {
    let articleMeta = this.getArticle().find('article-meta')
    return new MetaModel(articleMeta, this._getContext())
  }

  getFootnotes () {
    // TODO: this should return a Model for article.get('footnotes')
    return []
  }

  getFigures () {
    let figs = this.getArticle().get('body').findAll('fig')
    return figs.map(fig => this.getModel(fig.type, fig))
  }

  getTranslatables () {
    const translatableItems = ['title', 'abstract']
    const article = this.getArticle()
    const models = translatableItems.map(item => new TranslateableModel(this, article.get(item)))
    return models
  }

  addTranslation (model, languageCode) {
    const isText = model._node.isText()
    const articleSession = this.articleSession
    articleSession.transaction(tx => {
      let item = {
        language: languageCode
      }
      if (isText) {
        item.type = 'text-translation'
      } else {
        item.type = 'container-translation'
      }
      let node = tx.create(item)
      // HACK: trying to avoid selection errors of empty container
      if (!isText) node.append(tx.create({type: 'p'}))
      let length = tx.get([model.id, 'translations']).length
      tx.update([model.id, 'translations'], { type: 'insert', pos: length, value: node.id })
      tx.selection = null
    })
  }

  deleteTranslation (translatableModel, translationModel) {
    const articleSession = this.articleSession
    articleSession.transaction(tx => {
      let translatable = tx.get(translatableModel.id)
      let pos = translatable.translations.indexOf(translationModel.id)
      if (pos !== -1) {
        tx.update([translatableModel.id, 'translations'], { type: 'delete', pos: pos })
      }
      tx.delete(translationModel.id)
      tx.selection = null
    })
  }

  _getContext () {
    return this._context
  }

  /*
    TODO: In the future it should be necessary to expose those managers, instead
    API's should be used to access information.
  */
  getFigureManager () {
    return this.getArticleSession().getFigureManager()
  }

  getFootnoteManager () {
    return this.getArticleSession().getFootnoteManager()
  }

  getReferenceManager () {
    return this.getArticleSession().getReferenceManager()
  }

  getTableManager () {
    return this.getArticleSession().getTableManager()
  }

  get doc () {
    console.error('DEPRECATED: use api.getArticle() instead.')
    return this.getArticle()
  }

  _getDocument () {
    return this.getArticleSession().getDocument()
  }

  _getDocumentSession () {
    return this.getArticleSession()
  }

  _isPropertyRequired (type, propertyName) {
    let REQUIRED = REQUIRED_PROPERTIES[type]
    if (REQUIRED) return REQUIRED.has(propertyName)
    return false
  }
}
<|endoftext|>