# 04. __Documentation__

[![+Professional Support](https://www.totaljs.com/img/badge-support.svg)](https://www.totaljs.com/support/) [![+Chat with contributors](https://www.totaljs.com/img/badge-chat.svg)](https://messenger.totaljs.com)

## Flow

Coming soon.

## Flow component

Coming soon.

## Flow data

Each component received `FlowData` in the form below:

```javascript
component.on('data', function(flowdata) {

	// Properties:
	flowdata.id;               // {Number} A flowdata identificator
	flowdata.index;            // {Number} An input number
	flowdata.begin;            // {Date} when it started
	flowdata.data;             // {Anything} user defined data
	flowdata.completed;        // {Boolean} is sending completed?
	flowdata.tracking;         // {Array of Instances} all instances in order which they modified data
	flowdata.parent;           // {Component} a parent instance

	// Methods
	flowdata.clone();          // Clones the current data and keeps same repository
	flowdata.free();           // Removes data and repository (releases a memory)
	flowdata.rewrite(data);    // Rewrites the current with new
	flowdata.arg(string);      // Replaces the string with {key} keys by values from FlowData private repository according to the key

	// Private message repository
	flowdata.set(key, value);  // Sets a key-value to flowdata repository (doesn't modify data)
	flowdata.get(key);         // Gets a key-value (doesn't read data from "data")
	flowdata.rem(key);         // Removes a key-value (doesn't read data from "data")    
});

// Instance can also be created using bellow code.
// This is usefull when the component is first in the chain.
var flowdata = component.make({ hello: 'world' });
component.send(flowdata);

// Another way to get an instance of FlowData.
// But important: FlowData will be generated as a new instance
var flowdata = component.send('YOUR-DATA-TO-CHILD-CONNECTIONS');
```

__We recommend__ to send a previous data updated by new data:

```javascript
flowdata.rewrite('NEWDATA');
// or
flowdata.data = 'NEWDATA';

// Sending:
component.send(flowdata);
```

---

- `FLOW` is a global variable
- URL address `http://127.0.0.1:8000/$flow/` (default, can be changed in config)

__Properties__:

```javascript
FLOW.variables;
// Returns all custom variables defined by user key/value
// returns {Object}
// +v3.0.0
```

__Methods__:

```javascript
FLOW.emit('hello', 'arg1', 'arg..N');
// Emits event to all component instances

FLOW.send(message);
// Sends a message to designer via WebSocket

FLOW.debug(data, style, [group]);
// Sends a debug message
// message: {String/Object} - string will be formatted as markdown and object as JSON
// style: {String} - "info", "warning", "error" (default: "info")
// group: {String} (optional) - +v4.0.0

FLOW.set(key, value);
// Writes a value into the key-value store (data are stored on HDD)

FLOW.get(key);
// Reads a value from the key-value store (data are stored on HDD)

FLOW.rem(key);
// Removes value from the key-value store (data are stored on HDD)

FLOW.findByReference('STRING');
FLOW.findByReference(REGULAR_EXPRESSION);
// Finds all instances by reference
// returns {Array of Components}

FLOW.findByName('STRING');
FLOW.findByName(REGULAR_EXPRESSION);
// Finds all instances by name
// returns {Array of Components}

FLOW.findByComponent('STRING');
FLOW.findByComponent(REGULAR_EXPRESSION);
// Finds all instances by component name
// returns {Array of Components}

FLOW.findById('ID');
// Finds an instance by its ID
// returns {Component}

FLOW.hasComponent(name);
// Does have a flow registered component?
// returns {Boolean}

FLOW.hasInstance(id);
// Does have a flow registered instance?
// returns {Boolean}

FLOW.install(url, [callback]);
FLOW.install(filename, body, [callback]);
// Registers a new component and stores its content into the `/flow/` directory
// returns {FLOW}

FLOW.variable(key);
// Returns a value of variable by key
// return {Object}
// +v3.0.0

FLOW.reload([type], [callback]);
// +v6.0.0 Can reload a Flow
// @type {Number} 0 reloads only designer + variables (default), 1 reloads components + designer + variables
// @callback {Function} A callback
```

__Events__:

```javascript
ON('flow.register', function(declaration) {
	// New component has been registered
});

ON('flow.init', function(count) {
	// Init FLOW system
	// "count" a count of all new instances
});

ON('flow.reset', function(count) {
	// Components have been reset
});

ON('flow.open', function(instance) {
	// New instance of a component has been created
});

ON('flow.save', function(instance) {
	// Flow is saved/applied
	// +v1.2.0
});

ON('flow.close', function(instance) {
	// A component instance will be closed
});

ON('flow.options', function(instance) {
	// A component instance has changed options
	// +v3.0.0
});

ON('flow.variables', function(variables) {
	// Changed flow variables
	// +v3.0.0
});

ON('flow.reload', function(variables) {
	// Flow is reloaded (designer + variables)
	// +v6.0.0
});
```
# 04. - Component

Component is a single `js` file placed in `<app>/flow/` folder
- __IMPORTANT__: `exports.id` can only contain `a-z` `0-9` characters.

```javascript
// {String}, IMPORTANT (lower case without diacritics)
exports.id = 'component';

// {String}, optional (default: "component name")
exports.title = 'A component name (for human)';

// {String}, optional (default: "#656D78")
exports.color = '#656D78'; // use darker colors because the font color is "white"

// {Boolean}, optional (default: false)
exports.click = true;

// {Number}, optional (default: 0)
// +v3.0.0
exports.input = 0;

// or {Array of Colors}, input will have 2 inputs (red and blue)
// +v3.0.0
exports.input = ['red', 'blue'];

// {Number}, optional (default: 0)
exports.output = 1;

// or {Array of Colors}, output will have 2 outputs (red and blue)
exports.output = ['red', 'blue'];

// {String}, optional (default: "Common")
exports.group = 'Common';

// {String}, optional (default: "Unknown")
exports.author = 'Peter Širka';

// {String}, optional (default: "")
// Font-Awesome icon without "fa-"
exports.icon = 'home';

// {String or Object}, optional (default: undefined)
exports.status = 'DEFAULT STATUS TEXT';
// or
exports.status = { text: 'DEFAULT STATUS TEXT', color: 'red' };

// {String Array}
// optional (default: undefined), NPM dependencies
exports.npm = ['sqlagent', 'mqtt'];

// {Object}, optional (default "undefined")
// Default options for new and existing instances
exports.options = { enabled: true };

// Disables data cloning
exports.cloning = false;

// {Boolean}, optional (default: true)
// +v4.0.0
// hides stats under component box in designer UI
exports.traffic = false;

exports.install = function(component) {

	// =====================
	// DELEGATES
	// =====================

	// A close delegate (optional)
	// - "callback" argument must be executed!
	component.close = function(callback) {
		// This instance will be killed.
		// use this if some asyncronous work needs to be done
		// alternatively use component.on('close',...
	};


	// =====================
	// EVENTS
	// =====================

	component.on('click', function() {
		// optional
		// the component was clicked on in the designer
		// usefull for enabling/disabling some behavior or triggering some actions
	});

	component.on('data', function(flowdata) {

		// RAW DATA
		// returns {Object}
		flowdata.data;

		// Write value to data repository
		// returns {Message}
		flowdata.set('key', 'value');

		// Read value from data repository
		// returns {Object}
		flowdata.get('key');

		// Remove value from data repository
		// returns {Message}
		flowdata.rem('key');

		// {Object Array} Array of all components the message has passed through (previous components)
		flowdata.tracking;

		// {Object} Parent component (first component which started the flow)
		flowdata.parent;

		// {Boolean} Is completed?
		flowdata.completed;

		// {DateTime}
		flowdata.begin;

		// How can I modify data?
		flowdata.data = { newdata: true };

		// send the flowdata to connected components, if any :-)
		component.send(flowdata);
	});

	component.on('<input-number>', function(flowdata) {
		// data from specific input go also to the corresponding event -> input 0 to event '0'
		// data from all inputs go to 'data' event
		// data from specific input go also to the corresponding event -> input 0 to event '0'
	});


	component.on('options', function(new_options, old_options) {
		// optional
		// options have changed in the designer
		// instance.options holds the new_options already
	});

	component.on('variables', function(variables) {
		// +v3.0.0
		// optional
		// global variables have been changed
		// instance.variable(key)
	});

	component.on('close', function() {
		// optional
		// This instance will be killed
	});

	component.on('reinit', function() {
		// optional
		// Designer has been updated, but this instance still persists
		// This instance can have new connections.
	});

	component.on('signal', function(data, parent) {
		// optional
		// Captured signal
		// @data {Object} - optional, can be "null", or "undefined"
		// @parent {Component} - a component which created this signal
	});

	component.on('service', function(counter) {
		// optional
		// Service called each 1 minute
	});    

	// =====================
	// METHODS
	// =====================
	
	component.status(message, [color]);
	// Sends a status to designer
	// @message: {String/Object} - string will be formatted as markdown and object as JSON
	// color: {String} - "black" (default: "gray")

	component.debug(message, [style]);
	// Sends a debug message
	// @message: {String/Object} - string will be formatted as markdown and object as JSON
	// style: {String} - "info", "warning", "error" (default: "info")

	component.hasConnection(index);
	// Calculates connections
	// @index: {Number}
	// returns {Number}

	var message = component.send([index], data);
	message.set('repositorykey', 'value');
	console.log(message.get('repositorykey'));
	// Sends data
	// @index: {Number} - optional, the output index (otherwise all outputs)
	// @data: {String/Object}
	// returns Message;
  
	var message = component.send2([index], data);
	if (message) {
		// message will be sent
	} else {
		// no connections
	}
	// +v3.0.0
	// Alias for component.send() but with a check of connections

	component.set(key, value);
	// Writes a value to a private key-value store (data are stored on HDD)
	// @key {String}
	// @value {Object}
	// returns {Component}

	component.get(key);
	// Reads a value from a private key-value store (data are stored on HDD)
	// @key {String}
	// returns {Object}

	component.make(data);
	// Creates a new FlowData/Message instance.
	// @data {Object}
	// returns {Message}

	component.rem(key);
	// Removes a value from a private key-value store (data are stored on HDD)
	// @key {String}
	// returns {Component}

	component.variable(key);
	// +v3.0.0
	// Reads a value from global variables
	// @key {String}
	// returns {Object}

	component.signal([index], [data]);
	// Sends a signal to first connection (it emits "signal" event in target connection)
	// @index {Number} - optional, an output index (default: "undefined" --> all connections)
	// @data {Object} - optional, an additional data
	// returns {Component}

	component.click();
	// Performs click event.
	// returns {Component}
	
	component.log([a], [b], [c], [d]);
	// Writes some info into the log file
	// returns {Component}

	component.error(err, [parent|response|component]);
	// Creates error
	// returns {Component}

	component.save();
	// Saves current options, useful when options are changed internally. Options from settings form are saved automatically
	// returns {Component}

	component.reconfig();
	// If the component options changes on the server (not by recieving new options from designer) then use this to update options in designer

	// =====================
	// PROPERTIES
	// =====================

	component.custom;
	// {Object} - empty object for custom variables and methods

	component.name;
	// {String} - readonly, a component name (USER-DEFINED)

	component.reference;
	// {String} - readonly, a component reference (USER-DEFINED)

	component.options;
	// {Object} - readonly, custom settings

	component.state;
	// {Object} - readonly, latest state

	component.connections;
	// {Object} - readonly, all connections
};

exports.uninstall = function() {
	// OPTIONAL
};
```