Skip to content

nuagenetworks/vis-graphs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

How to use graphs

The use of the graphs module is to provide a module to quickly shows your data into graphs.

Table of Contents

Requirement-

  • We must need the following libraries which are using in different graphs -
  "react": "15.6.0",
  "react-dom": "15.6.0",
  "d3": "4.10.0",
  "eval-expression": "^1.0.0",
  "lodash": "^4.17.4",
  "material-ui": "^0.16.7",
  "material-ui-datatables": "0.18.2",
  "material-ui-superselectfield": "^1.9.8",
  "react-copy-to-clipboard": "^4.3.1",
  "react-filter-box": "^2.0.0",
  "react-icons": "^2.2.7",
  "react-lightweight-tooltip": "0.0.4",
  "react-tap-event-plugin": "2.0.1",
  "react-tooltip": "^3.2.1",
  "object-path": "^0.11.4",
  "react-google-maps": "^9.4.5",
  "prop-types": "^15.6.2",
  "react-csv": "1.0.8",
  "react-copy-to-clipboard": "^4.3.1",
  "react-modal": "^3.5.1",
  "eval-expression": "^1.0.0"

Usage examples

  • Make sure your current project must be a valid git project, if not then run the below command git init
  • Now run the following command to download graph module into your specified path
  git submodule add https://github.com/nuagenetworks/vis-graphs.git your-path

Here is an example how to use bar graph into your component -

import React, {Component} from 'react';
import { GraphManager } from "path-to-your-graph-component/vis-graphs/Graphs/index";
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import { theme } from "path-to-your-graph-component/vis-graphs/theme"
import injectTapEventPlugin from "react-tap-event-plugin";

injectTapEventPlugin();

const TABLE_DATA =  [
    {
        "L7Classification": "Proin",
        "Sum of MB": 10000
    },
    {
        "L7Classification": "Justo",
        "Sum of MB": 25000
    },
    ...
];

class Graph extends Component {

  handleClickEvent(datum) {
    //..handle click event
  }
  render() {
    // pass a graph name to getGraphComponent as a param to use that graph
    const GraphComponent = GraphManager.getGraphComponent('BarGraph')
    return (
        <MuiThemeProvider muiTheme={theme}>
            <GraphComponent
              data={data}
              data1={data1} // you may pass data from multiple source as well
              configuration={configuration} // configuration object
              width={width} // graph width (numeric)
              height={height} // graph height (numeric)
              onMarkClick={ this.handleClickEvent } // event listener
            />
        </MuiThemeProvider>
    );
  }
}

Note:

  • Make sure you have to wrap graph component with MuiThemeProvider and pass graph's theme to MuiThemeProvider as a props
  • Register injectTapEventPlugin() method before calling graph component to enable touch tap event on graphs

Common configuration

Configuration is a little more complex as it has more options. But it is working the same way, so don't worry :)

Here is the list of common options:

excludedColumns - (object) return array of fields/columns which need to remove from advance search filter. E.g -

 "excludedColumns": {
        "columnList": "['tcpflags', 'bytes', 'packets', 'classification.type']" // columnList may be a function or an array which will return an array of fields.
    }

enabledCount - (boolean) used to get the data length instead of data.

 // ...
    "data": {
        // ...
        "enabledCount": true,
    }
    // ...

Tolltip - If you want to add tooltips on an existing configuration ? Update its configuration:

  • column* - attribute name to use to display the value
  • label - tooltip label. If not specified, column will be used.
  • format - d3 format style to display the column value
  • duration - used to dispaly duration in date time format. You may set the format as per your choice.
{
    // ...
    "data": {
        // ...
        "tooltip": [
            { "column": "L7Classification", "label": "L7 Signature" },
            { "column": "Sum of MB", "format": ",.2s"},
            { "column": "timestamp", "label": "Value", "duration": "h [hrs], m [min], s [sec]"}
        ]
    }
    // ...
}

tooltip

The example above will display will display a tooltip with 2 lines (See picture below)

onMarkClick - Method used to handle click event

brush - (Number) to enble brushing with pre selected bars.Currently support in bar graph and heatmap graph. E.g -

"brush": 3,
"brushArea": 20
  • brushArea (Number) space in visualization where brush slider display (in percentage). Default is 20.

