ko.charts.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. // Licensed to Cloudera, Inc. under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. Cloudera, Inc. licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. ko.HUE_CHARTS = {
  17. TYPES: {
  18. LINECHART: "lines",
  19. BARCHART: "bars",
  20. POINTCHART: "points",
  21. PIECHART: "pie",
  22. MAP: "map"
  23. }
  24. };
  25. ko.bindingHandlers.pieChart = {
  26. update: function (element, valueAccessor) {
  27. var _options = valueAccessor();
  28. var _data = _options.transformer(_options.data);
  29. $(element).css("marginLeft", "auto");
  30. $(element).css("marginRight", "auto");
  31. if (typeof _options.maxWidth != "undefined") {
  32. var _max = _options.maxWidth * 1;
  33. $(element).width(Math.min($(element).parent().width(), _max));
  34. }
  35. if ($(element).find('svg').length > 0 && _data.length == 0) {
  36. $(element).find('svg').empty();
  37. }
  38. if (_data.length > 0 && isNaN(_data[0].value)) {
  39. _data = [];
  40. $(element).find('svg').empty();
  41. }
  42. if ($(element).is(":visible")) {
  43. nv.addGraph(function () {
  44. var _chart = nv.models.growingPieChart()
  45. .x(function (d) {
  46. return d.label
  47. })
  48. .y(function (d) {
  49. return d.value
  50. })
  51. .height($(element).width())
  52. .showLabels(true).showLegend(false);
  53. var _d3 = ($(element).find('svg').length > 0) ? d3.select($(element).find('svg')[0]) : d3.select($(element)[0]).append('svg');
  54. _d3.datum(_data)
  55. .transition().duration(150)
  56. .each("end", _options.onComplete != null ? _options.onComplete : void(0))
  57. .call(_chart);
  58. if (_options.fqs) {
  59. $.each(_options.fqs(), function (cnt, item) {
  60. if (item.field() == _options.field()) {
  61. _chart.selectSlices(item.filter());
  62. }
  63. });
  64. }
  65. nv.utils.windowResize(_chart.update);
  66. $(element).height($(element).width());
  67. $(element).parents(".card-widget").on("resize", function () {
  68. if (typeof _options.maxWidth != "undefined") {
  69. var _max = _options.maxWidth * 1;
  70. $(element).width(Math.min($(element).parent().width(), _max));
  71. }
  72. $(element).height($(element).width());
  73. _chart.update();
  74. });
  75. return _chart;
  76. }, function () {
  77. var _d3 = ($(element).find('svg').length > 0) ? d3.select($(element).find('svg')[0]) : d3.select($(element)[0]).append('svg');
  78. _d3.selectAll(".nv-slice").on('click',
  79. function (d, i) {
  80. if (typeof _options.onClick != "undefined") {
  81. chartsUpdatingState();
  82. _options.onClick(d);
  83. }
  84. });
  85. });
  86. }
  87. }
  88. };
  89. ko.bindingHandlers.barChart = {
  90. update: function (element, valueAccessor) {
  91. barChartBuilder(element, valueAccessor(), false);
  92. }
  93. };
  94. ko.bindingHandlers.timelineChart = {
  95. update: function (element, valueAccessor) {
  96. barChartBuilder(element, valueAccessor(), true);
  97. }
  98. };
  99. ko.bindingHandlers.lineChart = {
  100. update: function (element, valueAccessor) {
  101. lineChartBuilder(element, valueAccessor());
  102. }
  103. };
  104. ko.bindingHandlers.leafletMapChart = {
  105. update: function (element, valueAccessor) {
  106. var _options = valueAccessor();
  107. var _data = _options.transformer(valueAccessor().datum);
  108. function getMapBounds(lats, lngs) {
  109. lats = lats.sort();
  110. lngs = lngs.sort();
  111. return [
  112. [lats[lats.length - 1], lngs[lngs.length - 1]], // north-east
  113. [lats[0], lngs[0]] // south-west
  114. ]
  115. }
  116. if ($(element).data("map") != null) {
  117. try {
  118. $(element).data("map").remove();
  119. }
  120. catch (err) {
  121. if (typeof console != "undefined") {
  122. console.error(err);
  123. }
  124. }
  125. }
  126. var _lats = [];
  127. _data.forEach(function (item) {
  128. if (item.lat != null && $.isNumeric(item.lat)) {
  129. _lats.push(item.lat);
  130. }
  131. });
  132. var _lngs = [];
  133. _data.forEach(function (item) {
  134. if (item.lng != null && $.isNumeric(item.lng)) {
  135. _lngs.push(item.lng);
  136. }
  137. });
  138. if ($(element).parents(".tab-pane").length > 0){
  139. $(element).height($(element).parents(".tab-pane").height() - 100);
  140. }
  141. else {
  142. $(element).height(300);
  143. }
  144. if (((_options.visible != null && _options.visible) || _options.visible == null || typeof _options == "undefined") && _data.length > 0) {
  145. $(element).show();
  146. }
  147. else {
  148. $(element).hide();
  149. }
  150. var _map = null;
  151. if (element._map != null) {
  152. element._leaflet = false;
  153. element._map.remove();
  154. }
  155. if (_lats.length > 0 && _lngs.length > 0) {
  156. try {
  157. if (_map == null) {
  158. _map = L.map(element);
  159. L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
  160. attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributorss'
  161. }).addTo(_map);
  162. }
  163. _map.fitBounds(getMapBounds(_lats, _lngs));
  164. _data.forEach(function (item) {
  165. if (item.lng != null && item.lat != null) {
  166. var _addMarker = false;
  167. try {
  168. var _latLngObj = L.latLng(item.lat, item.lng);
  169. _addMarker = true;
  170. }
  171. catch (e){
  172. if (typeof console != "undefined") {
  173. console.error(e);
  174. }
  175. }
  176. if (_addMarker){
  177. var _marker = L.marker([item.lat, item.lng]).addTo(_map);
  178. if (item.label != null) {
  179. _marker.bindPopup(item.label);
  180. }
  181. }
  182. }
  183. });
  184. if (_options.onComplete != null) {
  185. _options.onComplete();
  186. }
  187. }
  188. catch (err) {
  189. $.jHueNotify.error(err.message);
  190. }
  191. }
  192. element._map = _map;
  193. }
  194. };
  195. ko.bindingHandlers.mapChart = {
  196. render: function (element, valueAccessor) {
  197. var _options = valueAccessor();
  198. $(element).empty();
  199. $(element).css("position", "relative");
  200. $(element).css("marginLeft", "auto");
  201. $(element).css("marginRight", "auto");
  202. if (typeof _options.maxWidth != "undefined") {
  203. var _max = _options.maxWidth * 1;
  204. $(element).width(Math.min($(element).parent().width(), _max));
  205. }
  206. $(element).height($(element).width() / 2.23);
  207. var _scope = typeof _options.data.scope != "undefined" ? String(_options.data.scope) : "world";
  208. var _data = _options.transformer(_options.data);
  209. var _maxWeight = 0;
  210. $(_data).each(function (cnt, item) {
  211. if (item.value > _maxWeight) _maxWeight = item.value;
  212. });
  213. var _chunk = _maxWeight / _data.length;
  214. var _mapdata = {};
  215. var _maphovers = {};
  216. var _fills = {};
  217. var _noncountries = [];
  218. if (_options.isScale) {
  219. _fills["defaultFill"] = HueColors.WHITE;
  220. var _colors = HueColors.scale(HueColors.LIGHT_BLUE, HueColors.DARK_BLUE, _data.length);
  221. $(_colors).each(function (cnt, item) {
  222. _fills["fill_" + cnt] = item;
  223. });
  224. $(_data).each(function (cnt, item) {
  225. var _place = item.label.toUpperCase();
  226. if (_place != null) {
  227. _mapdata[_place] = {
  228. fillKey: "fill_" + (Math.floor(item.value / _chunk) - 1),
  229. id: _place,
  230. cat: item.obj.cat,
  231. value: item.obj.value,
  232. selected: item.obj.selected
  233. };
  234. _maphovers[_place] = item.value;
  235. }
  236. else {
  237. _noncountries.push(item);
  238. }
  239. });
  240. }
  241. else {
  242. _fills["defaultFill"] = HueColors.LIGHT_BLUE;
  243. _fills["selected"] = HueColors.DARK_BLUE;
  244. $(_data).each(function (cnt, item) {
  245. var _place = item.label.toUpperCase();
  246. if (_place != null) {
  247. _mapdata[_place] = {
  248. fillKey: "selected",
  249. id: _place,
  250. cat: item.obj.cat,
  251. value: item.obj.value,
  252. selected: item.obj.selected
  253. };
  254. _maphovers[_place] = item.value;
  255. }
  256. else {
  257. _noncountries.push(item);
  258. }
  259. });
  260. }
  261. var _map = null;
  262. function createDatamap(element, options, fills, mapData, nonCountries, mapHovers) {
  263. _map = new Datamap({
  264. element: element,
  265. fills: fills,
  266. scope: _scope,
  267. data: mapData,
  268. onClick: function (data) {
  269. if (typeof options.onClick != "undefined") {
  270. chartsUpdatingState();
  271. options.onClick(data);
  272. }
  273. },
  274. done: function (datamap) {
  275. var _bubbles = [];
  276. if (options.enableGeocoding) {
  277. $(nonCountries).each(function (cnt, item) {
  278. HueGeo.getCityCoordinates(item.label, function (lat, lng) {
  279. _bubbles.push({
  280. fillKey: "selected",
  281. label: item.label,
  282. value: item.value,
  283. radius: 4,
  284. latitude: lat,
  285. longitude: lng
  286. });
  287. _map.bubbles(_bubbles, {
  288. popupTemplate: function (geo, data) {
  289. return '<div class="hoverinfo" style="text-align: center"><strong>' + data.label + '</strong><br/>' + item.value + '</div>'
  290. }
  291. });
  292. });
  293. });
  294. }
  295. },
  296. geographyConfig: {
  297. hideAntarctica: true,
  298. borderWidth: 1,
  299. borderColor: HueColors.DARK_BLUE,
  300. highlightOnHover: true,
  301. highlightFillColor: HueColors.DARK_BLUE,
  302. highlightBorderColor: HueColors.BLUE,
  303. selectedFillColor: HueColors.DARKER_BLUE,
  304. selectedBorderColor: HueColors.DARKER_BLUE,
  305. popupTemplate: function (geography, data) {
  306. var _hover = '';
  307. if (data != null) {
  308. _hover = '<br/>' + mapHovers[data.id];
  309. }
  310. return '<div class="hoverinfo" style="text-align: center"><strong>' + geography.properties.name + '</strong>' + _hover + '</div>'
  311. }
  312. }
  313. });
  314. if (options.onComplete != null) {
  315. options.onComplete();
  316. }
  317. }
  318. createDatamap(element, _options, _fills, _mapdata, _noncountries, _maphovers)
  319. $(element).parents(".card-widget").one("resize", function () {
  320. ko.bindingHandlers.mapChart.render(element, valueAccessor);
  321. });
  322. },
  323. init: function (element, valueAccessor) {
  324. ko.bindingHandlers.mapChart.render(element, valueAccessor)
  325. },
  326. update: function (element, valueAccessor) {
  327. ko.bindingHandlers.mapChart.render(element, valueAccessor);
  328. }
  329. };
  330. function lineChartBuilder(element, options) {
  331. var _datum = options.transformer(options.datum);
  332. $(element).height(300);
  333. if ($(element).find('svg').length > 0 && (_datum.length == 0 || _datum[0].values.length == 0)) {
  334. $(element).find('svg').empty();
  335. }
  336. if (_datum.length > 0 && _datum[0].values.length > 0 && (isNaN(_datum[0].values[0].x) || isNaN(_datum[0].values[0].y))) {
  337. _datum = [];
  338. $(element).find('svg').empty();
  339. }
  340. if ($(element).is(":visible")) {
  341. nv.addGraph(function () {
  342. var _chart = nv.models.lineWithBrushChart();
  343. if (_datum.length > 0 && _datum[0].values.length > 10) {
  344. _chart.enableSelection();
  345. }
  346. if (options.showControls != null) {
  347. _chart.showControls(false);
  348. }
  349. _chart.onSelectRange(function (from, to) {
  350. chartsUpdatingState();
  351. options.onSelectRange(from, to);
  352. });
  353. _chart.xAxis.showMaxMin(false);
  354. _chart.yAxis
  355. .tickFormat(d3.format(',0f'));
  356. var _d3 = ($(element).find('svg').length > 0) ? d3.select($(element).find('svg')[0]) : d3.select($(element)[0]).append('svg');
  357. _d3.datum(_datum)
  358. .transition().duration(150)
  359. .each("end", options.onComplete != null ? options.onComplete : void(0))
  360. .call(_chart);
  361. nv.utils.windowResize(_chart.update);
  362. return _chart;
  363. }, function () {
  364. var _d3 = ($(element).find('svg').length > 0) ? d3.select($(element).find('svg')[0]) : d3.select($(element)[0]).append('svg');
  365. _d3.selectAll(".nv-line").on('click',
  366. function (d, i) {
  367. if (typeof options.onClick != "undefined") {
  368. chartsUpdatingState();
  369. options.onClick(d);
  370. }
  371. });
  372. });
  373. }
  374. }
  375. function barChartBuilder(element, options, isTimeline) {
  376. var _datum = options.transformer(options.datum);
  377. $(element).height(300);
  378. var _isPivot = options.isPivot != null ? options.isPivot : false;
  379. if ($(element).find('svg').length > 0 && (_datum.length == 0 || _datum[0].values.length == 0)) {
  380. $(element).find('svg').empty();
  381. }
  382. if (_datum.length > 0 && _datum[0].values.length > 0 && isNaN(_datum[0].values[0].y)) {
  383. _datum = [];
  384. $(element).find('svg').empty();
  385. }
  386. if ($(element).is(":visible")) {
  387. nv.addGraph(function () {
  388. var _chart;
  389. var insertLinebreaks = function (d) {
  390. var _el = d3.select(this);
  391. var _mom = moment(d);
  392. if (_mom != null && _mom.isValid()) {
  393. var _words = _mom.format("hh:mm:ss YYYY-MM-DD").split(" ");
  394. _el.text('');
  395. for (var i = 0; i < _words.length; i++) {
  396. var tspan = _el.append("tspan").text(_words[i]);
  397. if (i > 0) {
  398. tspan.attr("x", 0).attr("dy", '15');
  399. }
  400. }
  401. }
  402. };
  403. if (isTimeline) {
  404. if ($(element).find('svg').length > 0 && $(element).find('.nv-discreteBarWithAxes').length > 0) {
  405. $(element).find('svg').empty();
  406. }
  407. _chart = nv.models.multiBarWithBrushChart();
  408. if (_datum.length > 0 && _datum[0].values.length > 10) {
  409. _chart.enableSelection();
  410. }
  411. _chart.onSelectRange(function (from, to) {
  412. chartsUpdatingState();
  413. options.onSelectRange(from, to);
  414. });
  415. _chart.xAxis.tickFormat(d3.time.format("%Y-%m-%d %H:%M:%S"));
  416. _chart.multibar.hideable(true);
  417. _chart.multibar.stacked(typeof options.stacked != "undefined" ? options.stacked : false);
  418. _chart.onStateChange(options.onStateChange);
  419. _chart.onChartUpdate(function () {
  420. _d3.selectAll("g.nv-x.nv-axis g text").each(insertLinebreaks);
  421. });
  422. }
  423. else {
  424. var _isDiscrete = false;
  425. for (var j = 0; j < _datum.length; j++) {
  426. for (var i = 0; i < _datum[j].values.length; i++) {
  427. if (isNaN(_datum[j].values[i].x * 1)) {
  428. _isDiscrete = true;
  429. break;
  430. }
  431. }
  432. }
  433. if (_isDiscrete && ! _isPivot) {
  434. if ($(element).find('svg').length > 0 && $(element).find('.nv-multiBarWithLegend').length > 0) {
  435. $(element).find('svg').empty();
  436. }
  437. _chart = nv.models.growingDiscreteBarChart()
  438. .x(function (d) {
  439. return d.x
  440. })
  441. .y(function (d) {
  442. return d.y
  443. })
  444. .staggerLabels(true);
  445. }
  446. else {
  447. if ($(element).find('svg').length > 0 && $(element).find('.nv-discreteBarWithAxes').length > 0) {
  448. $(element).find('svg').empty();
  449. }
  450. _chart = nv.models.multiBarWithBrushChart();
  451. if (_datum.length > 0 && _datum[0].values.length > 10) {
  452. _chart.enableSelection();
  453. }
  454. if (_isPivot){
  455. _chart.hideSelection();
  456. }
  457. else {
  458. _chart.xAxis.showMaxMin(false).tickFormat(d3.format(',0f'));
  459. }
  460. _chart.multibar.hideable(true);
  461. _chart.multibar.stacked(typeof options.stacked != "undefined" ? options.stacked : false);
  462. _chart.onStateChange(options.onStateChange);
  463. _chart.onSelectRange(function (from, to) {
  464. chartsUpdatingState();
  465. options.onSelectRange(from, to);
  466. });
  467. }
  468. }
  469. _chart.transitionDuration(0);
  470. _chart.yAxis
  471. .tickFormat(d3.format(',0f'));
  472. var _d3 = ($(element).find('svg').length > 0) ? d3.select($(element).find('svg')[0]) : d3.select($(element)[0]).append('svg');
  473. _d3.datum(_datum)
  474. .transition().duration(150)
  475. .each("end", function () {
  476. if (options.onComplete != null) {
  477. options.onComplete();
  478. }
  479. if (isTimeline) {
  480. _d3.selectAll("g.nv-x.nv-axis g text").each(insertLinebreaks);
  481. }
  482. }).call(_chart);
  483. $.each(options.fqs(), function (cnt, item) {
  484. if (item.field() == options.field) {
  485. _chart.selectBars(item.filter());
  486. }
  487. });
  488. nv.utils.windowResize(_chart.update);
  489. return _chart;
  490. }, function () {
  491. var _d3 = ($(element).find('svg').length > 0) ? d3.select($(element).find('svg')[0]) : d3.select($(element)[0]).append('svg');
  492. _d3.selectAll(".nv-bar").on("click",
  493. function (d, i) {
  494. if (typeof options.onClick != "undefined") {
  495. chartsUpdatingState();
  496. options.onClick(d);
  497. }
  498. });
  499. });
  500. }
  501. }
  502. function chartsUpdatingState() {
  503. $(document).find("svg").css("opacity", "0.5");
  504. }