# Welcome to the Spring Flo wiki! Spring Flo is a set of [Angular JS](https://angularjs.org/) directives for a diagram editor able to represent a DSL graphically and synchronize graphical and textual representation of that DSL. Graphical representation is done with a [Joint JS](http://jointjs.com/) graph object, textual representation can be either a plain HTML element (such as ` ``` The HTML above translates into a page with toolbar for buttons (Layout and Show/Hide Palette), text area for DSL and the Flo editor for graph representation of the DSL. ## All the extension points: ### Metamodel Service This service enables the domain in which Flo is being used to specify what kinds of element are being connected together in the graph and also how the graph should be converted to-and-from a textual representation. [Sample metamodel service is here](https://github.com/spring-projects/spring-flo/blob/master/samples/spring-flo-sample/src/main/resources/static/js/metamodel-service.js). #### textToGraph(flo, definition) Sets the graph contents for the `flo` object based on the textual representation of the DSL from `definition` object. Text is transformed into the corresponding Joint JS graph content. The graph is to be populated via `flo` objects functions such as `flo.createLink()` and `flo.createNode()` and cleared with `flo.clearGraph` #### graphToText(flo, definition) Convert the current graph available from the `flo` object into a textual representation which is then set (as the `text` property) on the `definition` object. #### load() Returns a promise that resolves to a `metamodel` object. The `metamodel` object layout is a map of element `group` names to a map of elements that belong to this `group`. The map of elements that belong to the `group` is a mapping between element's `name` and element's [Metadata Object](https://github.com/spring-projects/spring-flo/wiki#element-metadata) #### refresh() _(Optional)_ Refreshes the meta-model and returns a promise that is resolved to the same result as [load()](#load). Refresh should also fire event to `metamodel` change listeners. #### encodeTextToDSL(text) _(Optional)_ Encodes DSL element property value text to the DSL required format. Example is converting multiline text into a single line required by the DSL format. Used to display the property value in a human readable format. #### decodeTextFromDSL(dsl) _(Optional)_ Decodes DSL element property value text from DSL format. Example is converting single line text into a multiline text, i.e. replacing escaped line breaks. Used to set a property value for DSL element entered by the user via UI. #### subscribe(listener) _(Optional)_ Adds a listener to `metamodel` events. (See [Metamodel Listener](#metamodel-listener)) #### unsubscribe(listener) _(Optional)_ Removes `metamodel` events listener. (See [Metamodel Listener](#metamodel-listener)) #### isValidPropertyValue(element, key, value) _(Optional)_ Check if the the value being specified for the key on the specified element is allowed. For example: if the key takes an integer, don't allow alphabetic characters. ### Render Service The service is responsible for visual representation of graph elements based on the metadata (coming from [Metamodel Service](#metamodel-service)). This service is **optional**. [Sample render service is here](https://github.com/spring-projects/spring-flo/blob/master/samples/spring-flo-sample/src/main/resources/static/js/render-service.js). #### createNode(metadata, properties) _(Optional)_ Creates an instance of Joint JS graph node model object (`joint.dia.Element`). Parameters that may affect the kind of node model object are element's [metadata](#element-metadata) and map of properties (if any passed in). #### createLink(source, target, metadata, properties) _(Optional)_ Creates an instance of Joint JS graph link model object (`joint.dia.Link`). Parameters that may affect the kind of link model object are element's [metadata](#element-metadata), map of properties (if any passed in), source and target elements #### createHandle(kind, parent) _(Optional)_ Creates an instance of Joint JS graph node model object (`joint.dia.Element`). An example of a handle is a shape shown next to the parent shape interacting with which results in some editing action over the parent shape. Parameters that may affect the kind of handle model object are `kind` of type `string` (user defined, i.e. `delete`, `resize`, etc.) and handle's `parent` element. This function is only called by the framework if Editor Service `createHandles()` function is implemented. #### createDecoration(kind, parent) _(Optional)_ Creates an instance of Joint JS graph node model object (`joint.dia.Element`). An example of decoration is a validation marker displayed over the parent shape. Parameters that may affect the kind of decoration model object are `kind` of type `string` and decoration's `parent` element. Note that `kind` parameter is coming from the framework (unlike for `createHandle` function). This function is only called by the framework if Editor Service `validateNode()` function is implemented. (At the moment decorations are only the validation error markers). #### initializeNewNode(node, context) _(Optional)_ Performs any additional initialization of a newly created graph `node` when `node` is already added to the Joint JS graph and rendered on the canvas, e.g. element's SVG DOM structure is available. The `context` parameter is an object with `paper` and `graph` properties applicable for the `node`. Useful to perform any kind of initialization on a node when it's SVG DOM is appended to the page DOM. Examples: fit string label inside a shape, use angular directive on a shape, add DOM listeners etc. #### initializeNewLink(link, context) _(Optional)_ Performs any additional initialization of a newly created graph `link` when `link` is already added to the Joint JS graph and rendered on the canvas, e.g. element's SVG DOM structure is available. The `context` parameter is an object with `paper` and `graph` properties applicable for the `link`. Useful to perform any kind of initialization on a link when it's SVG DOM is appended to the page DOM. Examples: use angular directive on a shape, add DOM listeners etc. #### initializeNewHandle(handle, context) _(Optional)_ Performs any additional initialization of a newly created graph `handle` when `handle` is already added to the Joint JS graph and rendered on the canvas, e.g. element's SVG DOM structure is available. The `context` parameter is an object with `paper` and `graph` properties applicable for the `handle`. Useful to perform any kind of initialization on a handle shape when it's SVG DOM is appended to the page DOM. Examples: fit string label inside a shape, use angular directive on a shape, add DOM listeners etc. #### initializeNewDecoration(decoration, context) _(Optional)_ Performs any additional initialization of a newly created graph `decoration` when `decoration` is already added to the Joint JS graph and rendered on the canvas, e.g. element's SVG DOM structure is available. The `context` parameter is an object with `paper` and `graph` properties applicable for the `decoration`. Useful to perform any kind of initialization on a decoration shape when it's SVG DOM is appended to the page DOM. Examples: fit string label inside a shape, use angular directive on a shape, add DOM listeners etc. #### getNodeView() _(Optional)_ Returns instance of `joint.dia.ElementView`. It can also be a function of the form `function(element)` that takes an element model and should return an object responsible for rendering that model onto the screen. Under normal circumstances this function does not need to be implemented and the Joint JS view object created by the framework should be enough. Implement this function if different nodes require different Joint Js views or view has some special rendering (i.e. embedded HTML elements). See [Joint JS Paper Options](http://jointjs.com/api#joint.dia.Paper:options) #### getLinkView() _(Optional)_ Returns instance of Joint JS `joint.dia.LinkView`. Default is `joint.dia.LinkView`. It can also be a function of the form `function(link)` that takes a link model and should return an object responsible for rendering that model onto the screen. Under normal circumstances this function does not need to be implemented and the Joint JS view object created by the framework should be enough. Implement this function if different links require different Joint JS views or view has some special rendering (i.e. pattern applied to a line - `joint.shapes.flo.PatternLinkView`). See [Joint JS Paper Options](http://jointjs.com/api#joint.dia.Paper:options) #### layout(paper) _(Optional)_ Responsible for laying out the Joint JS graph that can be derived from passed in `paper` parameter (`paper.model`). #### handleLinkEvent(paper, event, link) _(Optional)_ Responsible for handling `event` that occurred on the `link` that belong to passed in Joint JS `paper` object. The `event` parameter is a `string` with possible values: `'add'`, `'remove'` or Joint JS native link change events such as `'change:source'`, `'change:target'`, etc. see [Joint JS Link Events](http://jointjs.com/api#joint.dia.Link:events) #### isSemanticProperty(propertyPath, element) _(Optional)_ Returns `true` for `string` property attribute path `propertyPath` on an `element` if graphs needs to perform some visual update based on `propertyPath` value change (Not needed for properties under `props` on an `element`). Visual update is performed by [refreshVisuals()](#refreshVisuals). The property path `propertyPath` is relative to Joint JS element `attrs` property #### refreshVisuals(element, propertyPath, paper) _(Optional)_ Performs some visual update of the graph or, which is more likely, the passed in `element` displayed on Joint JS `paper` based on the changed property specified by `propertyPath` #### getLinkAnchorPoint(linkView, view, port, reference) _(Optional)_ This function allows you to customize what are the anchor points of links. The function must return a point (with `x` and `y` properties) where the link anchors to the element. The function takes the link view, element view, the `port` (SVG element) the link should stick to and a reference point (either the closest vertex or the anchor point on the other side of the link). ### Editor Service The service responsible for providing Flo editor with rich editing capabilities such as handles around selected shapes, custom drag and drop behaviour, live and static validation. This service is **optional**. [Sample editor service is here](https://github.com/spring-projects/spring-flo/blob/master/samples/spring-flo-sample/src/main/resources/static/js/editor-service.js) #### createHandles(flo, createHandle, selected) _(Optional)_ Called when node is selected and handles can be displayed. Handles are usually small shapes around the `selected` Joint JS node in `flo` editor interactions with which modify properties on `selected` node, i.e. resize or delete handles. Call `createHandle(selected, kind, clickHandlerFunction, coordinate)` function to create a handle. The `kind` parameter is a `string` kind of a handle, `clickHandlerFunction` is performed when handle has been clicked on and `coordinate` is the place to put the handle shape. Note that if this function is implemented then Render Service `createHandle(...)` function must be implemented as well. The framework will remove handles automatically when needed, hence no need to worry about this on the client side. #### validatePort(paper, view, portView) _(Optional)_ Decide whether to create a link if the user clicks a port. The `portView` is the DOM element representing the port, `view` is the port's parent Joint JS view object show in Joint JS `paper` #### validateLink(flo, cellViewS, portS, cellViewT, portT, end, linkView) _(Optional)_ Decide whether to allow or disallow a connection between the source view/port (`cellViewS`/`portS`) and target view/port (`cellViewT`/`portT`). The `end` is either `'source'` or `'target'` and tells which end of the link is being dragged. This is useful for defining whether, for example, a link starting in a port POut of element A can lead to a port PIn of elmement B. #### calculateDragDescriptor(flo, draggedView, targetUnderMouse, coordinate, context) _(Optional)_ Called when dragging of a node `draggedView` is in progress over `targetUnderMouse` Joint JS graph element (node or link) at `coordinate`. There are also `flo` object parameter and `context` object, which currently just has a `boolean` property `palette` to denote whether drag and drop occurring on the palette or canvas. The function should return a [Drag Descriptor Object](#drag-descriptor). #### handleNodeDropping(flo, dragDescriptor) _(Optional)_ Performs necessary graph manipulations when the node being dragged is dropped. The `dragDescriptor` [Drag Descriptor](#drag-descriptor) should have the mandatory information on what is being dragged and where it's being dropped. The `flo` object parameter would help to make necessary graph modifications #### showDragFeedback(flo, dragDescriptor) _(Optional)_ Any custom visual feedback when dragging a node over some graph element (node or link) can be drawn by this function. `dragDescriptor` parameter has a [Drag Descriptor Object](#drag-descriptor) that has complete information about dragging in progress and `flo` object would help with drawing feedback using Joint JS #### hideDragFeedback(flo, dragDescriptor) _(Optional)_ Removes any custom visual feedback drawn by [showDragFeedback()](#show-drag-feedback). Has the same parameters. #### validateNode(flo, node) _(Optional)_ Returns a `javascript` array of `string` error messages that are the result of validating `node` Joint JS graph node on the canvas in `flo` editor #### preDelete(flo, deletedElement) _(Optional)_ Called prior to removal of the specified `deletedElement` allowing extra tidyup before that happens. For example: removes any dependent Joint JS graph elements related to the element about to be deleted. #### interactive _(Optional)_ If set to `false`, interaction with elements and links is disabled. If it is a function, it will be called with the cell view in action and the name of the method it is evaluated in (`'pointerdown'`, `'pointermove'`, ...). If the returned value of such a function is false interaction will be disabled for the action. For links, there are special properties of the interaction object that are useful to disable the default behaviour. These properties are: `vertexAdd`, `vertexMove`, `vertexRemove` and `arrowheadMove`. By setting any of these properties to false, you can disable the related default action on links. #### allowLinkVertexEdit _(Optional)_ If set to `false` link vertex (or bend point) creation or editing (e.g. movement) is not allowed in the editor. ## Data structure reference: ### Flo This object is created by the `flo-editor` directive controller and it contains various editor specific properties and functions. #### scheduleUpdateGraphRepresentation() Schedules an asynchronous update of the graph DSL representation based on the text DSL representation. #### updateGraphRepresentation() Asynchronously update the graph DSL representation based on the text DSL representation. A promise is returned which gets resolved when the update completes. #### updateTextRepresentation() Asynchronously update the text DSL representation (`definition` object) based on the graph DSL representation. A promise is returned which gets resolved when the update completes. #### performLayout() Arranges nodes and links of the graph on the canvas. #### clearGraph() Clears out canvas of all nodes and links. With syncing on this also causes the text DSL representation to clear. #### getGraph() Returns a reference to `joint.dia.Graph` object instance of the canvas contents (The graph model, see [Joint JS Graph API](http://jointjs.com/api#joint.dia.Graph) #### getPaper() Returns a reference to joint.dia.Paper object instance of the canvas (The graph view object, see [Joint JS Paper API](http://jointjs.com/api#joint.dia.Paper) #### enableSyncing(enable) Enables or disables textual and graph DSL representation synchronization mechanism based on the passed `boolean` parameter `enable`. Useful when textual DSL representation UI is collapsed. #### getSelection() Returns currently selected graph model element (node or link) on the canvas #### zoomPercent(percent) Angular getter/setter function for the zoom value on the canvas. Sets zoom percent value if the integer `number` parameter is supplied. Returns the integer percent value if parameter is missing (getter mode) #### gridSize(gridSize) Angular getter/setter function for the canvas grid size in pixels. Sets grid width value if the integer `number` parameter `gridSize` is supplied. Returns the current grid size value if parameter is missing (getter mode). Note that setting grid width to `1` turns the grid off. Invalid values for `gridSize` are ignored #### getMinZoom() Returns integer `number` minimum allowed value for the zoom percent. Useful to set the proper range for zoom controls. Needed by the zoom control on the canvas (if it is set to be shown). The value equals `5` by default (5%). #### getMaxZoom() Returns integer `number` maximum allowed value for the zoom percent. Useful to set the proper range for zoom controls. Needed by the zoom control on the canvas (if it is set to be shown). The value equals `400` by default (400%). #### getZoomStep() Returns integer `number` zoom percent increment/decrement step. Needed by the zoom control on the canvas (if it is set to be shown). The value equals `5` by default (5% increment/decrement value). #### fitToPage() Fits the whole graph into canvas's viewport (i.e. no need to scroll to look for content on the canvas). Adjusts the zoom level and scroll position appropriately #### readOnlyCanvas(newValue) Angular getter/setter function for the canvas "read-only" property. Read-only canvas does not allow for any user editing interaction of any shapes on the canvas. Sets the read-only property based on the passed in `newValue` parameter as the result the canvas toggles the behaviour for read-only state right away. Returns the current "read-only" state value if parameter is missing (getter mode). #### createNode(metadata, properties, location) Creates and returns the newly created Joint JS graph node (instance of `joint.dia.Element`) based on the graph node `metadata` object (see [Element Metadata](#element-metadata)), `properties` key-value pairs map, and location on the canvas (object with `x` and `y` properties). The new node is also added to the Flo canvas Joint JS `graph` and hence to the Joint JS `paper` and appears right away on the canvas before this function returns the result. #### createLink(source, target, metadata, properties); Creates and returns the newly created Joint JS graph link (instance of `joint.dia.Link`) between `source` and `target` nodes (of type `joint.dia.Element`) based on the graph link `metadata` object (see [Element Metadata](#element-metadata)), `properties` key-value pairs map. The new link is also added to the Flo canvas Joint JS `graph` and hence to the Joint JS `paper` and appears right away on the canvas before this function returns the result. ### Definition This object holds data related to DSL's textual representation. Typically this object should at least have `text` property of type `string` for the DSL text, but it can also have other properties that might be added by client's Metamodel Service graph-text conversion functions. ### Metamodel Listener Typically Metamodel object is loaded asynchronously via HTTP request. If metadata is cached by the service then it might be useful to register listeners. Flo editor palette would automatically rebuild itself if metamodel has changed ```javascript { metadataError: function(data) { /* Error loading metadata has occurred */ }, metadataRefresh: function() { /* Metadata is about to be refreshed */ }, metadataChanged: function(data) { /* New metadata is available */ } } ``` ### Drag Descriptor API client is free to add extra properties to this object (i.e. may help drawing visual feedback) ```javascript { context: context, /* String 'palette' or 'canvas' */ source: { cell: draggedNode, /* Joint JS graph node being dragged */ selector: selector, /* Optional. Joint JS CSS class selector for the subelement of the dragged node*/, port: portType /* Optional. Involved port DOM element type attribute value == port Joint JS markup 'type' property */ }, target: { cell: targetNode, /* Joint JS graph node target under mouse element */ selector: selector, /* Optional. Joint JS CSS class selector for the element under mouse within the targetNode */ port: portType /* Optional. Sub-element under mouse is a port. Port DOM element type attribute value == port Joint JS markup 'type' property */ }, }; ``` ### Joint JS Graph Node Markup ```javascript model: /* Joint JS model object for a module shape */ ... attributes: ... angle: 0, /* Joint JS property - rotation angle */ id: "02be8001-ea1e-4f30-a94e-9503da5964b5" /* Joint JS property - element model UUID position: /* Joint JS property - coordinates of the shape's bounding rectangle */ x: 119 y: 46 size: /* Joint JS property - size of the shape's bounding rectangle */ height: 40 width: 120 type: "sinspctr.IntNode" /* Flo property - internal, type (node, link, handle, decoration, etc) */ z: 1 /* Joint JS property - z-index of the shape ports: /* Joint JS property - internal, ports available on the shape */ input: id: "input" output: id: "output" tap: id: "tap" attrs: /* Joint JS property - user defined rendering constructs and semantic properties */ . /*\ */ .border /* \ */ .box /* \ */ .input-port /* \ */ .label1 /* \___User defined rendering constructs implied by the markup */ .label2 /* / */ .output-port /* / */ .shape /* / */ .stream-label /* / */ .tap-port /*/ */ metadata: /* Flo property. Node metadata supplied by Metamodel Service */ props: /* Flo property. Semantic properties of the element. Name <-> value pair map */ dir: "/Users/x/tmp" file: "temp.tmp" debug: true ... ... ... ``` ### Element Metadata Graphical element metadata supplied by Metamodel Service ```javascript metadata: { get: function(), /* function taking property key string as a parameter */ /* Returns promise that resolves to the metadata object of the property */ /* See snippet below showing the format of a property metadata */ group: "source", /* Category/Group of an element. Translates into palette groups of elements */ name: "file", /* Name or Type of an element (should be unique within its group) */ metadata: { /* Additional metadata for the element */ titleProperty: 'props/title', /* Property to be displayed at the top of all properties in properties Div */ noEditableProps: false, /* If true then element doesn't have properties to edit and properties Div is not shown */ allow-additional-properties: true, /* Allows user to create new properties for element in the properties Div */ } } ``` Element's property metadata is expected to be as follows ```javascript properties: { info: { defaultValue: null, description: "General information about the file", id: "info", name: "info", shortDescription: "File Info" }, language: { defaultValue: "English" description: "Language of the file contents", id: "language", name: "language", shortDescription: "Text Language" }, ... ```