User Tools

Site Tools


projects:d3-force-apex-plugin

Table of Contents

D3 Force Network Chart (Oracle APEX Region Type Plugin)

This is a D3 force JavaScript standalone implementation, playground and Oracle APEX plugin, which uses the D3 visualization library to render a network layout. It can run in any HTML page like in this wiki. For Oracle APEX you can use the plugin implementation for easier integration.

Example Graph

When you play around with the customization wizard please do not set the option useDomParentWidth to true - the graph will moving down under the table of contents, because the toc is a floating element in this wiki. If you want to have a responsive graph under special conditions like me in this wiki you can implement your own window resize handler and call then the graph API to resize the graph: example.width(yourCalculatedGraphWidth).resume(). If you are interested in my solution here in the wiki simply look in the pagesource by clicking the magnifier symbol on the right side of the page (tools dropdown on small devices).

Features

  • Works with APEX versions 4.2.x and 5.x or standalone in every HTML page
  • Interactive customization wizard
  • Source data can be a XML string, JSON string or JavaScript Object (JSON)
  • Link directions are visible and self references are rendered in a nice way - have a look in the online demos
  • Node sizes are calculated between given min and max values depending on the SIZEVALUE attribute in your source data
  • Node colors are assigned depending on the given COLORVALUE attribute in your source data - if you provide a IMAGE attribute for a node, then the image is used instead of a fill color
  • Optional tooltips depending on the given INFOSTRING attribute in your source data
  • If you have a node attribute called LINK, you can define on which event the URL should be called - default is dblclick - try it out in the online demos by double clicking the node KING
  • Nodes can be pinned and the current positions can be saved and loaded to predefine a layout - optionally you can align the nodes to a grid when they are dragged around
  • With the lasso mode you can select nodes and implement a graphical multi select
  • The graph can be zoomed between the two configured min and max scale factors and is callable with the API
  • There is a JavaScript API to interact with the graph, also including eight events (node click, node double click, node contextmenu, node mouse enter, node mouse leave, link click, lasso start, lasso end)
  • All eight events are available in APEX - the plugin region can be AJAX refreshed and triggers then also apexbeforerefresh and apexafterrefresh
  • NEW in 2.0.0: Automatic label placement after force end to prevent overlapping (optional, per default switched off), links can now have a COLOR and a INFOSTRING attribute - see also the changelog

Requirements

  • APEX 4.2 or higher, if used as a plugin
  • A modern browser, who is able to work with SVG and CSS3 - for more informations see the D3 Wiki

Credits

I would like to say THANK YOU to all the people who share their knowledge. Without this sharing I would not have been able to create this D3 implementation. Special thanks to Mike Bostock for his great library and to Carsten Czarski for mentoring me on Oracle APEX plugin development.

Getting started

The shortest possible way to get up and running a graph with the shipped sample data:

<!DOCTYPE html>
<html>
 
<head>
    <meta charset="utf-8">
    <title>D3 Force APEX Plugin</title>
    <link href="css/d3-force-2.0.1.css" rel="stylesheet" type="text/css">
    <script src="lib/d3/d3-3.5.6.min.js"></script>
    <script src="js/d3-force-2.0.1.min.js"></script>
</head>
 
<body
    onload="example = netGobrechtsD3Force().start();">
</body>
 
</html>

When using the APEX plugin, your variable to access the graph is automatically set by the plugin. You can open the JavaScript console in your browser and look for “d3_force_YOUR_REGION_STATIC_ID”. You can then interact with the graph - in the example we inspect the graphs current variables:

example.inspect();                        // in the example above
d3_force_YOUR_REGION_STATIC_ID.inspect(); // when using the APEX plugin
 
// or you can change the width of your graph:
d3_force_YOUR_REGION_STATIC_ID.width(700).resume();
 
// there are more then sixty methods...
// please have a look in the API methods overview

Configure the graph

There are two ways to configure the graph:

  1. Provide a configuration object to the graph function on initialisation
  2. Use API methods before starting the graph or also on runtime

Configuration object

var example = netGobrechtsD3Force('domContainerToAppendTheGraph',
    {"width":"700",
     "height":"500"})
    .start(dataObjectOrStringInJsonOrXmlFormat);

API methods

var example = netGobrechtsD3Force('domContainerToAppendTheGraph')
    .width(700)
    .height(500)
    .start(dataObjectOrStringInJsonOrXmlFormat);

Of course you can combine the two ways:

var example = netGobrechtsD3Force('domContainerToAppendTheGraph',
                                  {"width":"700","height":"500"})
  .onNodeClickFunction( function(event,data){ console.log(event,data,this); } )
  .debug(true)
  .start(dataObjectOrStringInJsonOrXmlFormat);

If the DOM container is not existing, then the container is created under the body element.

Customization wizard

You can use the customization wizard to get your configuration object. This wizard has a predefined set of values, e.g. you can choose values between 300 and 1200 for the width. If this does not meet your requirements, you can of course change these values in your configuration object. If you do so and use the customization wizard later on, your additional values are appended to the select lists of the wizard.

There are two ways to start the wizard:

  1. Click the link “Customize Me” in the graph - this link is shown, when the graph is in debug mode (when you are using the APEX plugin with a Application Builder session, then the debug mode is automatically set to true)
  2. Directly start the wizard by setting the customize option to true:
    example.customize(true).start(dataObjectOrStringInJsonOrXmlFormat);

Each configuration option in the wizard is also implemented as a get and set method.

example.width();             //get the current graph width
example.width(700).resume(); //set the current graph width and resume the graph

ATTENTION: Some options/methods are instantly working, some needs a resume of the graph force, some needs a complete render cycle with the start or render method. Please have also a look in the method descriptions for start, render, resume.