You may see example in Heatmap and Bargraph section

padding

  • top set top padding in pixels
  • bottom set bottom padding in pixels
  • right set right padding in pixels
  • left set left padding in pixels

textgraph 1

padding currently supported only for text graph

margin*

  • top set top margin in pixels
  • bottom set bottom margin in pixels
  • right set right margin in pixels
  • left set left margin in pixels

margin-example

colors - (array) List of colors to use to render the graph.

yLabelLimit - (numeric) Limit the character of y-axis label. Above the defined limit, the substring of the label will be display followed by the "..." and full label will be show on mouseover.

appendCharLength - (numeric) The length of the appended dots after the label if yLabelLimit defined

xLabelRotate - (boolean) rotate x-axis labels. Default is true. xLabelRotateHeight - (numeric) if xLabelRotate is enable then height occupied by x-axis labels. Default is 35 (in px). xLabelLimit - (numeric) number of character need to display. Default is 10. After 10 character ... is append with tooltip of full character for each label.

stroke

  • width define stroke width
  • color define stroke color

legend

  • show true to display legend. false otherwise. Default is false
  • orientation vertical or horizontal legend. Default is vertical
  • circleSize size of a legend circle. Default is 4 pixels
  • labelOffset space in pixel between the legend circle and its label. Default is 2.
  • labelFontSize (numeric) - Font size of the label.
  • separate (numeric) - This will separate the legends from the charts and use the provided value as percentage to define the area for the legends. This will be useful in case of large number of legends and enable the scroll for legends as well (if applicable). If value is 20, then 80% of the area will be used by charts and 20% will be used by legends.

filterOptions - Allows to set filters on the visualization. See dashboard configuration for more information as it is working the same way!

dateHistogram - To enable date formatted scaling if any of x-axis or y-axis data contain date. Default is false

x-axis and y-axis - (Supported Graphs - BarGraph, PieGraph, AreaGraph, HeatmapGraph, LineGraph)

  • xColumn attribute name in your results to use for x-axis

  • xLabel x-axis title

  • xTicks number of ticks to use for x-axis

  • xTickFormat d3 format style to display x-axis labels

  • xTickGrid (boolean) If set to true then the complete grid will be drawn

  • xTickSizeInner - If size is specified, sets the inner tick size to the specified value and returns the axis.

  • xTickSizeOuter If size is specified, sets the outer tick size to the specified value and returns the axis.

  • yColumn* attribute name in your results to use for y-axis

  • yLabel y-axis title

  • yTicks number of ticks to use on y-axis

  • yTickFormat d3 format style to display y-axis labels. Use empty string "" if whole number ticks are required.

  • yTickFormatType If y axis data is in duration(miliseconds) then set yTickFormatType: 'duration' and define format in yTickFormat property to make it readable. Here is the link for the duration format - https://www.npmjs.com/package/moment-duration-format.

E.g -

    "data": {
    ...
    "yTickFormat": "mm:ss",
    "yTickFormatType": "duration",
    ...
    }
  • yTickGrid (boolean) If set to true then the complete grid will be drawn
  • yTickSizeInner If size is specified, sets the inner tick size to the specified value and returns the axis.
  • yTickSizeOuter If size is specified, sets the outer tick size to the specified value and returns the axis.
  • zeroStart (boolean) eg. if the value is from range say 30 to 89 and zeroStart is enabled, then this will change the range to 0 to 89. Default value of zeroStart is "true", and currently applicable for line and area graph.
  • yRangePadding (boolean) default value is true, if property is enabled then this will add padding around the min and max range, say min and max range is 34 to 83, then this will change it to 30 to 90.

Graph specific configuration

BarGraph

Display vertical or horizontal bar charts

See sample configuration and data file

horizontal-bar

orientation - Orientation of the graph. Default is vertical. Set to horizontal to have an horizontal bar chart.

otherOptions - (object) For grouping a data in order to show in single bar after defined limit of bars. Grouping can either be define in percentage or number. Default is percentage. E.g -

  "otherOptions": {
        "label": "Others", //used to display name of bar
        "limit": 5, // afer a given limit grouping is enable
        "type": "number" // it can be percentage as well
    }

stackColumn - Used to show stacked data in bars. E.g-

  "stackColumn": "social"

