RefGraph.php.view.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. // @require variables:
  2. if ('undefined' === typeof HTML_ID_REF_GRAPH) throw "Missing HTML_ID_REF_GRAPH";
  3. if ('undefined' === typeof TYPENAME) throw "Missing TYPENAME";
  4. if ('undefined' === typeof PRIMARY_KEY) throw "Missing PRIMARY_KEY";
  5. // if ('undefined' === typeof FUNCTION_FETCH_CHILDRENS) throw "Missing FUNCTION_FETCH_CHILDRENS";
  6. // if ('undefined' === typeof FUNCTION_FETCH_PARENTS) throw "Missing FUNCTION_FETCH_PARENTS";
  7. // if ('undefined' === typeof JS_CHANNEL_UPDATE_NAME) throw "Missing JS_CHANNEL_UPDATE_NAME";
  8. var DBG = DBG || 0;
  9. var DBG_SANKEY = DBG_SANKEY || 0;
  10. DBG_SANKEY = 1
  11. var SHOW_FLOW_DIAGRAM = SHOW_FLOW_DIAGRAM || 0;
  12. var _isFullScreen = true; // TODO: get from arg
  13. var _html = {
  14. container: null,
  15. top: null,
  16. fullscreenBtn: null,
  17. selected: null
  18. }
  19. var _nodes = new vis.DataSet(); // [ { id: '', label: '' }, ... ]
  20. var _edges = new vis.DataSet(); // [ { id: '', from: '', to: '' }, ... ]
  21. var _network = null; // graph object
  22. if (SHOW_FLOW_DIAGRAM) {
  23. var _sankeyNode = null;
  24. var _sankeyLink = null;
  25. var sankey = null;
  26. var _sankeySvgNode = null;
  27. var _sankeySvgWidth = 0;
  28. var _sankeyHeight = 0;
  29. var _sankeyFormatNumber = d3.format(",.0f");
  30. var _sankeyFormat = function(d) { return _sankeyFormatNumber(d) + " TWh"; };
  31. var _sankeyColor = d3.scaleOrdinal(d3.schemeCategory10);
  32. }
  33. var _todoGraphData = [ { nodes: [], edges: [] } ]; // data by levels
  34. var _defaultWfsParams = {
  35. resolve: 'all',
  36. resolveDepth: 2,
  37. }
  38. if ('undefined' !== typeof WFS_URL) {
  39. _defaultWfsParams['WFS_URL'] = WFS_URL
  40. }
  41. var _defaultVisJsOptions = {
  42. nodes: {
  43. shape: 'box'
  44. },
  45. interaction: {
  46. dragNodes: false
  47. },
  48. physics: {
  49. enabled: false
  50. },
  51. layout: {
  52. hierarchical: {
  53. direction: 'LR',
  54. levelSeparation: 500, // hierarchical.levelSeparation Number 150 The distance between the different levels.
  55. nodeSpacing: 50, // hierarchical.nodeSpacing Number 100 Minimum distance between nodes on the free axis. This is only for the initial layout. If you enable physics, the node distance there will be the effective node distance.
  56. treeSpacing: 500, // hierarchical.treeSpacing Number 200 Distance between different trees (independent networks). This is only for the initial layout. If you enable physics, the repulsion model will denote the distance between the trees.
  57. sortMethod: 'directed'
  58. // hierarchical.enabled Boolean false Toggle the usage of the hierarchical layout system. If this option is not defined, it is set to true if any of the properties in this object are defined.
  59. // hierarchical.blockShifting Boolean true Method for reducing whitespace. Can be used alone or together with edge minimization. Each node will check for whitespace and will shift it's branch along with it for as far as it can, respecting the nodeSpacing on any level. This is mainly for the initial layout. If you enable physics, they layout will be determined by the physics. This will greatly speed up the stabilization time though!
  60. // hierarchical.edgeMinimization Boolean true Method for reducing whitespace. Can be used alone or together with block shifting. Enabling block shifting will usually speed up the layout process. Each node will try to move along its free axis to reduce the total length of it's edges. This is mainly for the initial layout. If you enable physics, they layout will be determined by the physics. This will greatly speed up the stabilization time though!
  61. // hierarchical.parentCentralization Boolean true When true, the parents nodes will be centered again after the the layout algorithm has been finished.
  62. // hierarchical.direction String 'UD' The direction of the hierarchical layout. The available options are: UD, DU, LR, RL. To simplify: up-down, down-up, left-right, right-left.
  63. // hierarchical.sortMethod String 'hubsize' The algorithm used to ascertain the levels of the nodes based on the data. The possible options are: hubsize, directed.
  64. //
  65. // Hubsize takes the nodes with the most edges and puts them at the top. From that the rest of the hierarchy is evaluated.
  66. //
  67. // Directed adheres to the to and from data of the edges. A --> B so B is a level lower than A.
  68. }
  69. }
  70. };
  71. function dataMakeNode(params) {
  72. var objectName = params.typeName.substr(params.typeName.indexOf(':') + 1)
  73. var nodeId = objectName + '.' + params.primaryKey // TODO: primaryKey?
  74. return {
  75. id: nodeId,
  76. label: makeShortLabel(nodeId), // TODO: get from schema assert @label attribute
  77. group: objectName,
  78. _loaded: true,
  79. typeName: params.typeName,
  80. primaryKey: params.primaryKey // TODO: _primaryKey
  81. }
  82. }
  83. function dataMakeEdge(parentNodeId, nodeObject) {
  84. return {
  85. id: parentNodeId + nodeObject.id,
  86. from: parentNodeId,
  87. to: nodeObject.id,
  88. }
  89. }
  90. function dataMakeXlinkNode(params) {
  91. var objectName = params.typeName.substr(params.typeName.indexOf(':') + 1)
  92. var nodeId = params.xlink.substr(params.xlink.indexOf('#') + 1)
  93. var primaryKey = nodeId.substr(nodeId.lastIndexOf('.') + 1)
  94. return {
  95. id: nodeId,
  96. label: makeShortLabel(nodeId) + ' (+)',
  97. group: objectName,
  98. _loaded: false,
  99. typeName: params.typeName,
  100. primaryKey: primaryKey
  101. }
  102. }
  103. function dataMakeFetchMoreNode(params) {
  104. // params = {
  105. // parentNodeId: parentNodeId,
  106. // type: json.type,
  107. // objectName: objectName,
  108. // ref: json,
  109. // }
  110. if(DBG)console.log('DBG dataMakeFetchMoreNode(params)', params)
  111. var nodeId = params.parentNodeId+'fetch-more-features-'+params.type+'-on-' + params.objectName;
  112. return {
  113. id: nodeId,
  114. label: 'Pobierz więcej (+)',
  115. group: 'fetch-more-data', // params.objectName,
  116. _loaded: false,
  117. _type: 'ref',
  118. parentNodeId: params.parentNodeId,
  119. ref: params.ref,
  120. // typeName: typeName,
  121. // primaryKey: nodeId.substr(nodeId.lastIndexOf('.') + 1)
  122. };
  123. }
  124. function dataMakeFetchMoreEdge(parentNodeId, fetchMoreNode) {
  125. // @param parentNodeId - from node id
  126. // @param fetchMoreNode - from dataMakeFetchMoreNode
  127. if(DBG)console.log('DBG dataMakeFetchMoreEdge(parentNodeId, fetchMoreNode)', {parentNodeId:parentNodeId, fetchMoreNode:fetchMoreNode})
  128. return {
  129. id: fetchMoreNode.id,
  130. from: parentNodeId,
  131. to: fetchMoreNode.id
  132. }
  133. }
  134. (function () {
  135. var form = document.getElementById('wfs_request')
  136. var featureTypeName = TYPENAME
  137. var wfsParams = Object.assign({}, _defaultWfsParams, {
  138. primaryKey: PRIMARY_KEY,
  139. })
  140. if(DBG)console.log('p5WFS_GetFeature', featureTypeName, wfsParams)
  141. p5WFS_GetFeature(featureTypeName, wfsParams).then(function (features) {
  142. if(DBG)console.log('features', features)
  143. updateWfsResponse(features, featureTypeName)
  144. updateSankeyDiagram()
  145. }).catch(function (e) {
  146. if(DBG)console.warn(e)
  147. p5UI__notifyAjaxCallback({ type: 'error', msg: e })
  148. })
  149. if (SHOW_FLOW_DIAGRAM) {
  150. _sankeySvgNode = document.getElementById("d3-sankey-test")
  151. var d3SvgNode = d3.select(_sankeySvgNode);
  152. var svgWidth = d3SvgNode.attr("width")
  153. if ('%' === svgWidth.substr(-1)) {
  154. svgWidth = parseInt(svgWidth.substr(0, svgWidth.length - 1))
  155. svgWidth = (isNaN(svgWidth) || svgWidth > 100 || svgWidth < 0) ? 100 : svgWidth
  156. var parentNodeStyle = getComputedStyle(_sankeySvgNode.parentNode)
  157. var wrapNodeWidth = _sankeySvgNode.parentNode.clientWidth - parseFloat(parentNodeStyle.paddingLeft || 0) - parseFloat(parentNodeStyle.paddingRight || 0);
  158. d3SvgNode.attr("width", (100 === svgWidth)
  159. ? wrapNodeWidth
  160. : Math.floor(wrapNodeWidth * svgWidth / 100)
  161. )
  162. }
  163. _sankeySvgWidth = parseInt(d3SvgNode.attr("width"));
  164. _sankeyHeight = parseInt(d3SvgNode.attr("height"));
  165. sankey = d3.sankey()
  166. .nodeWidth(15)
  167. .nodePadding(10)
  168. .extent([ [ 1, 1 ], [ _sankeySvgWidth - 1, _sankeyHeight - 6 ] ]);
  169. }
  170. renderLayout()
  171. })();
  172. function updateWfsResponse(features, featureTypeName) {
  173. if(DBG)console.log('updateWfsResponse', { features: features, featureTypeName: featureTypeName })
  174. // if (_network !== null) {
  175. // _network.destroy();
  176. // _network = null;
  177. // }
  178. parseResponseRec(_todoGraphData, features, featureTypeName)
  179. var _graphData = {
  180. nodes: _nodes,
  181. edges: _edges,
  182. };
  183. _todoGraphData.forEach(function (levelData, levelIdx) {
  184. if (levelData.nodes && levelData.nodes.length) {
  185. levelData.nodes.forEach(function (node) {
  186. try {
  187. _nodes.add(node)
  188. } catch (e) {
  189. if(DBG)console.log('_graphData.nodes.add [level='+levelIdx+'] error:', e);
  190. }
  191. })
  192. }
  193. if (levelData.edges && levelData.edges.length) {
  194. levelData.edges.forEach(function (edge) {
  195. try {
  196. _edges.add(edge)
  197. } catch (e) {
  198. if(DBG)console.log('_graphData.edges.add [level='+levelIdx+'] error:', e);
  199. }
  200. })
  201. }
  202. })
  203. _todoGraphData = [ { nodes: [], edges: [] } ]; // clear to default values
  204. var options = _defaultVisJsOptions
  205. if(DBG)console.log('_graphData', _graphData)
  206. if (!_network) {
  207. _network = new vis.Network(_html['container'], _graphData, options); // graphData: { nodes: [], edges: [] }
  208. // add event listeners
  209. _network.on('selectNode', function (params) {
  210. if(DBG)console.log('Selection: ', params.nodes)
  211. var featureID = params.nodes[0]
  212. if (!featureID) return;
  213. if ('-loading' === featureID.substr(-1 * '-loading'.length)) {
  214. // TODO: gui msg...
  215. if(DBG)console.log('loading nodes connected with: "' +featureID.substr(0, featureID.length - '-loading'.length) + '" ...')
  216. return;
  217. }
  218. var selectedNode = _nodes.get(featureID)
  219. if(DBG)console.log('Selection: selectedNode:', selectedNode)
  220. if (!selectedNode) return;
  221. if ('ref' === selectedNode._type) {
  222. fetchNodeMoreRefs(selectedNode, featureID)
  223. } else {
  224. renderSelectedNode(selectedNode)
  225. if (selectedNode._loaded) return;
  226. fetchNodeRecurse(selectedNode, featureID)
  227. }
  228. });
  229. }
  230. }
  231. function isP5LinkObject(json) {
  232. if ( !('type' in json) || !json['type'] ) return false;
  233. if ( !('value' in json) || !json['value'] ) return false;
  234. if ( !('@typeName' in json) || !json['@typeName'] ) return false;
  235. if ( !('@startIndex' in json) || !json['@startIndex'] ) return false;
  236. if ( !('@backRefPK' in json) || !json['@backRefPK'] ) return false;
  237. if ( !('@backRefNS' in json) || !json['@backRefNS'] ) return false;
  238. return true;
  239. }
  240. function parseResponseRec(_todoGraphData, json, typeName, parentNodeId, level) {
  241. var level = level || 0
  242. var parentNodeId = parentNodeId || null
  243. if(DBG)console.log('DBG::parseResponseRec', {json:json, typeName:typeName, parentNodeId:parentNodeId, isString: p5Utils__isString(json), isArray: p5Utils__isArray(json), isObject: p5Utils__isObject(json)});
  244. if (p5Utils__isArray(json)) {
  245. // TODO: create named group
  246. var isXlinkList = (json.length > 0 && p5Utils__isString(json[0]))
  247. json.forEach(function (subJson) {
  248. if (isXlinkList) {
  249. parseResponseXlinkListRec(_todoGraphData, subJson, typeName, parentNodeId, level)
  250. } else {
  251. parseResponseRec(_todoGraphData, subJson, typeName, parentNodeId, level)
  252. }
  253. })
  254. } else if (p5Utils__isObject(json) && isP5LinkObject(json)) {
  255. if(DBG)console.log('DBG::parseResponseRec isP5LinkObject');
  256. parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level)
  257. } else if (p5Utils__isObject(json)) {
  258. // _todoGraphData.nodes.add({ id: nodeId, label: nodeId })
  259. if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
  260. var nodeObject = dataMakeNode({
  261. typeName: typeName,
  262. primaryKey: (json['ID']) ? json['ID'] : null, // TODO: get primaryKey from object
  263. })
  264. var nodeId = nodeObject.id
  265. _todoGraphData[level].nodes.push(nodeObject)
  266. if (parentNodeId) {
  267. _todoGraphData[level].edges.push(dataMakeEdge(parentNodeId, nodeObject))
  268. }
  269. Object.keys(json).filter(function (fieldName) {
  270. return (fieldName.indexOf(':') > -1)
  271. })
  272. .forEach(function (fieldName) {
  273. var value = json[fieldName]
  274. parseResponseRec(_todoGraphData, value, fieldName, nodeId, level + 1)
  275. })
  276. } else if (p5Utils__isString(json)) {
  277. if(DBG)console.log('TODO: Not implemented - parseResponseRec isString');
  278. } else {
  279. if(DBG)console.log('TODO: Not implemented - parseResponseRec is not string, not array and not object');
  280. }
  281. }
  282. function parseResponseXlinkListRec(_todoGraphData, json, typeName, parentNodeId, level) {
  283. if(DBG)console.log('DBG::parseResponseRec:XlinkList', {json:json, typeName:typeName, parentNodeId:parentNodeId, isString: p5Utils__isString(json), isArray: p5Utils__isArray(json), isObject: p5Utils__isObject(json)});
  284. if (p5Utils__isString(json)) { // xlink "https://biuro.biall-net.pl/wfs/default_db/BI_audit_ENERGA_RUM_KONTRAHENCI#BI_audit_ENERGA_RUM_KONTRAHENCI.9233",
  285. var nodeId = json.substr(json.indexOf('#') + 1)
  286. {
  287. // _graphData.nodes.add({ id: nodeId, label: nodeId })
  288. if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
  289. var nodeObject = dataMakeXlinkNode({
  290. xlink: json,
  291. typeName: typeName,
  292. primaryKey: (json['ID']) ? json['ID'] : null, // TODO: get primaryKey from object
  293. })
  294. _todoGraphData[level].nodes.push(nodeObject)
  295. if (parentNodeId) {
  296. _todoGraphData[level].edges.push(dataMakeEdge(parentNodeId, nodeObject))
  297. }
  298. }
  299. } else if (p5Utils__isObject(json) && isP5LinkObject(json)) {
  300. parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level)
  301. } else {
  302. if(DBG)console.log('TODO: Not implemented - parseResponseRec:XlinkList is not string and not object');
  303. }
  304. }
  305. function parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level) {
  306. if(DBG)console.log('parseResponseRec isObject and P5Link - fetch more xlink object');
  307. // example json: { type: "next",
  308. // @backRefNS: "default_db/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA",
  309. // @backRefPK: "42",
  310. // @typeName: "default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_P…ow:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row",
  311. // @startIndex: "10" }
  312. if (!parentNodeId) throw "Missing parentNodeId for ref link object";
  313. var objectName = typeName.substr(typeName.indexOf(':') + 1)
  314. {
  315. // _graphData.nodes.add({ id: nodeId, label: nodeId })
  316. if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
  317. switch (json.type) {
  318. case 'next': {
  319. var nodeObject = dataMakeFetchMoreNode({
  320. parentNodeId: parentNodeId,
  321. type: json.type,
  322. objectName: objectName,
  323. ref: json,
  324. })
  325. _todoGraphData[level].nodes.push(nodeObject)
  326. _todoGraphData[level].edges.push(dataMakeFetchMoreEdge(parentNodeId, nodeObject))
  327. } break;
  328. default: {
  329. if(DBG)console.log('TODO: Not implemented - parseResponseRec isObject with type "'+json.type+'" - fetch more xlink object');
  330. }
  331. }
  332. }
  333. }
  334. function fetchNodeMoreRefs(selectedNode, featureID) {
  335. try {
  336. if (!selectedNode) throw "Missing featureID"
  337. if (!featureID) throw "Missing featureID"
  338. if (!selectedNode.ref) throw "Missing selectedNode.ref"
  339. var idSelectedNode = selectedNode.id
  340. var idSelectedEdge = selectedNode.id
  341. var parentNodeId = selectedNode.parentNodeId
  342. var featureTypeName = selectedNode.ref['@typeName']
  343. var backRefNS = selectedNode.ref['@backRefNS']
  344. var backRefPK = selectedNode.ref['@backRefPK']
  345. var backRefField = selectedNode.ref['@typeName']
  346. var startIndex = selectedNode.ref['@startIndex']
  347. if(DBG)console.log('DBG:fetch: fetchNodeMoreRefs', { 'selectedNode': selectedNode, 'featureID': featureID });
  348. _nodes.update({
  349. id: idSelectedNode,
  350. label: "Pobieranie danych..."
  351. })
  352. var wfsParams = {
  353. backRefNS: backRefNS,
  354. backRefPK: backRefPK,
  355. backRefField: backRefField,
  356. startIndex: startIndex,
  357. maxFeatures: 10,
  358. };
  359. p5WFS_GetFeature(featureTypeName, wfsParams).then(function (features) {
  360. if(DBG)console.log('features', features)
  361. if (!features.length) throw "Brak danych"
  362. features.forEach(function (feature) {
  363. // TODO: validate
  364. var objectName = featureTypeName.substr(featureTypeName.indexOf(':') + 1)
  365. var nodeId = objectName + '.' + feature.ID // TODO: primaryKey?
  366. _todoGraphData[0].edges.push({ id: parentNodeId + nodeId, from: parentNodeId, to: nodeId })
  367. })
  368. // var refFields = Object.keys(features[0]).filter(function (fieldName) {
  369. // return (fieldName.indexOf(':') > -1)
  370. // }).filter(function (fieldName) {
  371. // return (features[0][fieldName].length > 0)
  372. // })
  373. // if (!refFields.length) return "Brak danych"
  374. // features = features.map(function (feature) {
  375. // return {
  376. // feature
  377. // }
  378. // })
  379. // console.log('TODO: features: ', features);
  380. // updateWfsResponse(features[0], featureTypeName) // TODO: RMME DBG
  381. // updateWfsResponse(features[1], featureTypeName) // TODO: RMME DBG
  382. // var feature = features[1]
  383. // var nodeId = featureTypeName + '.' + feature['ID']
  384. // try {
  385. // _edges.add({
  386. // id: parentNodeId + nodeId,
  387. // from: parentNodeId,
  388. // to: nodeId
  389. // })
  390. // } catch (e) {
  391. // if(DBG)console.warn('TODO: XXX: ' + e)
  392. // }
  393. features.forEach(function (feature) {
  394. updateWfsResponse(feature, featureTypeName)
  395. })
  396. var nodeSelectedData = _nodes.get(idSelectedNode)
  397. var edgeSelectedData = _nodes.get(idSelectedEdge)
  398. _nodes.remove({ id: idSelectedNode })
  399. _edges.remove({ id: idSelectedEdge })
  400. if (features.length >= 10) {
  401. var nodeObject = dataMakeFetchMoreNode({
  402. parentNodeId: nodeSelectedData.parentNodeId,
  403. type: nodeSelectedData.type,
  404. objectName: nodeSelectedData.objectName,
  405. ref: Object.assign(nodeSelectedData.ref, {
  406. '@startIndex': (10 + parseInt(startIndex)),
  407. value: nodeSelectedData.ref.value.replace('startIndex=' + startIndex, 'startIndex=' + (10 + parseInt(startIndex)))
  408. })
  409. })
  410. _nodes.add(nodeObject)
  411. _edges.add(dataMakeFetchMoreEdge(parentNodeId, nodeObject))
  412. }
  413. updateSankeyDiagram()
  414. return "Pobrano nowe dane"
  415. }).then(function (msg) {
  416. p5UI__notifyAjaxCallback({ type: 'info', msg: msg })
  417. }).catch(function (e) {
  418. if(DBG)console.warn(e)
  419. p5UI__notifyAjaxCallback({ type: 'error', msg: e })
  420. _nodes.remove({ id: idSelectedNode })
  421. _edges.remove({ id: idSelectedEdge })
  422. })
  423. } catch (e) {
  424. if(DBG)console.warn(e)
  425. }
  426. }
  427. function fetchNodeRecurse(selectedNode, featureID) {
  428. try {
  429. var selectedNodeId = selectedNode.id
  430. if (!selectedNode) throw "Missing featureID"
  431. if (!featureID) throw "Missing featureID"
  432. var featureEx = featureID.split('.')
  433. if (2 !== featureEx.length) throw "Not supported featureID format"
  434. var featureTypeName = selectedNode.typeName; // 'default_db__x3A__' + featureEx[0] + ':' + featureEx[0]
  435. var wfsParams = Object.assign({}, _defaultWfsParams, {
  436. primaryKey: featureEx[1]
  437. });
  438. if(DBG)console.log('DBG:fetch: fetchNodeRecurse', { 'selectedNode': selectedNode, 'featureID': featureID });
  439. // view-source:http://visjs.org/examples/network/data/dynamicData.html
  440. _nodes.update({
  441. id: featureID,
  442. label: makeShortLabel(selectedNodeId) + ' ...'
  443. })
  444. p5WFS_GetFeature(featureTypeName, wfsParams).then(function (features) {
  445. if(DBG)console.log('features', features)
  446. if (!features.length || 1 !== features.length) throw "Brak danych" // require 1 feature with recurse
  447. var refFields = Object.keys(features[0]).filter(function (fieldName) {
  448. return (fieldName.indexOf(':') > -1)
  449. }).filter(function (fieldName) {
  450. return (features[0][fieldName].length > 0)
  451. })
  452. if (!refFields.length) return "Brak danych"
  453. updateWfsResponse(features, featureTypeName)
  454. updateSankeyDiagram()
  455. return "Pobrano nowe dane"
  456. }).then(function (msg) {
  457. p5UI__notifyAjaxCallback({ type: 'info', msg: msg })
  458. _nodes.update({ id: featureID, label: makeShortLabel(selectedNodeId), _loaded: true })
  459. }).catch(function (e) {
  460. if(DBG)console.warn(e)
  461. p5UI__notifyAjaxCallback({ type: 'error', msg: e })
  462. _nodes.update({ id: featureID, label: makeShortLabel(selectedNodeId), _loaded: true })
  463. })
  464. } catch (e) {
  465. if(DBG)console.warn(e)
  466. }
  467. }
  468. function makeShortLabel(label) { // TODO: shorter name
  469. // BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object.1234567
  470. if (label.length < 30) return label;
  471. var exLabel = label.split('.')
  472. if (exLabel.length !== 2) return label;
  473. return exLabel[0].substr(0, 24) + '...' + exLabel[0].substr(-10) + '.' + exLabel[1];
  474. }
  475. function renderLayout(mode) {
  476. if ('undefined' !== typeof mode && _isFullScreen !== mode) _isFullScreen = mode
  477. // create a network
  478. if (!_html['container']) {
  479. _html['container'] = document.getElementById(HTML_ID_REF_GRAPH)
  480. }
  481. _html['container'].style.position = (_isFullScreen) ? 'fixed' : 'relative'
  482. _html['container'].style.top = (_isFullScreen) ? '40px' : ''
  483. _html['container'].style.left = '0px'
  484. _html['container'].style.margin = '0'
  485. _html['container'].style.border = (_isFullScreen) ? '0' : '1px solid silver'
  486. _html['container'].style.padding = '0'
  487. _html['container'].style.backgroundColor = '#fff'
  488. _html['container'].style.height = (_isFullScreen) ? ''+(window.innerHeight-40)+'px' : ''+(window.innerHeight-60-_html['container'].offsetTop)+'px'
  489. _html['container'].style.width = (_isFullScreen) ? '' + (window.innerWidth) + 'px' : '100%'
  490. if (!_html['top']) {
  491. _html['top'] = document.createElement('div')
  492. _html['container'].parentNode.insertBefore(_html['top'], _html['container'].nextSibling)
  493. }
  494. _html['top'].style.position = (_isFullScreen) ? 'fixed' : 'relative'
  495. _html['top'].style.top = '0px'
  496. _html['top'].style.left = '0px'
  497. _html['top'].style.margin = '0'
  498. _html['top'].style.border = '0'
  499. _html['top'].style.padding = '0'
  500. _html['top'].style.backgroundColor = '#101010'
  501. _html['top'].style.height = '40px'
  502. _html['top'].style.width = (_isFullScreen) ? '' + (window.innerWidth) + 'px' : '100%'
  503. _html['top'].style.zIndex = 3
  504. if (!_html['fullscreenBtn']) {
  505. _html['fullscreenBtn'] = document.createElement('button')
  506. _html['fullscreenBtn'].className = 'btn btn-lg btn-link'
  507. _html['top'].appendChild(_html['fullscreenBtn'])
  508. _html['fullscreenBtn'].style.color = '#fff'
  509. _html['fullscreenBtn'].style.float = 'right'
  510. _html['fullscreenBtn'].style.outline = 'none'
  511. _html['fullscreenBtn'].addEventListener('click', function () {
  512. _isFullScreen = !_isFullScreen
  513. renderLayout()
  514. })
  515. }
  516. _html['fullscreenBtn'].innerHTML = (_isFullScreen)
  517. ? '<i class="glyphicon glyphicon-resize-small" title="Zamknij pełny ekran"></i>'
  518. : '<i class="glyphicon glyphicon-resize-full" title="Pełny ekran"></i>'
  519. ;
  520. if (!_html['selected']) {
  521. _html['selected'] = document.createElement('span')
  522. var htmlSelectedWrap = document.createElement('div')
  523. htmlSelectedWrap.style.color = '#fff'
  524. htmlSelectedWrap.style.fontSize = '14px'
  525. htmlSelectedWrap.style.lineHeight = '40px'
  526. htmlSelectedWrap.style.paddingLeft = '12px'
  527. htmlSelectedWrap.appendChild(document.createTextNode("Wybrany: "))
  528. htmlSelectedWrap.appendChild(_html['selected'])
  529. _html['top'].appendChild(htmlSelectedWrap)
  530. _html['selected'].innerHTML = 'brak'
  531. }
  532. }
  533. function renderSelectedNode(node) {
  534. var gotToTableLink = (node.typeName)
  535. ? '<a href="index.php?_route=ViewTableAjax&namespace=' + node.typeName.replace('__x3A__', '/').replace(':', '/') + '" class="btn btn-link" style="color:#fff" title="Przejdź do tabeli ' + node.typeName + '">' +
  536. '<i class="glyphicon glyphicon-list-alt"></i>' +
  537. '</a>'
  538. : ''
  539. ;
  540. var editLink = (node.typeName && node.primaryKey)
  541. ? '<a href="index.php?_route=ViewTableAjax&namespace=' + node.typeName.replace('__x3A__', '/').replace(':', '/') + '#EDIT/' + node.primaryKey + '" class="btn btn-link" style="color:#fff" title="Edytuj rekord ' + node.primaryKey + '">' +
  542. '<i class="glyphicon glyphicon-pencil"></i>' +
  543. '</a>'
  544. : ''
  545. ;
  546. _html['selected'].innerHTML = '' + node.id + ' ' + gotToTableLink + ' ' + editLink;
  547. }
  548. function updateSankeyDiagram() {
  549. if(DBG||DBG_SANKEY)console.log('updateSankeyDiagram..');
  550. if (!SHOW_FLOW_DIAGRAM || !sankey) return;
  551. // var sankeyData = { // example
  552. // nodes: [
  553. // /* 0 */ { name: "A" },
  554. // /* 1 */ { name: "B" },
  555. // /* 2 */ { name: "C" },
  556. // /* 3 */ { name: "D" },
  557. // /* 4 */ { name: "E" },
  558. // /* 5 */ { name: "F" },
  559. // ],
  560. // links: [
  561. // { "source": 0, "target": 1, "value": 100 },
  562. // { "source": 1, "target": 2, "value": 100 },
  563. // { "source": 2, "target": 3, "value": 100 },
  564. // { "source": 3, "target": 4, "value": 100 },
  565. // { "source": 5, "target": 2, "value": 100 },
  566. // { "source": 0, "target": 4, "value": 100 },
  567. // ]
  568. // }
  569. p5Utils__clearNode(_sankeySvgNode)
  570. var d3SvgNode = d3.select(_sankeySvgNode);
  571. _sankeyLink = d3SvgNode.append("g")
  572. .attr("class", "links")
  573. .attr("fill", "none")
  574. .attr("stroke", "#000")
  575. .attr("stroke-opacity", 0.2)
  576. .selectAll("path");
  577. _sankeyNode = d3SvgNode.append("g")
  578. .attr("class", "nodes")
  579. .attr("font-family", "sans-serif")
  580. .attr("font-size", 10)
  581. .selectAll("g");
  582. // var sankeyInstance = sankey(sankeyData);
  583. var mapVisNodeIdToIdx = _nodes.getIds().reduce(function (ret, id, idx) {
  584. ret[id] = idx;
  585. return ret;
  586. }, {});
  587. // TODO: fix - count from leaf's to root's
  588. var countOutLinks = _edges.get().reduce(function (ret, edge) {
  589. if (!mapVisNodeIdToIdx.hasOwnProperty(edge.from)) throw "Missing to in mapVisNodeIdToIdx["+edge.from+"]"
  590. var idxTarget = mapVisNodeIdToIdx[edge.from]
  591. ret[idxTarget] = (ret[idxTarget] > 0) ? ret[idxTarget] + 1 : 1
  592. // if(DBG||DBG_SANKEY)console.log('DBG:sankey: countOutLinks reduce', edge, ret);
  593. return ret
  594. }, {})
  595. if(DBG||DBG_SANKEY)console.log('DBG:sankey: countOutLinks', Object.keys(countOutLinks).map(function (idx) {
  596. return '' + _nodes.get()[idx].label + ' ('+countOutLinks[idx]+')';
  597. }));
  598. var sankeyInstance = sankey({
  599. nodes: _nodes.map(function (node) {
  600. return {
  601. name: node.label
  602. }
  603. }),
  604. links: _edges.map(function (edge) {
  605. if (!mapVisNodeIdToIdx.hasOwnProperty(edge.from)) throw "Missing from in mapVisNodeIdToIdx["+edge.from+"]"
  606. if (!mapVisNodeIdToIdx.hasOwnProperty(edge.to)) throw "Missing to in mapVisNodeIdToIdx["+edge.to+"]"
  607. var idxTarget = mapVisNodeIdToIdx[edge.to]
  608. return {
  609. source: mapVisNodeIdToIdx[edge.from],
  610. target: idxTarget,
  611. value: (countOutLinks[idxTarget] || 1) * 10
  612. }
  613. }),
  614. });
  615. // var sankeyInstance = sankey()
  616. // .nodes(
  617. // _nodes.map(function (node) {
  618. // return {
  619. // name: node.label
  620. // }
  621. // })
  622. // )
  623. // .edges(
  624. // _edges.map(function (edge) {
  625. // if (!mapVisNodeIdToIdx.hasOwnProperty(edge.from)) throw "Missing from in mapVisNodeIdToIdx["+edge.from+"]"
  626. // if (!mapVisNodeIdToIdx.hasOwnProperty(edge.to)) throw "Missing to in mapVisNodeIdToIdx["+edge.to+"]"
  627. // var idxTarget = mapVisNodeIdToIdx[edge.to]
  628. // return {
  629. // source: mapVisNodeIdToIdx[edge.from],
  630. // target: idxTarget,
  631. // value: (countOutLinks[idxTarget] || 1) * 10
  632. // }
  633. // })
  634. // )
  635. // .layout(32);
  636. _sankeyLink = _sankeyLink
  637. .data(sankeyInstance.links)
  638. .enter().append("path")
  639. .attr("d", d3.sankeyLinkHorizontal())
  640. .attr("stroke-width", function(d) { return Math.max(1, d.width); });
  641. _sankeyLink.append("title")
  642. .text(function(d) { return d.source.name + " → " + d.target.name + "\n" + _sankeyFormat(d.value); });
  643. _sankeyNode = _sankeyNode
  644. .data(sankeyInstance.nodes)
  645. .enter().append("g");
  646. _sankeyNode.append("rect")
  647. .attr("x", function(d) { return d.x0; })
  648. .attr("y", function(d) { return d.y0; })
  649. .attr("height", function(d) { return d.y1 - d.y0; })
  650. .attr("width", function(d) { return d.x1 - d.x0; })
  651. .attr("fill", function(d) { return _sankeyColor(d.name.replace(/ .*/, "")); })
  652. .attr("stroke", "#000");
  653. _sankeyNode.append("text")
  654. .attr("x", function(d) { return d.x0 - 6; })
  655. .attr("y", function(d) { return (d.y1 + d.y0) / 2; })
  656. .attr("dy", "0.35em")
  657. .attr("text-anchor", "end")
  658. .text(function(d) { return d.name; })
  659. .filter(function(d) { return d.x0 < _sankeySvgWidth / 2; })
  660. .attr("x", function(d) { return d.x1 + 6; })
  661. .attr("text-anchor", "start");
  662. _sankeyNode.append("title")
  663. .text(function(d) { return d.name + "\n" + _sankeyFormat(d.value); });
  664. }
  665. // global['FUNCTION_FETCH_CHILDRENS'] = 'refGraphFetchChildrens'
  666. // global['FUNCTION_FETCH_PARENTS'] = 'refGraphFetchParents'
  667. // global['_nodes'] = _nodes // TODO: DBG
  668. // global['_edges'] = _edges // TODO: DBG