Node Attributes

Node attributes are case sensitive:

  • ID: mandatory, string, alphanumeric node identifier
  • SIZEVALUE: mandatory, number, numeric base for radius calculation
  • COLORVALUE: mandatory, string, HTML color code or any alphanumeric string - see also colorScheme
  • COLORLABEL: optional, string, used for legend - if not existing, colorvalue is used instead
  • LABEL: optional, string, label for the node
  • LABELCIRCULAR: optional, boolean, overwrites the global option labelsCircular
  • INFOSTRING: optional, string, this string is shown as a tooltip - see also showTooltips and tooltipPosition
  • LINK: optional, string, URL to open on configurable event - see also nodeEventToOpenLink
  • IMAGE: optional, string, URL to a background image for a node instead of a fill color
  • fixed: optional, boolean, pin status of a node
  • x: optional, number, x position of fixed (pinned) node
  • y: optional, number, y position of fixed (pinned) node

Fixed, x and y are native D3 attributes - they must be lowercase

Link attributes are case sensitive:

  • FROMID: mandatory, string, id of node, where a link starts (links are able to showing directions, see also showLinkDirection)
  • TOID: mandatory, string, id of node, where a link ends
  • STYLE: optional, string, can be solid (default), dotted or dashed
  • COLOR: optional, string, must be a HTML color code like green or #00ff00
  • INFOSTRING: optional, string, this string is shown as a tooltip - see also showTooltips and tooltipPosition

Included Sample Data

The following data (XML notation, not all records shown) is included in the source code. You can deliver your data in three formats: XML string, JSON string or JavaScript object (JSON).

XML Notation Example

<data>
  <nodes
    ID="7839"
    LABEL="KING is THE KING, you know?"
    COLORVALUE="10"
    COLORLABEL="Accounting"
    SIZEVALUE="5000"
    LABELCIRCULAR="true"
    LINK="http://apex.oracle.com/"
    INFOSTRING="This visualization is based on the well known emp table."
  />
  <nodes
    ID="7698"
    LABEL="BLAKE"
    COLORVALUE="30"
    COLORLABEL="Sales"
    SIZEVALUE="2850"
  />
  <links
    FROMID="7839"
    TOID="7839"
    STYLE="dotted"
    COLOR="blue"
    INFOSTRING="This is a self link (same source and target node) rendered along a path with the STYLE attribute set to dotted and COLOR attribute set to blue."
  />
  <links
    FROMID="7698"
    TOID="7839"
    STYLE="dashed"
  />
</data>

JSON Notation Example

{
  "data":{
    "nodes":[
      {
        "ID":"7839",
        "LABEL":"KING is THE KING, you know?",
        "COLORVALUE":"10",
        "COLORLABEL":"Accounting",
        "SIZEVALUE":5000,
        "LABELCIRCULAR":true,
        "LINK":"http://apex.oracle.com/",
        "INFOSTRING":"This visualization is based on the well known emp table."
      },
      {
        "ID":"7698",
        "LABEL":"BLAKE",
        "COLORVALUE":"30",
        "COLORLABEL":"Sales",
        "SIZEVALUE":2850
      }
    ],
    "links":[
      {
        "FROMID":"7839",
        "TOID":"7839",
        "STYLE":"dotted",
        "COLOR":"blue",
        "INFOSTRING":"This is a self link (same source and target node) rendered along a path with the STYLE attribute set to dotted and COLOR attribute set to blue."
      },
      {
        "FROMID":"7698",
        "TOID":"7839",
        "STYLE":"dashed"
      }
    ]
  }
}

Example Queries

Please note, that you have to deliver distinct data to ensure your graph is rendered correctly - see also this issue on GitHub. It is always a good idea to review your queries/data before you try to use it in a visualization ;-)

Oracle - table emp

This query is the base for the shipped sample data:

WITH
nodes AS ( --> START YOUR NODES QUERY HERE
  SELECT XMLELEMENT( "nodes", xmlattributes(
         empno        AS id
       , ename        AS label
       , sal          AS sizevalue
       , d.deptno     AS colorvalue
     --, d.dname      AS colorlabel    -- optional, used for the graph legend
     --, 'http://...' AS link          -- optional
     --, 'some text'  AS infostring    -- optional, rendered as tooltip
     --, 'false'      AS labelcircular -- optional, overwrites labelsCircular
     --, 'http://...' AS image         -- optional, background image for a node
     --, 'true'       AS "fixed"       -- optional | fixed, x and y are native
     --, 100          AS "x"           -- optional | D3 attributes, they must be
     --, 100          AS "y"           -- optional | lowercase
         ) ) AS xml_nodes
    FROM emp e JOIN dept d ON e.deptno = d.deptno --< STOP YOUR NODES QUERY HERE
),
links AS ( --> START YOUR LINKS QUERY HERE
  SELECT XMLELEMENT( "links", xmlattributes(
         empno          AS fromid
       , NVL(mgr,empno) AS toid
     --, 'dashed'       AS style       -- optional, solid, dotted or dashed
     --, 'red'          AS color       -- optional, must be a HTML color code
     --, 'some text'    AS infostring  -- optional, rendered as tooltip
         ) ) AS xml_links
    FROM emp --< STOP YOUR LINKS QUERY HERE
)
SELECT XMLSERIALIZE( DOCUMENT( XMLELEMENT( "data",
       ( SELECT XMLAGG( xml_nodes ) FROM nodes ),
       ( SELECT XMLAGG( xml_links ) FROM links ) ) ) INDENT ) AS single_clob_result
  FROM DUAL

Oracle - data model of current schema

With this query you can visualize the data model of the current schema. Tested on Oracle 11g. Some subqueries select more data then needed for the visualization - this could be a starting point for your own custom visualization.

