/app/scripts/components/VizabiPrototype.js

https://gitlab.com/Webxity/vizabi-prototypes · JavaScript · 370 lines · 273 code · 61 blank · 36 comment · 36 complexity · eec09407095d13340f1ef7aeffa7e9f6 MD5 · raw file

  1. (function(window, document) {'use strict';
  2. var noop = angular.noop;
  3. var tools = [
  4. 'bar-chart',
  5. 'bubble-chart',
  6. 'line-chart'
  7. ];
  8. var toolsObject = {
  9. 'bar-chart': 'BarChart',
  10. 'bubble-chart': 'BubbleChart',
  11. 'line-chart': 'LineChart'
  12. };
  13. /**
  14. * Tool decider
  15. * @param tool {String}
  16. * @exception error
  17. */
  18. var tool = function(tool) {
  19. if (tool) {
  20. if (tools.indexOf(tool) === -1) {
  21. error('No built-in tool provider found.');
  22. }
  23. return tool;
  24. }
  25. };
  26. /**
  27. * Throw error
  28. * @param msg {String}
  29. */
  30. var error = function(msg) {
  31. var err = new Error(msg);
  32. return err.stack;
  33. };
  34. /**
  35. * Safe function execution
  36. * @param fn {Function}
  37. */
  38. var safeExec = function(fn) {
  39. fn = fn || function() {};
  40. try {
  41. fn()
  42. } catch (e) {
  43. throw error(e);
  44. }
  45. };
  46. /**
  47. * Todo: Optimize and make it work
  48. * Create custom events
  49. * @param e {Object}
  50. * @param data {Object}
  51. * @returns {CustomEvent|Boolean}
  52. */
  53. var createEvent = function(e, data) {
  54. if (!angular.isObject(e) || !angular.isObject(data)) {
  55. return false;
  56. }
  57. window.addEventListener(e.name, function(e) { console.log(e.detail) });
  58. e.event = new CustomEvent(e.name, {
  59. detail: data
  60. });
  61. window.dispatchEvent(e.event);
  62. };
  63. var switchResponsiveDevices = function() {
  64. document.querySelector('#resize-portrait').addEventListener(
  65. 'click',
  66. function() {
  67. this.setScreenSize(this.PORTRAIT);
  68. }.bind(this));
  69. document.querySelector('#resize-landscape').addEventListener(
  70. 'click',
  71. function() {
  72. this.setScreenSize(this.LANDSCAPE)
  73. }.bind(this));
  74. document.querySelector('#resize-full').addEventListener(
  75. 'click',
  76. function() {
  77. this.setScreenSize(this.FULL);
  78. }.bind(this));
  79. document.querySelector('#enter-fullscreen').addEventListener(
  80. 'click',
  81. function() {
  82. if (!stageEl) {
  83. error('Stage is not yet drawn');
  84. }
  85. this.settings.fullscreen = true;
  86. this.setScreenSize(this.FULL);
  87. if (this.settings.fullscreen === true) {
  88. if (stageEl.requestFullscreen) {
  89. stageEl.requestFullscreen();
  90. } else if (stageEl.msRequestFullscreen) {
  91. stageEl.msRequestFullscreen();
  92. } else if (stageEl.mozRequestFullScreen) {
  93. stageEl.mozRequestFullScreen();
  94. } else if (stageEl.webkitRequestFullscreen) {
  95. stageEl.webkitRequestFullscreen();
  96. }
  97. }
  98. }.bind(this));
  99. var langEl = document.querySelector('#change-lang');
  100. langEl.addEventListener(
  101. 'click',
  102. function() {
  103. var title = langEl.getAttribute('data-original-title');
  104. var langIndex = this.langs.indexOf(title.toLowerCase());
  105. if (langIndex !== -1) {
  106. this.lang = (this.lang === 'pt') ? 'en' : 'pt';
  107. langEl.setAttribute(
  108. 'data-original-title',
  109. this.langs[langIndex].toUpperCase()
  110. );
  111. this.visualize();
  112. }
  113. }.bind(this));
  114. };
  115. var listenWindowResize = function() {
  116. window.addEventListener('resize', function(e) {
  117. if (this.screenType === this.PORTRAIT
  118. || this.screenType === this.LANDSCAPE) {
  119. return false;
  120. }
  121. // Todo: Uncomment when fix
  122. //this.newEvent(this.eventTypes.RESIZE, e);
  123. var fullScreen = document.mozFullscreenElement
  124. || document.webkitFullscreenElement
  125. || document.msFullscreenElement
  126. || document.fullscreenElement;
  127. if (!fullScreen) {
  128. this.settings.fullscreen = false;
  129. }
  130. this.setScreenSize(this.FULL);
  131. }.bind(this));
  132. };
  133. // Private var for short instance
  134. var VP;
  135. var htmlID;
  136. var containerID;
  137. var containerEl;
  138. var stageID;
  139. var stageEl;
  140. /**
  141. * VizabiPrototype constructor object
  142. * @param tool {String} bar-chart/bubble-chart/line-chart
  143. * @param id {String} html id attribute
  144. * @param options {Object}
  145. * @constructor
  146. */
  147. window.VizabiPrototype = VP = function VizabiPrototype(tool, id, options) {
  148. this.PORTRAIT = 'portrait';
  149. this.LANDSCAPE = 'landscape';
  150. this.FULL = 'full';
  151. this.tool = tool;
  152. this.htmlID = htmlID = id;
  153. this.sliderID = '#slider';
  154. this.optionsID = '#options';
  155. this.options = options;
  156. this.stateObj = {};
  157. this.screenType = 'portrait';
  158. this.screenSize = this.screenSizes.portrait;
  159. this.lang = options.language.id || 'en';
  160. this.langs = ['en', 'pt'];
  161. var eventPrefix = 'vp:';
  162. this.events = {
  163. RESIZE: []
  164. };
  165. this.eventTypes = {
  166. RESIZE: {
  167. name: eventPrefix + 'resize',
  168. event: noop
  169. }
  170. };
  171. switchResponsiveDevices.call(this);
  172. listenWindowResize.call(this);
  173. this.visualize();
  174. };
  175. VP.prototype.render = function() {
  176. tool(this.tool);
  177. //Todo: Find a proper place for this code to execute. Reason is because of Async call by Ajax causes delay in constructor.
  178. this.containerID = containerID = '#vp-bar-main';
  179. this.stageID = stageID = 'div#vp-bar-stage';
  180. this.containerEl = containerEl = document.querySelector(containerID);
  181. this.stageEl = stageEl = document.querySelector(stageID);
  182. this.stageEl.style.width = this.screenSize.width + 'px';
  183. this.stageEl.style.height = this.screenSize.height + 'px';
  184. this[toolsObject[this.tool]]();
  185. /*safeExec(function() {
  186. ;
  187. }.bind(this));*/
  188. return this;
  189. };
  190. VP.prototype.newEvent = function(name, data) {
  191. createEvent.apply(null, arguments);
  192. };
  193. VP.prototype.visualize = function() {
  194. async.waterfall([
  195. function(fn) {
  196. this.queryData(fn);
  197. }.bind(this),
  198. function(data, fn) {
  199. this.dataset = data;
  200. fn(null);
  201. }.bind(this)
  202. ], function(err) {
  203. if (err) {
  204. return error(err);
  205. }
  206. this.render();
  207. }.bind(this));
  208. return this;
  209. };
  210. VP.prototype.queryData = function(fn) {
  211. var options = this.options;
  212. var path = 'data/' + this.tool + '/query-en.json';
  213. var lang = this.lang;
  214. if (options.data.reader !== 'local-json') {
  215. // remote server
  216. } else {
  217. path = options.data.path;
  218. var ext = '-'+lang+'.json';
  219. if (path.indexOf(ext) !== -1) {
  220. path = path.replace(ext, '');
  221. }
  222. path = 'data/' + this.tool + '/' + path + ext;
  223. }
  224. d3.json(path, function(error, json) {
  225. if (error) return console.warn(error);
  226. fn.apply(null, arguments);
  227. }.bind(this));
  228. };
  229. VP.prototype.destroy = function() {
  230. var svgNode = document.querySelector(this.htmlID);
  231. var sliderNode = document.querySelector(this.sliderID);
  232. while (svgNode && svgNode.hasChildNodes()) {
  233. svgNode.removeChild(svgNode.lastChild);
  234. }
  235. while (sliderNode && sliderNode.hasChildNodes()) {
  236. sliderNode.removeChild(sliderNode.lastChild);
  237. }
  238. return this;
  239. };
  240. VP.prototype.reRender = function() {
  241. return this.destroy().render();
  242. };
  243. //Changes the OPTIONS sent to constructor obtained from textArea.
  244. VP.prototype.stateChanger = function (defaultObj){
  245. if (JSON.parse(document.getElementById("stateText").value != '')){
  246. try{
  247. this.stateObj = JSON.parse(document.getElementById("stateText").value);
  248. this.options = this.stateObj;
  249. this.reRender();
  250. }
  251. catch(e)
  252. {
  253. alert('Please Enter Valid JSON: ' + e);
  254. }
  255. }
  256. else{
  257. alert('No input detected, using default options');
  258. this.options = defaultObj;
  259. this.reRender();
  260. }
  261. };
  262. VP.prototype.dataset = [];
  263. VP.prototype.settings = {
  264. fullscreen: false
  265. };
  266. VP.prototype.screenSizes = {
  267. portrait: {
  268. width: 320,
  269. height: 512
  270. },
  271. landscape: {
  272. width: 568,
  273. height: 320
  274. },
  275. full: {}
  276. };
  277. Object.defineProperty(VP.prototype.screenSizes.full, 'width', {
  278. get: function() {
  279. var width = containerEl.offsetWidth;
  280. if (VP.prototype.settings.fullscreen === true) {
  281. width -= 70;
  282. }
  283. return width;
  284. },
  285. set: function(v) {
  286. return v;
  287. }
  288. });
  289. Object.defineProperty(VP.prototype.screenSizes.full, 'height', {
  290. get: function() {
  291. var c = VP.prototype.settings.fullscreen === true ? stageEl : containerEl;
  292. return c.offsetHeight - 110;
  293. },
  294. set: function(v) {
  295. return v;
  296. }
  297. });
  298. VP.prototype.setScreenSize = function(type) {
  299. var size = this.screenSizes[this.PORTRAIT];
  300. if (type && this.screenSizes[type]) {
  301. size = this.screenSizes[type];
  302. }
  303. this.screenSize = size;
  304. this.screenType = type;
  305. stageEl.style.width = size.width + 'px';
  306. stageEl.style.height = size.height + 'px';
  307. this.reRender();
  308. };
  309. })(window, document);