stacked

stackSequence (array) sorting of stacked data manually. Only applicable when stackColumn is enable. It is an optional property. E.g -

"stackSequence": ['GOOGLE', 'NETFLIX']

In above example, 'google' & 'netflix' will display at the start of stacked bars and rest data will be sorted asc or desc order as per defined in sorting.

brush (number) To enble brushing with pre selected bars.Currently support in bar graph and heatmap graph. E.g -

"brush": 3,
"brushArea": 20

dynamicbargraph

xTicksLabel - (object) used to override labels of x axis ticks with some predefined strings. E.g -

   "xTicksLabel": {
       "NETFLIX": "NET",
       "WEBEX": "WEB",
       "HTTP": "HT",
       "GOOGLE": "Google",
       "MSOffice365": "Office"
   }

isSort - (boolean) - disable sorting on data to be display on bar graph. Default is true

E.g -

    "yColumn": "SumofBytes",
    "isSort": false

GroupBarGraph

Display group bar graph

See sample configuration and data file grouped-bar-graph

groupedKeys (array) - Define an array of keys which are used to form a group of bars. E.g -

    "groupedKeys": [
        "Under 5 Years",
        "5 to 13 Years",
        "14 to 17 Years",
        "18 to 24 Years",
        "25 to 44 Years",
        "45 to 64 Years",
        "65 Years and Over"
    ]

Note: - Rest properties can import from dynamic bar graph to build group bar graph.

LineGraph

Display one or multiple lines

See sample configuration and data file

multiline-chart

linesColumn (array || string)- attribute name in your results to display line value.

    "linesColumn": "L7Classification" // for single line
    or 
    "linesColumn": ["CPU", "MEMORY",  "DISK"] // for multiple line

or we may manually specify the color of each lines. For e.g -

       "linesColumn": [ //for multiple line with given colors
           {"key": "CPU", "color":"red"},
           {"key": "MEMORY", "color":"green"},
           {"key": "DISK", "color":"blue"}
   ],