ATTENTION: I tried to speed up the dictionary queries by using the with clause and filtering each subquery for the desired tables. Also, I tried to get the correct NOT NULL informations from the dictionary, since the nullable column in user_tab_columns does not reflect table level not null constraints. If you play around with the query to find an own solution you can run in massive performance problems when you join directly the dictionary views.

Many thanks to Adrian Billington for his excellent article about working with long columns.

--> Working with long columns: http://www.oracle-developer.net/display.php?id=430
--> The following helper function is needed to read the search condition:
CREATE OR REPLACE FUNCTION get_long_search_condition( p_constraint_name IN VARCHAR2 )
   RETURN VARCHAR2
AS
   v_return LONG;
   CURSOR c_search_condition
   IS
        SELECT search_condition
          FROM user_constraints
         WHERE constraint_name = p_constraint_name;
BEGIN
   OPEN c_search_condition;
   FETCH c_search_condition INTO v_return;
   CLOSE c_search_condition;
   RETURN SUBSTR( v_return, 1, 4000 );
END;
WITH
tabs AS ( --> filter your tables here, all other queries use tabs as filter
  SELECT TABLE_NAME
       , NVL( num_rows, 0 ) AS num_rows
       , CASE --> used for coloring the nodes in the graph
            WHEN TABLE_NAME LIKE 'APEX$%' THEN 'APEX websheet apps'
            WHEN TABLE_NAME LIKE 'DEMO%' THEN 'Sample database app'
            WHEN TABLE_NAME IN ('EMP', 'DEPT') THEN
               'The well known emp & dept tables'
            ELSE 'uncategorized tables'
         END
            AS table_group
    FROM user_tables
   WHERE TABLE_NAME NOT LIKE 'PLSQL%'
),
cons AS (
  SELECT owner
       , constraint_name
       , constraint_type
       , TABLE_NAME
       , get_long_search_condition( constraint_name ) AS search_condition
       , r_owner
       , r_constraint_name
       , delete_rule
       , STATUS
    FROM user_constraints
   WHERE TABLE_NAME IN (SELECT TABLE_NAME FROM tabs)
),
concols AS (
  SELECT owner
       , constraint_name
       , TABLE_NAME
       , column_name
       , POSITION
    FROM user_cons_columns
   WHERE TABLE_NAME IN (SELECT TABLE_NAME FROM tabs)
),
connotnulls AS ( --> because of primary keys and not null check constraints we
                 --> have to select distinct here
  SELECT   DISTINCT TABLE_NAME, column_name, NULLABLE
     FROM (  SELECT c.table_name
                  , cc.column_name
                  , c.constraint_name
                  , c.constraint_type
                  , c.search_condition
                  , 'N' AS NULLABLE
               FROM cons c
               JOIN concols cc ON c.constraint_name = cc.constraint_name
              WHERE     c.status = 'ENABLED'
                    AND (   c.constraint_type = 'P'
                         OR     c.constraint_type = 'C'
                            AND REGEXP_COUNT( TRIM( c.search_condition )
                                            ,    '^"{0,1}'
                                              || cc.column_name
                                              || '"{0,1}\s+is\s+not\s+null$'
                                            , 1
                                            , 'i' ) = 1 ))
),
tabcols AS (
  SELECT t.table_name
       , t.column_name
       , t.data_type
       , t.data_length
       , t.data_precision
       , t.data_scale
       , t.nullable AS nullable_dict
       --> dict does not recognize table level not null constraints:
       --> working with long columns:
       --> http://www.oracle-developer.net/display.php?id=430
       , NVL( n.nullable, 'Y' ) AS nullable_cons
    FROM user_tab_columns t
         LEFT JOIN connotnulls n
            ON     t.table_name = n.table_name
               AND t.column_name = n.column_name
   WHERE t.table_name IN (SELECT TABLE_NAME FROM tabs)
),
fks AS (
   SELECT concols.table_name AS source_table
       , r_concols.table_name AS target_table
       , cons.status
       , tabcols.nullable_cons
    FROM cons
         JOIN concols
            ON cons.constraint_name = concols.constraint_name
          JOIN concols r_concols
            ON cons.r_constraint_name = r_concols.constraint_name
         JOIN tabcols
            ON     concols.table_name = tabcols.table_name
               AND concols.column_name = tabcols.column_name
   WHERE     cons.constraint_type = 'R'
         AND cons.status = 'ENABLED'
),
nodes AS ( --> START YOUR NODES QUERY HERE
  SELECT   XMLELEMENT( "nodes"
                     , xmlattributes( TABLE_NAME AS id
                                    , TABLE_NAME AS label
                                    , table_group AS colorvalue
                                    , num_rows AS sizevalue ) )
              AS xml_nodes
    FROM tabs --< STOP YOUR NODES QUERY HERE
),
links AS ( --> START YOUR LINKS QUERY HERE
  SELECT   XMLELEMENT( "links"
                     , xmlattributes( source_table AS fromid
                                    , target_table AS toid
                                    , CASE nullable_cons
                                         WHEN 'Y' THEN 'dashed'
                                         WHEN 'N' THEN 'solid'
                                      END AS STYLE )
                     , STATUS )
              AS xml_links
    FROM fks --< STOP YOUR LINKS QUERY HERE
)
SELECT XMLSERIALIZE( DOCUMENT( XMLELEMENT( "data",
       ( SELECT XMLAGG( xml_nodes ) FROM nodes ),
       ( SELECT XMLAGG( xml_links ) FROM links ) ) ) INDENT ) AS single_clob_result
  FROM DUAL

API Methods

Command And Helper Methods

start

