/**
* Copyright (c) 2014, Oracle and/or its affiliates.
* All rights reserved.
*/
/**
* @ojcomponent oj.ojDiagram
* @augments oj.dvtBaseComponent
* @since 1.1.0
*
* @classdesc
* <h3 id="diagramOverview-section">
* JET Diagram Component
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#diagramOverview-section"></a>
* </h3>
*
* <p>Diagrams are used to display a set of nodes and the links between them.</p>
*
* {@ojinclude "name":"warning"}
*
* <pre class="prettyprint">
* <code>
* <div data-bind="ojComponent: {
* component: 'ojDiagram',
* layout : customLayoutObj.doLayout,
* nodes: [{id: N0, label: "Node 0",
* icon : {color: "#fdffcc", width: 10, height : 10}},
* {id: N1, label: "Node 1",
* icon : {color: "#2190e5", width: 20, height : 20}},
* {id: N2, label: "Node 2",
* icon : {color: "#5ea7d9", width: 30, height : 30}}],
* links : [{id: "L0", startNode : "N0", endNode : "N1"},
* {id: "L1", startNode : "N1", endNode : "N2"],
* {id: "L2", startNode : "N2", endNode : "N0"]]
* }"/>
* </code>
* </pre>
*
* {@ojinclude "name":"a11yKeyboard"}
*
* <h3 id="touch-section">
* Touch End User Information
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#touch-section"></a>
* </h3>
*
* {@ojinclude "name":"touchDoc"}
*
* <h3 id="keyboard-section">
* Keyboard End User Information
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#keyboard-section"></a>
* </h3>
*
* {@ojinclude "name":"keyboardDoc"}
*
* <h3 id="perf-section">
* Performance
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#perf-section"></a>
* </h3>
*
* <h4>Animation</h4>
* <p>Animation should only be enabled for visualizations of small to medium data sets.
* Alternate visualizations should be considered if identifying data changes is important,
* since all nodes will generally move and resize on any data change.
* </p>
*
* <h4>Data Set Size</h4>
* <p>Applications should avoid setting very large data densities on this component.
* Applications can aggregate small nodes to reduce the displayed data set size.
* </p>
*
* <h4>Style Attributes</h4>
* <p>Use the highest level options property available. For example, consider
* using attributes on <code class="prettyprint">styleDefaults.nodeDefaults</code>,
* <code class="prettyprint">styleDefaults.linkDefaults</code>, instead of
* attributes on the individual nodes and links. The component can take advantage of these
* higher level attributes to apply the style properties on containers, saving
* expensive DOM calls.
* </p>
*
* {@ojinclude "name":"rtl"}
*
* @desc Creates a JET Diagram.
*
* @example <caption>Initialize the Diagram:</caption>
* $(".selector").ojDiagram(layout : customLayoutObj.doLayout,
nodes:[{id:N0,label:"Node 0",icon:{color:"#fdffcc",width:10,height:10}},
{id:N1,label:"Node 1",icon:{color:"#2190e5",width:20,height:20}},
{id:N2,label:"Node 2",icon:{color:"#5ea7d9",width:30,height:30}}],
links:[{id:"L0",startNode:"N0",endNode:"N1"},
{id:"L1",startNode:"N1",endNode:"N2"},
{id:"L2",startNode:"N2",endNode:"N0"}]);
*/
oj.__registerWidget('oj.ojDiagram', $['oj']['dvtBaseComponent'],
{
widgetEventPrefix : "oj",
options: {
/**
* Fired whenever a supported component option changes, whether due to user interaction or programmatic
* intervention. If the new value is the same as the previous value, no event will be fired.
*
* @property {Object} data event payload
* @property {string} data.option the name of the option that changed, i.e. "value"
* @property {Object} data.previousValue an Object holding the previous value of the option
* @property {Object} data.value an Object holding the current value of the option
* @property {Object} ui.optionMetadata information about the option that is changing
* @property {string} ui.optionMetadata.writeback <code class="prettyprint">"shouldWrite"</code> or
* <code class="prettyprint">"shouldNotWrite"</code>. For use by the JET writeback mechanism.
*
* @example <caption>Initialize the component with the <code class="prettyprint">optionChange</code> callback:</caption>
* $(".selector").ojDiagram({
* 'optionChange': function (event, data) {}
* });
*
* @example <caption>Bind an event listener to the <code class="prettyprint">ojoptionchange</code> event:</caption>
* $(".selector").on({
* 'ojoptionchange': function (event, data) {
* window.console.log("option changing is: " + data['option']);
* };
* });
*
* @expose
* @event
* @memberof oj.ojDiagram
* @instance
*/
optionChange: null
},
//** @inheritdoc */
_Render: function(isResize) {
this.options['_logger'] = oj.Logger;
if (this.options['_templateFunction']) {
this.options['renderer'] = this._getTemplateRenderer(this.options['_templateFunction']);
}
if (this.options['renderer']) {
this.options['_contextHandler'] = this._getContextHandler();
}
return this._super(isResize);
},
/**
* Creates a callback function that will be used by DvtDiagramNode as a custom renderer
* @param {Function} templateFunction template function used to render knockout template
* @return {Function} a function that will be used by DvtDiagramNode as a custom renderer
* @private
* @instance
* @memberof oj.ojDiagram
*/
_getTemplateRenderer: function(templateFunction) {
var thisRef = this;
var templateHandlerFunc = function (context) {
var dummyDiv = document.createElement("div");
dummyDiv.style.display = "none";
dummyDiv._dvtcontext = thisRef._context;
thisRef.element.append(dummyDiv);
templateFunction({'parentElement':dummyDiv, 'data': context['data']});
var elem = dummyDiv.children[0];
if (elem && elem.namespaceURI === 'http://www.w3.org/2000/svg') {
$(dummyDiv).remove();
return elem;
}
else if (elem) {
return thisRef._GetDvtComponent(elem);
}
return null;
};
return templateHandlerFunc;
},
/**
* Creates a callback function that will be used by DvtDiagramNode to populate context for the custom renderer
* @return {Function} context handler callback used to create context for a custom renderer
* @private
* @instance
* @memberof oj.ojDiagram
*/
_getContextHandler: function() {
var thisRef = this;
var contextHandlerFunc = function (parentElement, rootElement, data, state, previousState) {
var context = {
'component': oj.Components.getWidgetConstructor(thisRef.element),
'parentElement': parentElement,
'rootElement': rootElement,
'data': data,
'state': state,
'previousState' : previousState,
'id' : data['id'],
'type' : 'node',
'label' : data ['label']
};
return context;
}
return contextHandlerFunc;
},
/**
* Renders default hover effect for the diagram node
* @param {Object} context - property object with the following fields
* <ul>
* <li>{Function} component - ojDiagram widget constructor</li>
* <li>{Object} data - a data object for the node</li>
* <li>{SVGElement} parentElement - a parent group element that takes a custom SVG fragment as the node content. Used for measurements and reading properties.
* Modifications of the parentElement are not supported</li>
* <li>{SVGElement} rootElement - an SVG fragment created as a node content passed for subsequent modifications</li>
* <li>{Object} state - property object with the following boolean properties: hovered, selected, focused, zoom</li>
* <li>{Object} previousState - property object with the following boolean properties: hovered, selected, focused, zoom</li>
* <li>{string} id - node id</li>
* <li>{string} type - object type - node</li>
* <li>{string} label - object label</li>
* </ul>
* @expose
* @instance
* @memberof oj.ojDiagram
*/
renderDefaultHover: function (context) {
if (!context['previousState'] || context['state']['hovered'] != context['previousState']['hovered']) {
var comp = this._GetDvtComponent(this.element);
comp.processDefaultHoverEffect(context['id'], context['state']['hovered']);
}
},
/**
* Renders default selection effect for the diagram node
* @param {Object} context - property object with the following fields
* <ul>
* <li>{Function} component - ojDiagram widget constructor</li>
* <li>{Object} data - a data object for the node</li>
* <li>{SVGElement} parentElement - a parent group element that takes a custom SVG fragment as the node content. Used for measurements and reading properties.
* Modifications of the parentElement are not supported</li>
* <li>{SVGElement} rootElement - an SVG fragment created as a node content passed for subsequent modifications</li>
* <li>{Object} state - property object with the following boolean properties: hovered, selected, focused, zoom</li>
* <li>{Object} previousState - property object with the following boolean properties: hovered, selected, focused, zoom</li>
* <li>{string} id - node id</li>
* <li>{string} type - object type - node</li>
* <li>{string} label - object label</li>
* </ul>
* @expose
* @instance
* @memberof oj.ojDiagram
*/
renderDefaultSelection: function (context) {
if (!context['previousState'] || context['state']['selected'] != context['previousState']['selected']) {
var comp = this._GetDvtComponent(this.element);
comp.processDefaultSelectionEffect(context['id'], context['state']['selected']);
}
},
/**
* Renders default focus effect for the diagram node
* @param {Object} context - property object with the following fields
* <ul>
* <li>{Function} component - ojDiagram widget constructor</li>
* <li>{Object} data - a data object for the node</li>
* <li>{SVGElement} parentElement - a parent group element that takes a custom SVG fragment as the node content. Used for measurements and reading properties.
* Modifications of the parentElement are not supported</li>
* <li>{SVGElement} rootElement - an SVG fragment created as a node content passed for subsequent modifications</li>
* <li>{Object} state - property object with the following boolean properties: hovered, selected, focused, zoom</li>
* <li>{Object} previousState - property object with the following boolean properties: hovered, selected, focused, zoom</li>
* <li>{string} id - node id</li>
* <li>{string} type - object type - node</li>
* <li>{string} label - object label</li>
* </ul>
* @expose
* @instance
* @memberof oj.ojDiagram
*/
renderDefaultFocus: function (context) {
if (!context['previousState'] || context['state']['focused'] != context['previousState']['focused']) {
var comp = this._GetDvtComponent(this.element);
comp.processDefaultFocusEffect(context['id'], context['state']['focused']);
}
},
//** @inheritdoc */
_CreateDvtComponent : function(context, callback, callbackObj) {
return dvt.DvtDiagram.newInstance(context, callback, callbackObj);
},
//** @inheritdoc */
_ConvertLocatorToSubId : function(locator) {
var subId = locator['subId'];
// Convert the supported locators
if(subId == 'oj-diagram-link') {
// link[index]
subId = 'link[' + locator['index'] + ']';
}
else if(subId == 'oj-diagram-node') {
// node[index]
subId = 'node[' + locator['index'] + ']';
}
else if(subId == 'oj-diagram-tooltip') {
subId = 'tooltip';
}
// Return the converted result or the original subId if a supported locator wasn't recognized. We will remove
// support for the old subId syntax in 1.2.0.
return subId;
},
//** @inheritdoc */
_ConvertSubIdToLocator : function(subId) {
var locator = {};
if(subId.indexOf('link') == 0) {
// link[index]
locator['subId'] = 'oj-diagram-link';
locator['index'] = this._GetFirstIndex(subId);
}
else if(subId.indexOf('node') == 0) {
// node[index]
locator['subId'] = 'oj-diagram-node';
locator['index'] = this._GetFirstIndex(subId);
}
else if(subId == 'tooltip') {
locator['subId'] = 'oj-diagram-tooltip';
}
return locator;
},
//** @inheritdoc */
_GetComponentStyleClasses : function() {
var styleClasses = this._super();
styleClasses.push('oj-diagram');
return styleClasses;
},
//** @inheritdoc */
_GetChildStyleClasses : function() {
var styleClasses = this._super();
styleClasses['oj-diagram-node-label'] = {'path': 'styleDefaults/nodeDefaults/labelStyle', 'property': 'CSS_TEXT_PROPERTIES'};
styleClasses['oj-diagram-node oj-selected'] = {'path': 'styleDefaults/nodeDefaults/selectionColor', 'property': 'border-color'};
styleClasses['oj-diagram-node oj-hover'] = [
{'path': 'styleDefaults/nodeDefaults/hoverOuterColor', 'property': 'border-top-color'},
{'path': 'styleDefaults/nodeDefaults/hoverInnerColor', 'property': 'border-bottom-color'}
];
styleClasses['oj-diagram-link'] = {'path': 'styleDefaults/linkDefaults/color', 'property': 'color'};
styleClasses['oj-diagram-link-label'] = {'path': 'styleDefaults/linkDefaults/labelStyle', 'property': 'CSS_TEXT_PROPERTIES'};
styleClasses['oj-diagram-link oj-selected'] = {'path': 'styleDefaults/linkDefaults/selectionColor', 'property': 'border-color'};
styleClasses['oj-diagram-link oj-hover'] = [
{'path': 'styleDefaults/linkDefaults/hoverOuterColor', 'property': 'border-top-color'},
{'path': 'styleDefaults/linkDefaults/hoverInnerColor', 'property': 'border-bottom-color'}
];
return styleClasses;
},
//** @inheritdoc */
_GetEventTypes : function() {
return ['optionChange'];
},
//** @inheritdoc */
_GetTranslationMap: function() {
// The translations are stored on the options object.
var translations = this.options['translations'];
// Safe to modify super's map because function guarentees a new map is returned
var ret = this._super();
ret['DvtUtilBundle.DIAGRAM'] = translations['componentName'];
return ret;
},
//** @inheritdoc */
_HandleEvent : function(event) {
var type = event && event.getType ? event.getType() : null;
if(type === dvt.DvtSelectionEvent.TYPE) {
// update the options selection state
this._UserOptionChange('selection', event.getSelection());
}
else {
this._super(event);
}
},
/**
* Returns number of diagram nodes
* @return {Number} The number of nodes
* @expose
* @instance
* @memberof oj.ojDiagram
*/
getNodeCount: function() {
var auto = this._component.getAutomation();
return auto.getNodeCount();
},
/**
* Returns an object with the following properties for automation testing verification of the diagram node at the
* specified index.
*
* @param {String} nodeIndex Node index
* @property {string} background The background style for the node.
* @property {Object|null} icon The icon for the node, or null if none exists.
* @property {string} icon.color The color of the icon
* @property {string} icon.shape The shape of the icon
* @property {string} label Node label
* @property {boolean} selected The selected state of the node
* @property {string} tooltip Node tooltip
* @return {Object|null} An object containing properties for the node at the given index, or null if none exists.
* @expose
* @instance
* @memberof oj.ojDiagram
*/
getNode: function(nodeIndex) {
var auto = this._component.getAutomation();
return auto.getNode(nodeIndex);
},
/**
* Returns number of diagram links
* @return {Number} The number of links
* @expose
* @instance
* @memberof oj.ojDiagram
*/
getLinkCount: function() {
var auto = this._component.getAutomation();
return auto.getLinkCount();
},
/**
* Returns an object with the following properties for automation testing verification of the diagram link at the
* specified index.
*
* @param {number} linkIndex Link index
* @property {string} color Link color
* @property {string} label Link label
* @property {string} endConnectorType The type of end connector on the link
* @property {string} endNode The id of the end node.
* @property {boolean} selected The selected state of the link
* @property {string} startConnectorType The type of start connector on the link
* @property {string} startNode The id of the start node.
* @property {string} style Link style
* @property {string} tooltip Link tooltip
* @property {number} width Link width
* @return {Object|null} An object containing properties for the link at the given index, or null if none exists.
* @expose
* @instance
* @memberof oj.ojDiagram
*/
getLink: function(linkIndex) {
var auto = this._component.getAutomation();
return auto.getLink(linkIndex);
},
/**
* {@ojinclude "name":"nodeContextDoc"}
* @param {!Element} node - {@ojinclude "name":"nodeContextParam"}
* @returns {Object|null} {@ojinclude "name":"nodeContextReturn"}
*
* @example {@ojinclude "name":"nodeContextExample"}
*
* @expose
* @instance
* @memberof oj.ojDiagram
*/
getContextByNode: function(node) {
// context objects are documented with @ojnodecontext
var context = this.getSubIdByNode(node);
if (context && context['subId'] !== 'oj-diagram-tooltip')
return context;
return null;
}
});
Source: src/main/javascript/oracle/oj/ojdiagram/ojdiagram.js
Oracle® JavaScript Extension Toolkit (JET)
1.1.2
E65298-01