showNull - (Boolean) If false, Show truncated line if yValue is null . Default is true connected - (Boolean) If true then it will create the lines without any gaps and zero for missing x axis. Default is false. defaultY - (string | object) default yAxis value used to draw straight horizontal line to show cut off value. It can be object which define data source and column to get data from another query and you may define separate tooltip for this staright line from data source. Example -

 {
     `"defaultY": {
         "source": "data2",
         "column": "memory",
         "tooltip": [
             { "column": "memory", "label": "memory"},
             { "column": "cpu", "label": "cpu"}
         ]
     }
 }

yTicksLabel - (object) used to override labels of y axis ticks with some predefined strings. E.g -

{
   "-1": "inactive",
   "0": "starting" ,
   "1": "active"
}

See x-axis and y-axis sections in BarGraph for more information

PieGraph

Display nice Pie or Donut graphs

See sample configuration and data file

donut

pieInnerRadius - Inner radius of the slices. Make this non-zero for a Donut Chart

pieOuterRadius - Outer radius of the slices

pieLabelRadius - Radius for positioning labels

percentages - (boolean) Show area in percentage in each slice of pie chart. Default is false.

percentagesFormat - Format data for percentage.

labelCount - (number) Hide labels of pie graph if slice count is greater than labelCount. Default is 5.

labelFontSize - (number) font size of the label. Default is 10.

labelLimit - (numeric) number of character need to display. Default is 10. After 10 character ... is append with tooltip of full character for each label.

otherOptions - optional object

  • type Value must be percentage or number, and default is percentage
  • limit As per the type we can define the limit in percentage or slices respectively.
  • minimum In case of percentage, if we want to override the mimium slices of 10.

Table

This is used to show data in tabular form

See sample configuration and data file

table

selectable - To enable/disable selectable feature - Default is true

multiSelectable - To enable/disable multi select feature - default is false

showCheckboxes - To show checkboxes to select rows - default is false

enableSelectAll - To enable/disable select all feature - Default is true

matchingRowColumn - (string) Compare matchingRowColumn value with all available datas and if equal to selected row, then save all matched records in store under "matchedRows"

selectColumnOption - (Boolean) To show columns selection dropdown set this value to true (default is false). In Columns array set display: false to hide any column (default is true, i.e. column will display inside the table if display is missing or set to true).

selectedColumns - (Array) Containing the list of labels for the columns to be displayed, if empty or not present then will be used the display key of the columns records. Must be applicable if selectColumnOption is set to true

onColumnSelection - (handler) Event to capture the list of selected columns and must be passed as props.

highlight - (Array of columns) Highlighted the rows if value of columns is not null

fixedHeader - (boolean) fix table header while scrolling. Default is true.

hidePagination - Hide paging and search bar if data size is less than pagination limit - Default is true

enableNumericSearch - (boolean) To enable/disable addition search operator for number type column value - Default is false

border

  • top set top border. Default is solid 1px #ccc
  • bottom set bottom border. Default is 0
  • right set right border. Default is 0
  • left set left border. Default is 0

header - header specific parameters includes

fontColor - Color of the header text

columns - (Array) Array of columns display in the table. Example -

"columns":
  [
      { "column": "type", "label": " ", "colors" : {
          "OTHER": "green",
          "DENY": "red"
          },
          "sort": false, // to disable sorting on column
          "filter": false, // hide column from search bar to filter data
          "nested": true // to enable searching on nested column of ElasticSearch query. It is applicable
          // on columns which have nested type template on ElasticSearch
      },
      { "column": "protocol", "label": "Proto", "selection": true  } // set `selection: true` to enable autocompleter for values of `protocol` column in search bar and must be string only.
      { "column": "sourceip", "label": "SIP" },
      { 
          "column": "subnetName", 
          "label": "Subnet", 
          "totalCharacters": 16, // show number of characters for column value
          "tooltip": {"column": "nuage_metadata.subnetName"}, // show tooltip on column values
          "fontColor": "red" // set the font color of the column value
          "displayOption": {"app": "facebook", "nsg": "ovs-1"} // if request parameter (context) contain any key-value of displayOption then only show given columns.
      }
  ]

tabifyOptions - Converting the provided array indexes to comma separated values, instead of generating the multiple rows (avoiding possible duplicates). E.g -

"tabifyOptions": {
    "concatenationFields": [
        {
            "path": "nuage_metadata.src-pgmem-info",
            "field": "name",
            "method": "(obj) => `${obj.name} (${obj.category})`"
        },
        {
            "path": "nuage_metadata.dst-pgmem-info",
            "field": "category"
        }
    ]
}

In above example, if a value of the column show via colors then add colors property in object and mentioned all values as a key and color as a value in order to replace color from value. Note: Add label property with space to declare empty column in the table. E.g -

table-status-with-color

ChordGraph

This graph visualises the inter-relationships between entities and compare similarities between them

See sample configuration and data file

chordgraph

outerPadding - Padding from container. Default is 30

arcThickness - Outer arc thickness. Default is 20

padAngle - Padding between arcs. Default is 0.07

labelPadding - Padding of the labels from arcs. Default is 10

transitionDuration - Duration of animation. Default is 500

defaultOpacity - Default opacity. Default is 0.6

fadedOpacity - Hovered opacity. Default is 0.1

additionalKeys - (array) declare additional fields to pass on context on event listener. E.g -

    "additionalKeys": ["SumOf", "doc_count", "hash"],

additionalMapping - (array) declare additional key and value to insert dynamically into query. It is replace the replace keyword from the query and insert dynamically generated content. E.g -

"additionalMapping": [
        {
            "fieldName": "nuage_metadata.flowid", // define key name for the query
            "additonalKey": "hash" // define value to insert into query correspnding to the defined key (it should be present in additionalKeys array)
        },
        {
            "fieldName": "nuage_metadata.packets", 
            "additonalKey": "SumOf" 
        }
    ],

Note: - to use additionalMapping in the destination visualization, please define additionalKeys in source visualization. To replace additional keys you have to use following syntax:

"query": {
            "bool": {
              "should": [
                {
                    "bool": {
                    "must": [
                        {"term": {"nuage_metadata.enterpriseName": "{{enterpriseName:chord_enterprise}}"} },
                        {
                        "replace": "additionalData"
                        }
                    ]
                    }
                }
              ]
            }
          } 

bidirectionalTooltip - Indicates if tooltip needs to display content corresponding to both directions. Default is true

SimpleTextGraph

This graph allows you to display a simple text information.

See sample configuration and data file

textgraph

targetedColumn - Name of the attribute to use to display the value. If not specified, this graph will display the length of the result

titlePosition - Position title on top or at the bottom of the graph

textAlign - Align text on left, center or right. Default is center

fontSize - Font size

fontColor - Font color

borderRadius - Set a radius if you want to display your text in a square or rounded area. Default is 50%

innerWidth - Define the percentage of the width for the area. 1 means 100% of the width. Default is 0.3

innerHeight - Define the percentage of the height for the area. 1 means 100% of the width. Default is 0.4

customText - Additional text to be displayed along with the primary text. If not provided, title will be used in its place

VariationTextGraph

This graph shows a value and its variation from the previous one.

See sample configuration and data file

variationtextgraph

drawColor - Color in case there is no variation

negativeColor - Color in case the variation is lower than 0

positiveColor - Color in case the variation is geater than 0

textAlign - Align text on left, center or right. Default is center

fontSize - Font size

fontColor - Font color

showVariation - Display percentage variation. Default is true

HeatmapGraph

This graph shows a value of a column at given timestamp. It is a graphical representation of data where the individual values contained in a matrix are represented as colors

See sample configuration and data file

heatmap

selectedData: Selected data, normally returned by onMarkClick event after clicking on the cell, and will be used to highlight the cell for selected data.

legendColumn - Used to display matrix

xAlign - (boolean) If true then align x-axis label to the left position , default align is middle

heatmapColor - (object) Used to define the color of the matrix of given legendColumn value. E.g -

`"heatmapColor": {
    "InSla": "#b3d645"
}`

AreaGraph

This graph displays graphically quantitative data. The area between axis and line are commonly emphasized with colors, textures and hatchings. Commonly one compares with an area chart two or more quantities.

See sample configuration and data file

AreaGraph

linesColumn (Object) Its value is used to display area in graph

"linesColumn": [
    {
        "key": "CPU"
    },
    {
        "key": "MEMORY"
    },
    {
        "key": "DISK",
        "value": "DISK"
    }
]

stacked - (boolean) Whether area shown as stacked or not. Default is false.

GuageGraph

Display a needle or dial to indicate where your data point(s) falls over a particular range

See sample configuration and data file

guagegraph

maxValue - Maximum value to draw speddometer

currentColumn - Column used to show needle value

gauzeTicks - Number of ticks on speedometer

Geomap

Display a cluster markers on map to show data

See sample configuration and data file

geomap

latitudeColumn - Latitude of the marker

longitudeColumn - Longitude of the marker

nameColumn - name displayed on marker infowindow

localityColumn - Locality displayed on marker infowindow

idColumn - id to uniquely identified each marker

links - (Object) to show connected lines beetween markers. For e.g.

"links": {
    "source": "data1", // data source
    "sourceColumn": "source", // source column id(equivalent to idColumn)
    "destinationColumn": "destination" // destination column id(equivalent to idColumn)
}

filters - List down columns in search bar

"filters": [
            {
                "columnText": "name",
                "columnField": "nsgatewayName",
                "type": "text"
            },
            {
                "columnField": "status",
                "type": "selection" // for `selection`, value of status field should be string
            }

        ]

markerIcon - (Object || string) to show markers icon. List of all the icons are defined in the Icon Helper files. Please add the icon over there before using the "key" over here like: nsGateway, icon1, icon2 and so on . For e.g.

"markerIcon": "nsgGateway"

or

"markerIcon": {
    "default": "default-icon", // optional
    "defaultUrgency": "GREEN", // optional
    "criteria": [
        {
            "icon": "icon1",
            "fields": {
                "nsg.status": "deactivated"
            },
            "urgency": "GRAY" // Either of "GREY", "RED", "YELLOW", "BLUE", as per criticaliy.
        },
        {
            "icon": "icon2",
            "fields": {
                "nsg.status": "activated",
                "nsg.signal": "yellow"
            }
        }
    ]
}

TreeGraph

This graph displays nested hierarchical data. It is used to show the relation between parent node and child nodes using a horizonal tree based layout.

See sample data file

TreeGraph

data (Array of object) Nested hierarchical data passed into map to show tree view.

onClickChild (Function) Function that is called when any node is clicked. It is called with one argument, the node which was clicked. Typically it is used to fetch data from server and update the children of the node in tree data.

const clickChild = (child) => {
  const { name, children } = child;
  // do something
}
<TreeGraph onClickChild={clickChild} />

width (Integer) Width of treemap area.

height (Integer) Height of treemap area.

PortGraph

This graph displays the information of networking ports.

See sample data file

PortGraph

columns (array of object) data to show "key: value" information at top of the ports in graph. Note: this data should come from secondary query source named "data2".

topColumn (string) the name of the column to use for the data on top of port icon. For example "topBottom": "portName"

bottomColumn (string) the name of the column to use for the data below port icon, example: "bottomColumn": "type"

portColor (object) define the criteria to show the color of each port icon as per defined condition. If getColor function is provided getColor is used to determine the color for the port. E.g -

"portColor": {
    "defaultColor": "gray",
    "field": "status",
    "criteria": [
        {
            "value": "UP",
            "color": "green"
        },
        {
            "value": "DOWN",
            "color": "red"
        }
    ],
    "getColor": "({state, type}) => { if (!state) return '#B5B5B5'; switch(state) { case 'DOWN': return '#D9070A'; case 'UP': return type === 'Network' ? '#5A83DE' : '#62AC00'; default: return '#B5B5B5'}}"
}

__portIcon__ (object) define the criteria to show the icon of each port. E.g -

```javascript
"portIcon": {
    "default": "nsGatewayIcon",
    "getIcon": "({name}) => { if (name === 'Port 1') return 'default2' }",
    "criteria": [
        {
            "icon": "nsGatewayIcon",
            "fields": {
                "status": "DOWN",
                "type": "ACCESS"
            }
        },
        {
            "icon": "nsGatewayIcon",
            "fields": {
                "status": "UP",
                "type": "ACCESS"
            }
        }
    ]
}

Note: icon must be a key exists in svgIcons object in helpers/icons.js

defaultIconColor (string) default color of the port icon. Default is gray .

minPortFontSize (number) minimum font size of the port icon. Default is 20 (px).

maxPortFontSize (number) maximum font size of the port icon. Default is 40 (px).

showUpperColumnName (boolean) show column text on the top of Port. Default is true.

showLowerCloumnName (boolean) show column text on the bottom of the Port. Default is true.

minPortWidth (number) minimum width of each port (container). Default is 45 (px).

rowLimit (number) show number of ports in each row. Default is 16.

tooltipScript (string) a custom Tooltip component to allow for more customization of the tooltip. The tooltip component must be available at: @/scripts and it will have available 2 props:

  • data which is the data coming from the port hovered over
  • data2 which is data from the second query

ProgressBarGraph

This graph is used to display process data out of the total data

See sample data file

progress-bar-graph

maxData (string) this column is used to display total value of each bar.

usedData (string) this column is used to display used value of each bar.

label (string) this column used to display the bar name.

barColor (string) color of the bar drawn by used data.

backgroundColor (string) color of the bar drawn by max data.

percentage (boolean) whether to show bar value in percentage or not. Deafult is false

defaultRange (numeric) If max data is not present then this value is used to calculate the percentage. default is 100.

colorRange (array) define the criteria to show the color of each bar on the basis of defined used value range. E.g -

"colorRange": [
            {
                "upto": 10,
                "color": "red"
            },
            {
                "upto": 70,
                "color": "blue"
            },
            {
                "upto": 100,
                "color": "green"
            }
        ]

LeafletGraph

Display a cluster markers on map to show data

toolTip (array) contain object of tooltips label and data to be displayed.

"tooltip": [
    {
        "column": "gatewayName",
        "label": "NSG"
    },
    {
        "column": "address",
        "label": "Address"
    },
    {
        "column": "bootstrapStatus",
        "label": "Bootstrap Status"
    },
    {
        "column": "NSGVersion",
        "label": "NSG Version"
    },
    {
        "column": "criticalAlarmsCount",
        "label": "Critical Alarms"
    },
    {
        "column": "majorAlarmsCount",
        "label": "Major Alarms"
    },
    {
        "column": "minorAlarmsCount",
        "label": "Minor Alarms"
    }
]

See sample configuration and data file

![leafletgraph](Screenshot from Gyazo)