This method starts the graph. You can configure your graph with all the available methods, but without the start method your changes will NOT take effect. You can pass new data (XML Notation Example, JSON Notation Example) to the start method. Data can be a XML string, JSON string or JavaScript object (JSON). If you use the APEX plugin, then the start method internally does the AJAX call to your Oracle database, but you can prevent this behavior by passing a data to this method. This also means, that you can use data from a textarea or a report for the APEX plugin, to overwrite the existing data and you do not need to configure any query to run this plugin. If you do so and you do not pass data to the start method on the very first call, then the plugin provides sample data - it is the same data with the APEX online demo of this plugin, there is no query configured and you get therefor the sampledata :-)

render

The render method does the same as the start method - the only difference is, that the render method does not try to load data, if you use the APEX plugin. You can use this method after changing options which need a render cycle to take the changes into effect:

example.minNodeRadius(4).maxNodeRadius(20).render();

resume

The resume method restarts only the force on your graph without a render cycle. This saves CPU time and can be useful if you change only things in your graph who do not need rendering to taking into effect:

example.releaseFixedNodes().resume();

zoom

This method expects three parameters: centerX, centerY, viewportWidth. If zoomMode is set to true, then the graph is centered to the given position and scaled to the calculated scale factor (effective graph with / viewportWidth). The reason to have a viewportWidth instead of a scale factor is, that you can rely on given data like the coordinates and radius of a node without calculating the scale factor by yourself - you define your desired viewport width and the zoom method is calculating the neccesary scale factor for this viewport width. If the calculated scale factor is less then or greater then the configured minimum and maximum scale factors, then these configured scale factors are used. The reason for this a good user experience, since the graph would be otherwise falling back on these scale factors when the user is scaling the graph by mouse or touch events. Example

var node = example.nodeDataById('9999');
example.zoom(node.x, node.y, node.radius * 6);

zoomSmooth

This method does the same as the zoom method - the difference is, that the zoom is animated in a nice way and there is a optional fourth parameter for the duration of the transition, which defaults to 1500ms. Example:

var node = example.nodeDataById('8888');
example.zoomSmooth(node.x, node.y, node.radius * 6); // default duration of 1500ms
 
var node = example.nodeDataById('9999');
example.zoomSmooth(node.x, node.y, node.radius * 6, 3000); // duration of 3000ms

transform

Behaves like a normal getter/setter (the zoom and zoomSmooth methods implements only setters) and can be used in the conf object to initialize the graph with different translate/scale factors than [0,0]/1 - works only, if the zoomMode is set to true - the current transform value(an object) is rendered in the customization wizard conf object text area like all other options when the current value is different then the default value {“translate”:[0,0],“scale”:1}.

example.transform({"translate":[100,100],"scale":0.5});
 
var currentTransform = example.transform();

moveFixedNodes(x,y)

Command method (has no get or set function). Moves all fixed nodes in the provided direction:

example.moveFixedNodes(10,-5).resume();

adds 10 to x position and -5 to y position on all fixed nodes. ATTENTION: If alignFixedNodesToGrid is set to true this can have unexpected behavior - you must then provide values greater then gridSize halved to see any changes on your graph, otherwise the positions are falling back to the nearest (current) grid position.

releaseFixedNodes

Command method (has no get or set function and expects no parameter): Release all fixed (pinned) nodes:

example.releaseFixedNodes().resume();

positions

Numeric. Gets or sets the current positions of all nodes. This lets you save and load a specific layout or modify the current positions. Works nice together with the pinMode:

// get current positions: Array of objects like [{"ID":"7839","x":200,"y":100,"fixed":1},...])
var pos = example.positions();
// set positions
example.positions(pos.map(function(p){ p.x += 10; return p; })).resume();
 
// all in one ;-)
example.positions( example.positions().map(function(p){ p.x += 10; return p; }) ).resume();

sampleData

Gets or sets the sample data. This makes only sense before the first start, because only on the first start without data available the sample data is used. After the first start you can provide new data with the start method. Example:

//first start
example.sampleData('<node>...').start();
 
//later
example.start('<node>...');

data

Returns the current graph data as JSON object. This method expects no parameter and terminates the method chain. Example:

//JSON object
example.data();
 
//stringified JSON object
JSON.stringify(example.data());

nodeDataById

String. Returns the data from a specific node as JSON object. This method expects a node ID as parameter and terminates the method chain. Example:

//get the data from the node with the ID 8888
example.nodeDataById('8888');
 
//get the data from the node with the ID 'myAlphanumericID'
example.nodeDataById('myAlphanumericID');

showLoadingIndicator

Command method, boolean, shows or hides the loading indicator:

// Show:
example.showLoadingIndicator(true);
 
// Hide:
example.showLoadingIndicator(false);

domParentWidth

Returns the current with of the graphs DOM parent. This method expects no parameter and terminates the method chain.

If the option useDomParentWidth is set to true, then this is the effective width of the graph - independent of the configured width.

options

Get and set the whole configuration object with one call. Ouput includes all options, who are accessible via the API methods including the registered event functions.

optionsCustomizationWizard

Get and set the whole configuration object with one call. Output includes only the options, who are accessible via the customization wizard.

customize

Boolean. Gets or sets the customize status. If enabled, the customizing wizard is opened, otherwise closed.

var example = netGobrechtsD3Force();
example.customize(true).start();

debug

Boolean. Gets or sets the debug mode. When debug is enabled, there is a link rendered in the SVG to start the customize wizard and debug messages are written to the console.

inspect

Shows the current closure object, which holds all functions, variables and data. This method expects no parameter and terminates the method chain.

version

Shows the current plugin version. This method expects no parameter and terminates the method chain.

Node Render Options

minNodeRadius

