Graph database Analysis of the Steam Network
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

551 lines
13 KiB

  1. ;(function(undefined) {
  2. 'use strict';
  3. /**
  4. * GEXF Library
  5. * =============
  6. *
  7. * Author: PLIQUE Guillaume (Yomguithereal)
  8. * URL: https://github.com/Yomguithereal/gexf-parser
  9. * Version: 0.1.1
  10. */
  11. /**
  12. * Helper Namespace
  13. * -----------------
  14. *
  15. * A useful batch of function dealing with DOM operations and types.
  16. */
  17. var _helpers = {
  18. getModelTags: function(xml) {
  19. var attributesTags = xml.getElementsByTagName('attributes'),
  20. modelTags = {},
  21. l = attributesTags.length,
  22. i;
  23. for (i = 0; i < l; i++)
  24. modelTags[attributesTags[i].getAttribute('class')] =
  25. attributesTags[i].childNodes;
  26. return modelTags;
  27. },
  28. nodeListToArray: function(nodeList) {
  29. // Return array
  30. var children = [];
  31. // Iterating
  32. for (var i = 0, len = nodeList.length; i < len; ++i) {
  33. if (nodeList[i].nodeName !== '#text')
  34. children.push(nodeList[i]);
  35. }
  36. return children;
  37. },
  38. nodeListEach: function(nodeList, func) {
  39. // Iterating
  40. for (var i = 0, len = nodeList.length; i < len; ++i) {
  41. if (nodeList[i].nodeName !== '#text')
  42. func(nodeList[i]);
  43. }
  44. },
  45. nodeListToHash: function(nodeList, filter) {
  46. // Return object
  47. var children = {};
  48. // Iterating
  49. for (var i = 0; i < nodeList.length; i++) {
  50. if (nodeList[i].nodeName !== '#text') {
  51. var prop = filter(nodeList[i]);
  52. children[prop.key] = prop.value;
  53. }
  54. }
  55. return children;
  56. },
  57. namedNodeMapToObject: function(nodeMap) {
  58. // Return object
  59. var attributes = {};
  60. // Iterating
  61. for (var i = 0; i < nodeMap.length; i++) {
  62. attributes[nodeMap[i].name] = nodeMap[i].value;
  63. }
  64. return attributes;
  65. },
  66. getFirstElementByTagNS: function(node, ns, tag) {
  67. var el = node.getElementsByTagName(ns + ':' + tag)[0];
  68. if (!el)
  69. el = node.getElementsByTagNameNS(ns, tag)[0];
  70. if (!el)
  71. el = node.getElementsByTagName(tag)[0];
  72. return el;
  73. },
  74. getAttributeNS: function(node, ns, attribute) {
  75. var attr_value = node.getAttribute(ns + ':' + attribute);
  76. if (attr_value === undefined)
  77. attr_value = node.getAttributeNS(ns, attribute);
  78. if (attr_value === undefined)
  79. attr_value = node.getAttribute(attribute);
  80. return attr_value;
  81. },
  82. enforceType: function(type, value) {
  83. switch (type) {
  84. case 'boolean':
  85. value = (value === 'true');
  86. break;
  87. case 'integer':
  88. case 'long':
  89. case 'float':
  90. case 'double':
  91. value = +value;
  92. break;
  93. case 'liststring':
  94. value = value ? value.split('|') : [];
  95. break;
  96. }
  97. return value;
  98. },
  99. getRGB: function(values) {
  100. return (values[3]) ?
  101. 'rgba(' + values.join(',') + ')' :
  102. 'rgb(' + values.slice(0, -1).join(',') + ')';
  103. }
  104. };
  105. /**
  106. * Parser Core Functions
  107. * ----------------------
  108. *
  109. * The XML parser's functions themselves.
  110. */
  111. /**
  112. * Node structure.
  113. * A function returning an object guarded with default value.
  114. *
  115. * @param {object} properties The node properties.
  116. * @return {object} The guarded node object.
  117. */
  118. function Node(properties) {
  119. // Possible Properties
  120. var node = {
  121. id: properties.id,
  122. label: properties.label
  123. };
  124. if (properties.viz)
  125. node.viz = properties.viz;
  126. if (properties.attributes)
  127. node.attributes = properties.attributes;
  128. return node;
  129. }
  130. /**
  131. * Edge structure.
  132. * A function returning an object guarded with default value.
  133. *
  134. * @param {object} properties The edge properties.
  135. * @return {object} The guarded edge object.
  136. */
  137. function Edge(properties) {
  138. // Possible Properties
  139. var edge = {
  140. id: properties.id,
  141. type: properties.type || 'undirected',
  142. label: properties.label || '',
  143. source: properties.source,
  144. target: properties.target,
  145. weight: +properties.weight || 1.0
  146. };
  147. if (properties.viz)
  148. edge.viz = properties.viz;
  149. if (properties.attributes)
  150. edge.attributes = properties.attributes;
  151. return edge;
  152. }
  153. /**
  154. * Graph parser.
  155. * This structure parse a gexf string and return an object containing the
  156. * parsed graph.
  157. *
  158. * @param {string} xml The xml string of the gexf file to parse.
  159. * @return {object} The parsed graph.
  160. */
  161. function Graph(xml) {
  162. var _xml = {};
  163. // Basic Properties
  164. //------------------
  165. _xml.els = {
  166. root: xml.getElementsByTagName('gexf')[0],
  167. graph: xml.getElementsByTagName('graph')[0],
  168. meta: xml.getElementsByTagName('meta')[0],
  169. nodes: xml.getElementsByTagName('node'),
  170. edges: xml.getElementsByTagName('edge'),
  171. model: _helpers.getModelTags(xml)
  172. };
  173. // Information
  174. _xml.hasViz = !!_helpers.getAttributeNS(_xml.els.root, 'xmlns', 'viz');
  175. _xml.version = _xml.els.root.getAttribute('version') || '1.0';
  176. _xml.mode = _xml.els.graph.getAttribute('mode') || 'static';
  177. var edgeType = _xml.els.graph.getAttribute('defaultedgetype');
  178. _xml.defaultEdgetype = edgeType || 'undirected';
  179. // Parser Functions
  180. //------------------
  181. // Meta Data
  182. function _metaData() {
  183. var metas = {};
  184. if (!_xml.els.meta)
  185. return metas;
  186. // Last modified date
  187. metas.lastmodifieddate = _xml.els.meta.getAttribute('lastmodifieddate');
  188. // Other information
  189. _helpers.nodeListEach(_xml.els.meta.childNodes, function(child) {
  190. metas[child.tagName.toLowerCase()] = child.textContent;
  191. });
  192. return metas;
  193. }
  194. // Model
  195. function _model(cls) {
  196. var attributes = [];
  197. // Iterating through attributes
  198. if (_xml.els.model[cls])
  199. _helpers.nodeListEach(_xml.els.model[cls], function(attr) {
  200. // Properties
  201. var properties = {
  202. id: attr.getAttribute('id') || attr.getAttribute('for'),
  203. type: attr.getAttribute('type') || 'string',
  204. title: attr.getAttribute('title') || ''
  205. };
  206. // Defaults
  207. var default_el = _helpers.nodeListToArray(attr.childNodes);
  208. if (default_el.length > 0)
  209. properties.defaultValue = default_el[0].textContent;
  210. // Creating attribute
  211. attributes.push(properties);
  212. });
  213. return attributes.length > 0 ? attributes : false;
  214. }
  215. // Data from nodes or edges
  216. function _data(model, node_or_edge) {
  217. var data = {};
  218. var attvalues_els = node_or_edge.getElementsByTagName('attvalue');
  219. // Getting Node Indicated Attributes
  220. var ah = _helpers.nodeListToHash(attvalues_els, function(el) {
  221. var attributes = _helpers.namedNodeMapToObject(el.attributes);
  222. var key = attributes.id || attributes['for'];
  223. // Returning object
  224. return {key: key, value: attributes.value};
  225. });
  226. // Iterating through model
  227. model.map(function(a) {
  228. // Default value?
  229. data[a.id] = !(a.id in ah) && 'defaultValue' in a ?
  230. _helpers.enforceType(a.type, a.defaultValue) :
  231. _helpers.enforceType(a.type, ah[a.id]);
  232. });
  233. return data;
  234. }
  235. // Nodes
  236. function _nodes(model) {
  237. var nodes = [];
  238. // Iteration through nodes
  239. _helpers.nodeListEach(_xml.els.nodes, function(n) {
  240. // Basic properties
  241. var properties = {
  242. id: n.getAttribute('id'),
  243. label: n.getAttribute('label') || ''
  244. };
  245. // Retrieving data from nodes if any
  246. if (model)
  247. properties.attributes = _data(model, n);
  248. // Retrieving viz information
  249. if (_xml.hasViz)
  250. properties.viz = _nodeViz(n);
  251. // Pushing node
  252. nodes.push(Node(properties));
  253. });
  254. return nodes;
  255. }
  256. // Viz information from nodes
  257. function _nodeViz(node) {
  258. var viz = {};
  259. // Color
  260. var color_el = _helpers.getFirstElementByTagNS(node, 'viz', 'color');
  261. if (color_el) {
  262. var color = ['r', 'g', 'b', 'a'].map(function(c) {
  263. return color_el.getAttribute(c);
  264. });
  265. viz.color = _helpers.getRGB(color);
  266. }
  267. // Position
  268. var pos_el = _helpers.getFirstElementByTagNS(node, 'viz', 'position');
  269. if (pos_el) {
  270. viz.position = {};
  271. ['x', 'y', 'z'].map(function(p) {
  272. viz.position[p] = +pos_el.getAttribute(p);
  273. });
  274. }
  275. // Size
  276. var size_el = _helpers.getFirstElementByTagNS(node, 'viz', 'size');
  277. if (size_el)
  278. viz.size = +size_el.getAttribute('value');
  279. // Shape
  280. var shape_el = _helpers.getFirstElementByTagNS(node, 'viz', 'shape');
  281. if (shape_el)
  282. viz.shape = shape_el.getAttribute('value');
  283. return viz;
  284. }
  285. // Edges
  286. function _edges(model, default_type) {
  287. var edges = [];
  288. // Iteration through edges
  289. _helpers.nodeListEach(_xml.els.edges, function(e) {
  290. // Creating the edge
  291. var properties = _helpers.namedNodeMapToObject(e.attributes);
  292. if (!('type' in properties)) {
  293. properties.type = default_type;
  294. }
  295. // Retrieving edge data
  296. if (model)
  297. properties.attributes = _data(model, e);
  298. // Retrieving viz information
  299. if (_xml.hasViz)
  300. properties.viz = _edgeViz(e);
  301. edges.push(Edge(properties));
  302. });
  303. return edges;
  304. }
  305. // Viz information from edges
  306. function _edgeViz(edge) {
  307. var viz = {};
  308. // Color
  309. var color_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'color');
  310. if (color_el) {
  311. var color = ['r', 'g', 'b', 'a'].map(function(c) {
  312. return color_el.getAttribute(c);
  313. });
  314. viz.color = _helpers.getRGB(color);
  315. }
  316. // Shape
  317. var shape_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'shape');
  318. if (shape_el)
  319. viz.shape = shape_el.getAttribute('value');
  320. // Thickness
  321. var thick_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'thickness');
  322. if (thick_el)
  323. viz.thickness = +thick_el.getAttribute('value');
  324. return viz;
  325. }
  326. // Returning the Graph
  327. //---------------------
  328. var nodeModel = _model('node'),
  329. edgeModel = _model('edge');
  330. var graph = {
  331. version: _xml.version,
  332. mode: _xml.mode,
  333. defaultEdgeType: _xml.defaultEdgetype,
  334. meta: _metaData(),
  335. model: {},
  336. nodes: _nodes(nodeModel),
  337. edges: _edges(edgeModel, _xml.defaultEdgetype)
  338. };
  339. if (nodeModel)
  340. graph.model.node = nodeModel;
  341. if (edgeModel)
  342. graph.model.edge = edgeModel;
  343. return graph;
  344. }
  345. /**
  346. * Public API
  347. * -----------
  348. *
  349. * User-accessible functions.
  350. */
  351. // Fetching GEXF with XHR
  352. function fetch(gexf_url, callback) {
  353. var xhr = (function() {
  354. if (window.XMLHttpRequest)
  355. return new XMLHttpRequest();
  356. var names,
  357. i;
  358. if (window.ActiveXObject) {
  359. names = [
  360. 'Msxml2.XMLHTTP.6.0',
  361. 'Msxml2.XMLHTTP.3.0',
  362. 'Msxml2.XMLHTTP',
  363. 'Microsoft.XMLHTTP'
  364. ];
  365. for (i in names)
  366. try {
  367. return new ActiveXObject(names[i]);
  368. } catch (e) {}
  369. }
  370. return null;
  371. })();
  372. if (!xhr)
  373. throw 'XMLHttpRequest not supported, cannot load the file.';
  374. // Async?
  375. var async = (typeof callback === 'function'),
  376. getResult;
  377. // If we can't override MIME type, we are on IE 9
  378. // We'll be parsing the response string then.
  379. if (xhr.overrideMimeType) {
  380. xhr.overrideMimeType('text/xml');
  381. getResult = function(r) {
  382. return r.responseXML;
  383. };
  384. }
  385. else {
  386. getResult = function(r) {
  387. var p = new DOMParser();
  388. return p.parseFromString(r.responseText, 'application/xml');
  389. };
  390. }
  391. xhr.open('GET', gexf_url, async);
  392. if (async)
  393. xhr.onreadystatechange = function() {
  394. if (xhr.readyState === 4)
  395. callback(getResult(xhr));
  396. };
  397. xhr.send();
  398. return (async) ? xhr : getResult(xhr);
  399. }
  400. // Parsing the GEXF File
  401. function parse(gexf) {
  402. return Graph(gexf);
  403. }
  404. // Fetch and parse the GEXF File
  405. function fetchAndParse(gexf_url, callback) {
  406. if (typeof callback === 'function') {
  407. return fetch(gexf_url, function(gexf) {
  408. callback(Graph(gexf));
  409. });
  410. } else
  411. return Graph(fetch(gexf_url));
  412. }
  413. /**
  414. * Exporting
  415. * ----------
  416. */
  417. if (typeof this.gexf !== 'undefined')
  418. throw 'gexf: error - a variable called "gexf" already ' +
  419. 'exists in the global scope';
  420. this.gexf = {
  421. // Functions
  422. parse: parse,
  423. fetch: fetchAndParse,
  424. // Version
  425. version: '0.1.1'
  426. };
  427. if (typeof exports !== 'undefined' && this.exports !== exports)
  428. module.exports = this.gexf;
  429. }).call(this);