Numeric. The minimum node radius. Each node radius is calculated by its SIZEVALUE attribute in a range between the minimum and the maximum node radius.

maxNodeRadius

Numeric. The maximum node radius. Each node radius is calculated by its SIZEVALUE attribute in a range between the minimum and the maximum node radius.

colorScheme

Can be “color20”, “color20b”, “color20c”, “color10” or “direct”.

The first four use the color functions provided by D3, which return up to 20 colors for the given keywords for your data attribute COLORVALUE - this can be a text like a department name or a postal zip code. With the last option you can provide direct css color values in your data like blue or #123456.

dragMode

Boolean. If true, the nodes are draggable.

pinMode

Boolean. If true, the nodes are fixed (pinned) at the end of a drag event.

nodeEventToStopPinMode

String. Can be “none”, “click”, “dblclick” and “contextmenu” and defines, which event will release a node. This releasing of a node is sometimes a bit unstable (not on the code side, but on the visualizing side) and depends on the next tick event. You have to play around with this. If you want only all nodes to be released you can simply set the pin mode to false and start the graph.

onNodeContextmenuPreventDefault

Boolean. If true, the context menu default browser action on the nodes are prevented. This could be useful, if you want to implement an own context menu for the nodes.

String. Can be “none”, “click”, “dblclick” or “contextmenu”. Works only for nodes, who have a filled LINK attribute.

nodeLinkTarget

String. This text is used as the link target, when a node has a LINK attribute.

There are three special keywords: “none”, “nodeID” and “domContainerID”. If you use “none”, the link is opened in the same window/tab where your graph is currently shown. If you use “nodeID”, the ID of the currently clicked node is used as the target attribute, this means - you get one window/tab for each node in your graph - when you click a second time on the same node, the window/tab is reused. The same with the keyword “domContainerID” - you get one window/tab for each graph on your page - when you click a second time on the same node, the window/tab is reused.

Anything else is not interpreted - your given text is simply used as the target attribute of the link. This is also the case for the second option in the customize wizard called “_blank”. If you use this, then each click on a node opens in a new window/tab. You are not restricted to use only the predefined select options. It is up to you to overwrite the value in your configuration object. As an example: If you want to have always the same window/tab for each click on a node, then simply provide a text here, that is fit your needs e.g. “myOwnWindowName”.

showLabels

Boolean. If true, labels are rendered on top of the nodes.

labelsCircular

Boolean. If true, then the labels are rendered along a path around the nodes.

You can overwrite this attribute on node level by setting a attribute called LABELCIRCULAR on the node to true or false. As an example you can see this in the online demo on the node named KING.

ATTENTION: If you set the LABELCIRCULAR attribute on a specific or all nodes, then the global configuration parameter labelsCircular has no effect on these nodes and is also not changeable in the customize wizard.

labelDistance

Numeric. The distance of a label from the nodes outer border.

preventLabelOverlappingOnForceEnd

Boolean. If set to true the labels are aligned with a simulated annealing function to prevent overlapping when the graph is cooled down (correctly on the force end event and only on labels, who are not circular).

labelPlacementIterations

Numeric. The number of iterations for the preventLabelOverlappingOnForceEnd function - default is 250 - as higher the number, as higher the quality of the result. For details refer to the description of the simulated annealing function of the author Evan Wang under https://github.com/tinker10/D3-Labeler.

showTooltips

Boolean. If true and you provided in your source data an attribute INFOSTRING, then a tooltip is shown by hovering a node.

tooltipPosition

Can be “node”, “svgTopLeft” or “svgTopRight”.

alignFixedNodesToGrid

Boolean. If true, fixed nodes are aligned to the nearest grid position on the drag end event. You can pin nodes, when pinMode is set to true or by delivering nodes with the attribute “fixed” set to true and a “x” and “y” attribute with the position. If you have already fixed nodes on your graph you can also set this attribute at runtime and resume the force:

example.alignFixedNodesToGrid(true).resume()

gridSize

Number. The grid size of the virtual grid for the option alignFixedNodesToGrid:

example.gridSize(50).alignFixedNodesToGrid(true).resume()

linkDistance

Numeric. The distance between nodes centers.

showLinkDirection

Boolean. If true, you get an marker at the end of a link.

Boolean. If true, then links with the same source and target are rendered along a path around the node bottom.

selfLinkDistance

Numeric. The distance of the self link path around a node.

Graph Render Options

useDomParentWidth

Boolean. If true, the width of the chart(SVG element) is aligned to its DOM parent element.

width

Numeric. The width of the chart.

height

Numeric. The height of the chart.

setDomParentPaddingToZero

Boolean. If true, the parent DOM element of the graph gets the style { padding: 0px; }. If set to false, this style is removed from the DOM parent of the graph.

showBorder

Boolean. If true, a class named border is added to the SVG element, if false the class will be removed.

It is up to you to change the css for this class. The default is “1px solid silver” with a border radius of 5px.

showLegend

Boolean. If true, a legend for all COLORVALUEs in the node data is rendered in the bottom left corner of the graph.

showLoadingIndicatorOnAjaxCall

Boolean. If true, a loading indicator is shown when used as a APEX plugin during the AJAX calls. If you want to show the loading indicator in a standalone implementation you can show and hide the loading indicator directly with the API method [showLoadingIndicator] (#showloadingindicator):

// Show:
example.showLoadingIndicator(true);
 
// Hide:
example.showLoadingIndicator(false);

lassoMode

Boolean. If true, you can select nodes with a lasso.

See also onLassoStartFunction and onLassoEndFunction

zoomMode

Boolean. If true, you can zoom and pan the graph.

ATTENTION: When zoomMode is set to true then the lassoMode is only working with the pressed alt or shift key .

KNOWN BUG: In iOS it is after the first zoom event no more possible, to drag a node - instead the whole graph is moved - this is, because iOS Safari provide a wrong event.target.tagName. Also a problem: your are not able to press the alt or shift key - if you want to use lasso and zoom together on a touch device, you have to provide a workaround. One possible way is to provide a button, which turns zoom mode on and off with the API zoomMode method - then the user has the choice between these two modes - not comfortable, but working.

minZoomFactor

Number. The minimum possible zoom factor.

maxZoomFactor

Number. The maximum possible zoom factor.

autoRefresh

Boolean. If true, the graph is refreshed automatically. This makes only sense when running as APEX plugin - here you have the SQL queries for loading new data with AJAX. If you run your code standalone, you have to provide new data as a parameter in the start or render method and therefore you have to use your own auto refresh logic.

refreshInterval

Numeric. The refresh interval in milliseconds.

chargeDistance

Numeric. Gets or sets the maximum distance over which charge forces are applied. For more informations have a look at the D3 API Reference. This option is not shown in the customize wizard.

charge

Numeric. Gets or sets the charge strength to the specified value. For more informations have a look at the D3 API Reference.

gravity

Numeric. Gets or sets the gravitational strength to the specified numerical value. For more informations see the D3 API Reference.

linkStrength

Numeric. Gets or sets the strength (rigidity) of links to the specified value in the range [0,1]. For more informations see the D3 API Reference.

friction

Numeric. Gets or sets the friction coefficient to the specified value. For more informations have a look at the D3 API Reference.

theta

Numeric. Gets or sets the Barnes–Hut approximation criterion to the specified value. For more informations see the D3 API Reference.

Event Functions

onNodeMouseenterFunction

Function. Gets or sets the function for the node mouseenter event.

In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword.

example.onNodeMouseenterFunction(
    function(event, data){
      console.log("Node mouse enter - event:", event);
      console.log("Node mouse enter - data:", data);
      console.log("Node mouse enter - this:", this);
    }
);

If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Mouse Enter [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:

console.log("Node mouse enter - event:", this.browserEvent);
console.log("Node mouse enter - data:", this.data);

Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.

onNodeMouseleaveFunction

Function. Gets or sets the function for the node mouseleave event.

In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword.

example.onNodeMouseleaveFunction(
    function(event, data){
      console.log("Node mouse leave - event:", event);
      console.log("Node mouse leave - data:", data);
      console.log("Node mouse leave - this:", this);
    }
);

If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Mouse Leave [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:

console.log("Node mouse leave - event:", this.browserEvent);
console.log("Node mouse leave - data:", this.data);

Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.

onNodeClickFunction

Function. Gets or sets the function for the node click event.

In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword.

example.onNodeClickFunction(
    function(event, data){
      console.log("Node click - event:", event);
      console.log("Node click - data:", data);
      console.log("Node click - this:", this);
    }
)

If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Click [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:

console.log("Node click - event:", this.browserEvent);
console.log("Node click - data:", this.data);

Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.

onNodeDblclickFunction

Function. Gets or sets the function for the node dblclick event.

In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword.

example.onNodeDblclickFunction(
    function(event, data){
      console.log("Node double click - event:", event);
      console.log("Node double click - data:", data);
      console.log("Node double click - this:", this);
    }
);

If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Double Click [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:

console.log("Node double click - event:", this.browserEvent);
console.log("Node double click - data:", this.data);

Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.

onNodeContextmenuFunction

Function. Gets or sets the function for the node contextmenu event.

In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword.

example.onNodeContextmenuFunction(
    function(event, data){
      console.log("Node contextmenu - event:", event);
      console.log("Node contextmenu - data:", data);
      console.log("Node contextmenu - this:", this);
    }
);

If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Contextmenu [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:

console.log("Node contextmenu - event:", this.browserEvent);
console.log("Node contextmenu - data:", this.data);

Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.

onLinkClickFunction

Function. Gets or sets the function for the link click event.

In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword.

example.onLinkClickFunction(
    function(event, data){
      console.log("Link click - event:", event);
      console.log("Link click - data:", data);
      console.log("Link click - this:", this);
    }
)

If used as APEX plugin you can also create an APEX dynamic action on the component event “Link Click [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:

console.log("Link click - event:", this.browserEvent);
console.log("Link click - data:", this.data);

Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.

Attention: It is not so easy to click a link, because the links are so narrow - if this option is needed I recommend to switch on the zoom mode - with zoom and pan it feels very natural to click links.

onLassoStartFunction

Function. Gets or sets the function for the lassostart event.

In the first two parameters you get the event and the d3 lasso data, inside your function you have access to the DOM node with the this keyword. In case of the lasso this is refering the svg container element, because the lasso itself is not interesting.

example.onLassoStartFunction(
    function(event, data){
      console.log("Lasso start - event:", event);
      console.log("Lasso start - data:", data);
      console.log("Lasso start - this:", this);
    }
);

If used as APEX plugin you can also create an APEX dynamic action on the component event “Lasso Start [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:

console.log("Lasso start - event:", this.browserEvent);
console.log("Lasso start - data:", this.data);

Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.

onLassoEndFunction

Function. Gets or sets the function for the lassoend event.

In the first two parameters you get the event and the d3 lasso data, inside your function you have access to the DOM node with the this keyword. In case of the lasso this is refering the svg container element, because the lasso itself is not interesting.

example.onLassoEndFunction(
    function(event, data){
      console.log("Lasso end - event:", event);
      console.log("Lasso end - data:", data);
      console.log("Lasso end - this:", this);
    }
);

If used as APEX plugin you can also create an APEX dynamic action on the component event “Lasso End [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:

console.log("Lasso end - event:", this.browserEvent);
console.log("Lasso end - data:", this.data);

Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.

Changelog

This D3 force implementation uses semantic versioning.

1.0.2 (2015-01-30)

  • Fixed “Configuration - Boolean values are not correct initialized” (found by Carsten Czarski, thank you for your response!)
  • Fixed “APEX - Page items to submit not working on AJAX refresh” (found by Carsten Czarski, thank you for your response!)

1.0.3 (2015-01-30)

  • Fixed “APEX - AJAX refresh not working without setting items to submit in region source”
  • Correct links from customize wizard to online API documentation
  • Activate also debug mode, when customize wizard is started
  • Some small cosmetic changes

1.0.4 (2015-02-15)

  • Fixed “APEX - unable to view datasets > 32k” (found by Andrew Weir, thank you for your response!)
  • Improved error handling: errors are shown as single nodes with error text as label
  • Empty nodes array does no longer break render function
  • Positions are rounded on export to save space for APEX parameter item

1.0.5 (2015-02-21)

  • Fixed “Links not correctly rendered in IE 9, 10, 11 when showLinkDirection is set to true” (found by Philippe Duchateau, thank you for your response!)

1.1.0 (2015-04-19)

  • New option lassoMode: boolean - if set to true you can select nodes with a lasso
  • New events for lasso mode: lassostart, lassoend - if You register to this events, you get as data an object with all nodes, number of selected nodes and also a APEX compatible string of selected node IDs in the form of the multi select lists like 1234:567:890 - for details and examples see API reference
  • New option alignFixedNodesToGrid: boolean - if set to true nodes are aligned to the nearest grid position on the drag end event - works only, if pinMode is set to true (thanks to Carsten Czarski for showing me an use case for this option)
  • New option gridSize: numeric - default 50 - grid size for the new option alignFixedNodesToGrid
  • New possible node attribute IMAGE: URL to an image - if you provide this attribute in your source data (SQL query with the APEX plugin), the node is rendered with an background image instead of a fill color (idea by Andrew Weir, thank you for your response!) - attention: this is definitly slowing down your visualization - please do not complain about this fact ;-)
  • New possible node attributes fixed, x, y (all lower case, because of these are also internal attributes of the D3 force layout): With these attributes you are able to predefine a layout already in your data (SQL query)
  • New API method moveFixedNodes(x,y): moves all fixed nodes in the provided direction - exampleGraphVariable.moveFixedNodes(10,-5).resume(); adds 10 to x position and -5 to y position on all fixed nodes - ATTENTION if alignFixedNodesToGrid is set to true this can have unexpected behavior - you must then provide values greater then grid size halved to see any changes on your graph, otherwise the positions are falling back to the nearest (current) grid position
  • New API method releaseFixedNodes
  • New API method resume: with this method you can resume the graph force without a complete render cycle - e.g. you call the new method releaseFixedNodes and to take your changes into effect you can call then resume exampleGraphVariable.releaseFixedNodes().resume();
  • New API method render: with this method you can render the graph with a complete render cycle - when used standalone there is no difference between the start and the render method - when used as APEX plugin the start method try to fetch new data with the query provided in your region source and call then the render method - with the render method you are now able to rerender the graph in APEX without fetching new data exampleGraphVariable.minNodeRadius(4).maxNodeRadius(20).render();
  • API method positions: In the past this method was only used to predefine a layout before rendering the graph - now you can call this method also after rendering is complete and with calling the new method resume you can apply new positions at runtime without rerender the graph exampleGraphVariable.positions([…]).resume(); (thanks to Mark Russellbrown to show me an unconventional use case for my force implementation and therefore force me to think about modification after rendering ;-)
  • New third keyword for the option nodeLinkTarget in the customize wizard: “domContainerID” - if you use this keyword, then each event on a node, that opens the link is using the DOM container ID of your graph for the link target - this means, all your links are opened in the same browser window/tab, but a second graph is using a different browser window/tab (thanks to Philippe Duchateau for the question regarding this option) - please have a look in the API reference for more details: nodelinktarget
  • Reducing the rendered DOM data by removing unnecessary id attributes on nodes, links and labels
  • Input data can now be also an object: you have the choice to deliver graph data in three formats (XML string, JSON string or JavaScript Object) - when used as APEX plugin the data is transferred as text - your query has to select a single clob result and this clob can also be a XML or JSON string - you have the choice depending on your database version and existing libraries
  • Fixed “Dragging a node triggers a click event”

1.2.0 (2015-05-31)

  • Refactor render function, so that the returned graph function is only one line of code and does not spoil the console when debug is set to true
  • New option zoomMode (thanks to Alexej Schneider to ask for this feature and for testing the new version and his valuable feedback): I tried this before and was not happy with the solution, because the pan were disturbing the nodes drag functionality - now it is working :-) ATTENTION: When zoomMode is set to true then the lassoMode is only working with the pressed alt or shift key KNOWN BUG: In iOS it is after the first zoom event no more possible, to drag a node - instead the whole graph is moved - this is, because iOS Safari provide a wrong event.target.tagName. Also a problem: your are not able to press the alt or shift key - if you want to use lasso and zoom together on a touch device, you have to provide a workaround. One possible way is to provide a button, which turns zoom mode on and off with the API zoomMode method - then the user has the choice between these two modes - not comfortable, but working.
  • New option minZoomFactor: The minimum possible zoom factor
  • New option maxZoomFactor: The maximum possible zoom factor
  • New method zoom: Can be used to programatically zoom to a point in the graph with the three parameters centerX, centerY and viewportWidth. More informations under zoom
  • New method zoomSmooth: Does the same as the zoom method, but animated in a nice way: zoomsmooth
  • New method nodeDataById: Helper function to get the data of one node. Can be helpful for the two new zoom methods to programatically focus a single node
  • New option showLegend: renders a legend for all (distinct) COLORVALUE attribute values of the nodes
  • New option showLabels: Labels are not new - a label is rendered, when a node has a filled attribute LABEL - new is the possibility to switch on and off the labels globally
  • Hint in the customize wizard, that the configuration object has to be saved in the region attributes to save the configuration permanently (thanks to Renato Nobre to ask me a question about this topic)
  • Reorganize the options in the customize wizard thematically: node/link/graph related options

1.2.1 (2015-06-02)

  • Fixed “Customize wizard jumps down when dragged on pages with hidden or fixed elements”

1.3.0 (2015-06-07)

  • New option showLoadingIndicatorOnAjaxCall: if set to true, a loading indicator is shown when used as a APEX plugin during the AJAX calls. If you want to show the loading indicator in a standalone implementation you can show and hide the loading indicator directly with the API method showLoadingIndicator (SHOW: example.showLoadingIndicator(true); HIDE: example.showLoadingIndicator(false);)
  • Update to D3 v3.5.5

1.4.0 (2015-08-03)

  • New possible node attribute COLORLABEL: Since there is an option to render a legend, it makes no sense to render the color names as legend labels, if the colorScheme “direct” is used to directly deliver CSS color codes (thanks to Philippe Duchateau for telling me about the problems). With other color schemes it is ok, since the COLORVALUE information can be any string like department names or ids or city names or whatever. To not to break existing graphs, the COLORVALUE is used as the legend label, if the COLORLABEL is not given in the nodes attributes.
  • New option onLinkClickFunction: You can register a function which is called when a link is clicked (thanks to Niels de Bruijn for requesting this feature). It is not so easy to click a link, because the links are so narrow - if this option is needed I recommend to switch on the zoom mode - with zoom and pan it feels very natural to click links.
  • New option setDomParentPaddingToZero: Boolean. If true, the style “padding: 0px;” is added to the graphs DOM parent element. If false, this style is removed from the graphs DOM parent element.
  • The customization wizard shows now in the configuration object only non-default options. This reduces the size of the configuration object and is more transparent.
  • New API methods “options” and “optionsCustomizationWizard”: with this API methods you can get and set the whole configuration object with one call. “options” ouput includes all options, who are accessible via the API methods including the registered event functions (no APEX dynamic actions, only the functions under the report attributes). “optionsCustomizationWizard” output includes only the options, who are accessible via the customization wizard. With both methods you can set all options who are accessible via the API in one call.
  • Restructuring the online API reference method overview.

1.4.1 (2015-08-05)

  • Fixed “Tooltip on wrong positions in complex layouts”. This was also the case with APEX 5 and universal theme. Thanks to Philippe Duchateau for telling me about this problem.

2.0.0 (2015-11-07)

  • New option preventLabelOverlappingOnForceEnd: If set to true the labels are aligned with a simulated annealing function to prevent overlapping when the graph is cooled down (correctly on the force end event and only on labels, who are not circular) - thanks to Philippe Duchateau to ask for such a feature and all the testing
  • New option labelPlacementIterations: The number of iterations for the preventLabelOverlappingOnForceEnd function - default is 250 - as higher the number, as higher the quality of the result - for details refer to the description of the simulated annealing function of the author Evan Wang under https://github.com/tinker10/D3-Labeler
  • New behaviour: the font size and weight of a label is aligned when you hovering a node with your mouse - this helps you to find the right label in graphs with many nodes
  • New possible value dotted for the links STYLE attribute: Now you have solid, dashed and dotted available for link styles
  • New link attribute INFOSTRING: Like for nodes - this is shown as a tooltip, if tooltips are switched on in the configuration and you hover the links; ATTENTION: links are very narrow, so this plays nice together with the zoomMode; thanks again to Philippe Duchateau for the ideas of this and the next feature :-)
  • New link attribute COLOR: This must be a HTML color code like green or #00ff00 because of SVG standard 1.1 does not support the inheritance of colors to markers and the graph function hast to manage dynamic markers for the colors and therefore the color names are used as identifiers for the markers
  • New API method/option transform: behaves like a normal getter/setter (the zoom and zoomSmooth methods implements only setters) and can be used in the conf object to initialize the graph with different translate/scale factors than [0,0]/1 - works only, if the zoomMode is set to true - the current transform value(an object) is rendered in the customization wizard conf object text area like all other options when the current value is different then the default value {“translate”:[0,0],“scale”:1}
  • Fixed: With the option alignFixedNodesToGrid it was possible to place nodes direct on the graphs left or top border - now the nodes are placed to the gridSize value, if the current position is smaller or equal the half of the gridsize
  • Fixed: Provided fixed positions on startup not correctly set
  • Fixed: No node shown if there is only one record return (thanks to Kenny Wang for reporting this bug)
  • Code integration of the D3 lasso and labeler plugins - no more need to load the files for this plugins
  • Code replacement of the XML to JSON converter X2JS with an own one
  • Code refactoring against JSHint: This refactoring is also the reason for a new major version (API changed: renamed graph function, integration of libs, new XML parser)
  • Update to D3 v3.5.6

2.0.1 (2015-11-18)

  • Fixed: Fixed positions not working in initial data in v2.0.0 - thanks to github.com/rlashaw to report this issue
  • Move online demo and documentation to own wiki for better maintenance

2.0.2 (2016-07-17)

  • Fixed: #12 - tooltips not showing correctly, if showLabels are set to false - thanks to github.com/pocelka to report this issue
projects/d3-force-apex-plugin.txt · Last modified: 2017/01/31 21:36 by Ottmar Gobrecht