InfographicView.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2019, 2020
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['jquery', 'underscore', '../VisView', 'text!./Infographic.html', '../../../util/DashboardFormatter', '../../../widgets/livewidget/nls/StringResources', '../../../lib/@waca/dashboard-common/dist/utils/ScaleUtil', './InfographicScaleView', '../../../lib/@waca/dashboard-common/dist/ui/AuthoringToolbar', '../../../lib/@waca/core-client/js/core-client/utils/Utils', '../../../lib/@waca/core-client/js/core-client/utils/BrowserUtils', '../../../lib/@waca/dashboard-common/dist/utils/EventChainLocal', '../VisEventHandler', './InfographicEventTarget', '../../../DynamicFileLoader'], function ($, _, VisView, InfographicTemplate, Formatter, stringResources, ScaleUtil, InfographicScaleView, Toolbar, Utils, BrowserUtils, EventChainLocal, VisEventHandler, InfographicEventTarget, DynamicFileLoader) {
  8. 'use strict';
  9. // Offset used with size of shapes calculations
  10. var OFFSET = 1;
  11. /**
  12. * The InfographicView renders one or more aggregated facts
  13. * eg: Total Revenue for the currently selected filter values.
  14. */
  15. var InfographicView = VisView.extend({
  16. className: 'dataview infographic-widget',
  17. templateString: InfographicTemplate,
  18. events: {
  19. 'primaryaction .infographic-scale-wrapper': 'infographicToolbar'
  20. },
  21. /**
  22. * Initialize the view and its handlers, then render.
  23. */
  24. init: function init() {
  25. InfographicView.inherited('init', this, arguments);
  26. this.animationType = this.ANIMATION_TYPES.NONE;
  27. this.toolbarShown = false;
  28. this.isPercent = false;
  29. this.isFirstRender = true;
  30. this.visModel.on('change:theme', this.onChangeTheme, this);
  31. this._colorsService = this.dashboardApi.getFeature('Colors');
  32. this._translationService = this.dashboardApi.getDashboardCoreSvc('TranslationService');
  33. this._showTranslationIcon = false;
  34. },
  35. // @override
  36. remove: function remove() {
  37. if (this.eventHandler) {
  38. this.eventHandler.remove();
  39. this.eventHandler = null;
  40. }
  41. if (this.visModel) {
  42. this.visModel.off('change:theme', this.onChangeTheme, this);
  43. }
  44. this.visControl = null;
  45. InfographicView.inherited('remove', this, arguments);
  46. },
  47. getRenderer: function getRenderer() {
  48. return 'gemini/dashboard/visualizations/renderer/infographic/InfographicRenderer';
  49. },
  50. /**
  51. * Infographic view overwrites the default animation in order to avoid it fading in/out when
  52. * applying filters.
  53. */
  54. //@Override
  55. animate: function animate() {
  56. // Rendering is complete, no animation yet
  57. this.visModel.renderCompleteBeforeAnimation();
  58. },
  59. /**
  60. * Loads and creates the control. Returns a promise which is resolved when the control
  61. * is created and ready to render
  62. */
  63. //TODO: Remove function
  64. whenVisControlReady: function whenVisControlReady() {
  65. var _this = this;
  66. if (this.visControl) {
  67. return Promise.resolve(this.visControl);
  68. } else {
  69. return DynamicFileLoader.load(['dashboard-analytics/visualizations/renderer/infographic/control/InfographicControl']).then(function (modules) {
  70. var VisControl = modules[0];
  71. _this.visControl = new VisControl({
  72. domNode: _this.$el.find('.infographic-widget-content')[0]
  73. });
  74. //TODO: Retain event target
  75. _this.eventHandler = new VisEventHandler({
  76. target: new InfographicEventTarget({
  77. $el: _this.$el,
  78. visControl: _this.visControl,
  79. visAPI: _this.visModel,
  80. view: _this
  81. }),
  82. transaction: _this.transactionApi,
  83. ownerWidget: _this.ownerWidget,
  84. visAPI: _this.visModel,
  85. edgeSelection: true
  86. });
  87. return _this.visControl;
  88. });
  89. }
  90. },
  91. _readyToRender: function _readyToRender() {
  92. //to cover the case where the slot is changed to an infographic and need a shape
  93. return this.isMappingComplete() && !this.hasMissingFilters() && !this.hasUnavailableMetadataColumns();
  94. },
  95. /**
  96. * render the results.
  97. * If results are not available, fetch them first.
  98. * @param {Object} renderInfo - renderInfo passed to all render methods from the render sequence.
  99. * @returns a promise which is resolved when the render is complete.
  100. */
  101. render: function render(renderInfo) {
  102. this.graphicFillColor = this.content.getPropertyValue('value.graphic.fillColor');
  103. this.graphicCurrentScaleOption = this.content.getPropertyValue('value.graphic.currentScaleOption');
  104. this.graphicContent = this.content.getPropertyValue('value.graphic.content');
  105. var doFillAnimation = !BrowserUtils.isIE();
  106. if (!this._readyToRender()) {
  107. this.resizeToWidget(renderInfo);
  108. this.renderIconView();
  109. return Promise.resolve(this);
  110. }
  111. this.removeIconView();
  112. this.resizeToWidget(renderInfo);
  113. var html = this.buildHTML(renderInfo.data.getResult(), doFillAnimation);
  114. this.addToDom(html);
  115. this.applyVisModelProperties();
  116. Utils.embedSVGIcon(this.$el); // patch disappearing SVGs in win10 IE11
  117. this.updateTranslationIcon();
  118. return InfographicView.inherited('render', this, arguments);
  119. },
  120. /**
  121. * Shows icon of the chart type and hide the rave output area whose role is application
  122. * @override VisView._renderIconView
  123. */
  124. _renderIconView: function _renderIconView() {
  125. if (this.$el) {
  126. this.$el.closest('.infographic-widget').empty().append(this.$iconView);
  127. }
  128. this.visModel.renderCompleteBeforeAnimation();
  129. this.visModel.renderComplete();
  130. },
  131. summaryReveal: function summaryReveal() {
  132. this.visModel.renderCompleteBeforeAnimation();
  133. if (!this._readyToRender()) {
  134. return Promise.resolve();
  135. }
  136. var animateClass = this._fillVertically() ? 'fillVertical' : 'fillHorizontal';
  137. var reflowShape = function reflowShape(el) {
  138. el.classList.remove(animateClass);
  139. void el.clientWidth; // forces repaint which triggers fill animation
  140. el.classList.add(animateClass);
  141. };
  142. this.$el.find('.animate').each(function (i, el) {
  143. reflowShape(el);
  144. });
  145. return Promise.resolve();
  146. },
  147. /**
  148. * @override VisView.removeIconView
  149. * */
  150. removeIconView: function removeIconView() {
  151. if (this.$iconView && this.$el) {
  152. this.$el.find('div[class="value"]').addClass('hide');
  153. this.$el.find('div[class="label"]').addClass('hide');
  154. this.$iconView.remove();
  155. this.$iconView = null;
  156. }
  157. },
  158. getDescription: function getDescription() {
  159. var description;
  160. if (this.infographicInfo) {
  161. var label = this.getLabel();
  162. var infographic = stringResources.get('infographicLabel', {
  163. label: this.infographicInfo.label,
  164. value: this.infographicInfo.v
  165. });
  166. description = stringResources.get('WidgetLabelWithDescripion', {
  167. label: label,
  168. description: infographic
  169. });
  170. } else {
  171. description = InfographicView.inherited('getDescription', this, arguments);
  172. }
  173. return description;
  174. },
  175. /**
  176. * Creates and displays toolbar for updating/changing scale options, with inner content
  177. * @param event - event that caused this function to be triggered. Contains target, which is used to determine where to place toolbar
  178. * @returns promise resolved when the toolbar is rendered and displayed to user
  179. */
  180. infographicToolbar: function infographicToolbar(event) {
  181. // Prevent widget ODT from displaying/updating
  182. new EventChainLocal(event).setProperty('preventDefaultContextBar', true);
  183. if (this.graphicContent && !this.toolbarShown) {
  184. var toolbar = new Toolbar({
  185. container: $('body'),
  186. placement: 'auto bottom',
  187. calculateBoundingRect: true
  188. });
  189. var infoScale = new InfographicScaleView({
  190. content: this.content,
  191. visModel: this.visModel,
  192. widget: this.visModel.ownerWidget,
  193. widgetValue: this.infographicValue,
  194. isPercent: this.isPercent,
  195. currentScaleOption: this.graphicCurrentScaleOption
  196. });
  197. toolbar.addItems([{
  198. responsive: false,
  199. editable: false,
  200. changedAction: null,
  201. subView: infoScale,
  202. type: 'SubView'
  203. }]);
  204. this.visModel.ownerWidget.on('expandedView:visible', this.onShowExpandedContent.bind(this, toolbar));
  205. this.$el.closest('.page.pageabsolute, .page.pagecontainer').on('scroll.BaseController', this.scaleToolbarOnScroll.bind(this, toolbar));
  206. this.toolbarShown = true;
  207. // Checks when the update scale toolbar is closed
  208. toolbar.on('toolbar:hide', this.onToolbarClosed, this);
  209. toolbar.on('toolbar:show', function () {
  210. infoScale.setFocus();
  211. });
  212. toolbar.setSelectionContext([event.target]);
  213. return toolbar.show();
  214. } else {
  215. return undefined;
  216. }
  217. },
  218. /**
  219. * Event handler called when toolbar is closed
  220. * @returns Sets that no toolbar exists
  221. */
  222. onToolbarClosed: function onToolbarClosed() {
  223. this.toolbarShown = false;
  224. },
  225. /**
  226. * Closes the scale toolbar when expanded view is shown
  227. * @param toolbar - toolbar object that needs to be closed
  228. * @param event - Event that triggered the function call
  229. * @returns {undefined} toolbar is closed and event listener turned off
  230. */
  231. onShowExpandedContent: function onShowExpandedContent(toolbar) {
  232. this.visModel.ownerWidget.off('expandedView:visible', this.onShowExpandedContent, this);
  233. toolbar.hide();
  234. },
  235. /**
  236. * Closes the scale toolbar on page scroll
  237. * @param toolbar - toolbar object that needs to be closed
  238. * @param event - Event that triggered the function call (need to confirm it is a scroll we want to close the toolbar on)
  239. * @returns {undefined} If valid page scroll the toolbar is closed
  240. */
  241. scaleToolbarOnScroll: function scaleToolbarOnScroll(toolbar, event) {
  242. if (event.target.classList.contains('page') && (event.target.classList.contains('pageabsolute') || event.target.classList.contains('pagecontainer'))) {
  243. //remove event listener when the toolbar is hidden
  244. this.$el.closest('.page.pageabsolute, .page.pagecontainer').off('scroll.BaseController');
  245. toolbar.hide();
  246. }
  247. },
  248. /**
  249. * Create the HTML output
  250. * @param queryResults The query results
  251. * @param doFillAnimation Whether or not the fill animation should be rendered
  252. */
  253. buildHTML: function buildHTML(queryResults, doFillAnimation) {
  254. this.infographicInfo = {};
  255. var slots = this.content.getFeature('Visualization').getSlots().getMappedSlotList();
  256. if (!slots || slots.length === 0) {
  257. return Promise.resolve('');
  258. }
  259. this.infographicValue = queryResults && queryResults.getRowCount() > 0 ? queryResults.getValue(0, 0).value : null;
  260. this.infographicInfo = this._buildHtml(slots[0], this.infographicValue, doFillAnimation);
  261. return this.dotTemplate(this.infographicInfo);
  262. },
  263. /**
  264. * Listener to rerender when theme changes (default palette changes with
  265. * theme)
  266. */
  267. onChangeTheme: function onChangeTheme() {
  268. this.visModel.getRenderSequence().reRender();
  269. },
  270. _showItemLabel: function _showItemLabel() {
  271. return this.visModel.getPropertyValue('showItemLabel');
  272. },
  273. _getCustomLabel: function _getCustomLabel() {
  274. return this.visModel.getPropertyValue('baseValueLabel');
  275. },
  276. _fillVertically: function _fillVertically() {
  277. return this.visModel.getPropertyValue('fillDirection') === 'BottomToTop';
  278. },
  279. /**
  280. * Return text formatted value for field to avoid jsHint cyclomatic
  281. * complexity warnings
  282. *
  283. * @param slot, slotAPI
  284. * @param v The raw fact value.
  285. * @returns text formatted data value
  286. */
  287. _valueFormat: function _valueFormat(dataItem, v) {
  288. var formatSpec = dataItem.getFormat();
  289. v = Formatter.format(v, formatSpec);
  290. if (v.length < 5) {
  291. // if the formatted value is less than 5 characters long, it
  292. // looks ugly when stretched so we are adding spaces before and
  293. // after. The SVG will size it nicely after that.
  294. var sPrefix = v.length < 3 ? ' ' : ' ';
  295. v = sPrefix + v + sPrefix;
  296. }
  297. return v;
  298. },
  299. _getForegroundColor: function _getForegroundColor() {
  300. var hexColor = this._colorsService.getHexColorFromDashboardColorSet(this.visModel.getPropertyValue('elementColor'));
  301. if (hexColor === 'transparent') {
  302. hexColor = '';
  303. }
  304. return hexColor;
  305. },
  306. getScaleOption: function getScaleOption() {
  307. var result;
  308. if (typeof this.graphicCurrentScaleOption !== 'undefined') {
  309. result = this.graphicCurrentScaleOption;
  310. } else {
  311. result = ScaleUtil.SCALE_VALUE_DEFAULT;
  312. }
  313. return result;
  314. },
  315. getScaleValue: function getScaleValue(scaleProperties, currentOption, value) {
  316. var result;
  317. if (scaleProperties && typeof currentOption !== 'undefined') {
  318. var scales = scaleProperties.availableScales;
  319. if (currentOption === ScaleUtil.SCALE_VALUE_DEFAULT) {
  320. // Default value
  321. result = scaleProperties.optimalScale;
  322. } else if (currentOption === ScaleUtil.SCALE_VALUE_MANY) {
  323. // Many
  324. var scaleManyIndex = scales.indexOf(scaleProperties.optimalScale) - 1;
  325. if (scaleManyIndex < 0) {
  326. scaleManyIndex = 0;
  327. }
  328. result = scales[scaleManyIndex];
  329. } else {
  330. // Few
  331. var scaleFewIndex = scales.indexOf(scaleProperties.optimalScale) + 1;
  332. if (scaleFewIndex > scales.length - 1) {
  333. scaleFewIndex = scales.length - 1;
  334. }
  335. result = scales[scaleFewIndex];
  336. }
  337. } else if (value) {
  338. result = ScaleUtil.calcOptimalValue(value);
  339. }
  340. return result;
  341. },
  342. // livewidget_cleanup there might be a defect here...
  343. // the correnspoding unittest: `render as infographic` is not passing..
  344. _calcScale: function _calcScale(v) {
  345. var scaleObj = {};
  346. var scaleProperties = ScaleUtil.getScalingProperties(v, this.isPercent);
  347. scaleObj.currentScaleOption = this.getScaleOption();
  348. scaleObj.scale = this.getScaleValue(scaleProperties, scaleObj.currentScaleOption, v);
  349. scaleObj.abbreviatedScale = Formatter.format(scaleObj.scale, {
  350. decimalFormatLength: 'short'
  351. });
  352. // Determine the number of shapes and partial shapes to display
  353. scaleObj.scaleComponents = ScaleUtil.getScaleComponents(v, scaleObj.scale, this.isPercent);
  354. return scaleObj;
  355. },
  356. _isPercent: function _isPercent(dataItem, v) {
  357. var dataItemFormat = dataItem.getFormat();
  358. return dataItemFormat && dataItemFormat.type === 'percent' && Math.abs(v) <= 1;
  359. },
  360. _constructShapeStyle: function _constructShapeStyle(hexColor) {
  361. var shapeStyle = {};
  362. var property = 'fill:';
  363. var colorClass = 'colorFill';
  364. shapeStyle.svgClass = 'Shape';
  365. if (this.graphicFillColor === 'transparent') {
  366. colorClass = 'colorStroke';
  367. property = 'stroke:';
  368. shapeStyle.svgClass = 'Line';
  369. }
  370. if (hexColor !== '') {
  371. shapeStyle.colorClass = '';
  372. shapeStyle.coloredStyle = 'style="' + property + hexColor + '"';
  373. } else {
  374. shapeStyle.colorClass = colorClass;
  375. shapeStyle.coloredStyle = '';
  376. }
  377. return shapeStyle;
  378. },
  379. /**
  380. * Return information needed to construct the HTML output The values and
  381. * html fragments returned construct the output.
  382. *
  383. * @param slot The slot item which gets its label rendered when showItemLabel is true.
  384. * @param v The fact value which is rendered into a scaleable svg viewBox.
  385. * @param doFillAnimation Whether or not the fill animation should be rendered
  386. * @returns a data structure with values to substitute into the html templateString.
  387. */
  388. _buildHtml: function _buildHtml(slot, v, doFillAnimation) {
  389. var dataItemIndex = 0; //Infographic view only has one data item
  390. var dataItem = slot.getDataItemList()[dataItemIndex];
  391. var hexColor = this._getForegroundColor();
  392. var infographic;
  393. var fillVertical = this._fillVertically();
  394. var shapeStyle = this._constructShapeStyle(hexColor);
  395. this.isPercent = this._isPercent(dataItem, v);
  396. var scaleObj = this._calcScale(v);
  397. var scale = scaleObj.scale;
  398. var scaleComponents = scaleObj.scaleComponents;
  399. // keep original viewbox
  400. var matches = this.graphicContent.match(RegExp('viewBox="(.*?)"'));
  401. var viewBox = matches && matches[1] ? matches[1] : '0 0 100 100';
  402. var svgContent = this._extractSvgContent(this.graphicContent);
  403. var clipPathLastElement = this._calculateClipPath({
  404. x: viewBox.split(' ')[0],
  405. y: viewBox.split(' ')[1],
  406. width: viewBox.split(' ')[2],
  407. height: viewBox.split(' ')[3]
  408. }, scaleComponents, fillVertical, doFillAnimation);
  409. infographic = {
  410. content: svgContent,
  411. scaleComponents: scaleComponents,
  412. percentageScaleValue: this._getPercentageScaleValue(v, scale),
  413. scale: scale,
  414. abbreviatedScale: scaleObj.abbreviatedScale,
  415. viewBox: viewBox,
  416. svgId: _.uniqueId('svgDef'),
  417. style: shapeStyle,
  418. fillClass: fillVertical ? 'fillVertical' : 'fillHorizontal',
  419. doFillAnimation: doFillAnimation,
  420. // 0.5 -> 500ms animation
  421. fillDuration: 0.5 / (scaleComponents.numShapes + 1),
  422. clipPathLastElement: clipPathLastElement
  423. };
  424. // Update the graphic model data with things calculated
  425. this.currentScaleOption = scaleObj.currentScaleOption;
  426. this.numOfShapes = scaleComponents.numShapes + scaleComponents.numGreyedShapes;
  427. if (scaleComponents.partialValue > 0) {
  428. this.numOfShapes++;
  429. }
  430. v = this._valueFormat(dataItem, v);
  431. return {
  432. v: v,
  433. showTitleString: this._showItemLabel(),
  434. label: this._getCustomLabel() || dataItem.getLabel(),
  435. infographic: infographic
  436. };
  437. },
  438. /**
  439. * Calculates the string to display for the percentage scale value (i.e. +/- 1%, 10%, or 100%)
  440. * @param value - The percentage value we are displaying
  441. * @param scale - The current scale value being used
  442. * @returns String to be displayed for legend
  443. */
  444. _getPercentageScaleValue: function _getPercentageScaleValue(value, scale) {
  445. if (!this.isPercent) {
  446. return false;
  447. }
  448. var result = scale * 100;
  449. if (value < 0) {
  450. result *= -1;
  451. }
  452. return Formatter.format(result / 100, {
  453. type: 'percent',
  454. maximumFractionDigits: 1,
  455. minimumFractionalDigits: 1
  456. });
  457. },
  458. /**
  459. * A helper function that calculates clip path
  460. * @param {object} viewBoxProp - properties of viewBox {x:xxx, y: xxx, width: xxx, height: xxx}
  461. * @param {object} scale - scale componenets
  462. * @param {Boolean} isVertical - true if the shapes are filled from bottom to top
  463. * @return {object} - a clipPath {x: xxx, y: xxx, width: xxx, height: xxx}
  464. */
  465. _calculateClipPath: function _calculateClipPath(viewBoxProp, scale, isVertical) {
  466. // calculate clipPath width and height based on the viewBox
  467. var greyedClipPath;
  468. var partialValue;
  469. if (isVertical) {
  470. //viewBoxProp members variable are strings. Convert viewBoxProp.x to do the arithmetic operation
  471. partialValue = viewBoxProp.height * (1 - scale.partialValue) + Number(viewBoxProp.y);
  472. greyedClipPath = {
  473. width: '100%',
  474. y: partialValue
  475. };
  476. } else {
  477. //viewBoxProp members variable are strings. Convert viewBoxProp.x to do the arithmetic operation
  478. partialValue = viewBoxProp.width * scale.partialValue + Number(viewBoxProp.x);
  479. greyedClipPath = {
  480. width: partialValue,
  481. y: 0
  482. };
  483. }
  484. return greyedClipPath;
  485. },
  486. /**
  487. * a helper method extract the content inside of a SVG Element
  488. * @param {string} content - a string that contains svg tag. eg. <svg id='hello'><circle cx="100" cy="100" r="100"/></svg>
  489. * @return {string} svgContent - a string that does not contain svg tag. eg. <circle cx="100" cy="100" r="100"/>
  490. */
  491. _extractSvgContent: function _extractSvgContent(content) {
  492. var $childrenContent = $(content).children();
  493. // eslint-disable-next-line no-undef
  494. var serializer = new XMLSerializer();
  495. var svgContent = '';
  496. $childrenContent.each(function () {
  497. svgContent += serializer.serializeToString(this);
  498. });
  499. return svgContent;
  500. },
  501. /**
  502. * Take some HTML and render it to the dom element for this view.
  503. *
  504. * @param sHtml A string of HTML to render.
  505. */
  506. addToDom: function addToDom(sHtml) {
  507. this.$el.empty();
  508. this.$el.prepend(sHtml);
  509. this.setElement(this.$el);
  510. },
  511. onVisible: function onVisible() {
  512. if (this.renderOnVisible) {
  513. this._resize();
  514. }
  515. },
  516. /**
  517. * Apply widget-level properties as specified either in overrides in the
  518. * visModel or defaults in the definition.
  519. */
  520. applyVisModelProperties: function applyVisModelProperties() {
  521. this._resizeInfographicShapes();
  522. },
  523. _resizeInfographicShapes: function _resizeInfographicShapes() {
  524. // return if nothing to show
  525. if (this.numOfShapes === 0) {
  526. return;
  527. }
  528. var $node = $(this.el).find('.infographic');
  529. var width = Math.floor($node.width());
  530. //set the height to 1 if it is zero to avoid dividing by 0 case
  531. var height = Math.floor($node.height()) || 1;
  532. var x = width,
  533. y = height,
  534. n = this.numOfShapes;
  535. var px = Math.ceil(Math.sqrt(n * x / y));
  536. var sx, sy;
  537. if (Math.floor(px * y / x) * px < n) {
  538. sx = y / Math.ceil(px * y / x);
  539. } else {
  540. sx = x / px;
  541. }
  542. var py = Math.ceil(Math.sqrt(n * y / x));
  543. if (Math.floor(py * x / y) * py < n) {
  544. sy = x / Math.ceil(x * py / y);
  545. } else {
  546. sy = y / py;
  547. }
  548. // UX design of 8 pixels of padding (4 on each side) around each
  549. var shapePadding = 8;
  550. // Subtract an extra pixel for small spacing issue
  551. var size = Math.max(sx, sy) - shapePadding - OFFSET;
  552. var sizePx = size + 'px';
  553. this.$el.find('.infographic .infographic-content').each(function () {
  554. // Going through jQuery here was really expensive on Safari, so go straight to the style attributes.
  555. this.style.width = this.style.height = sizePx;
  556. });
  557. },
  558. preRenderFillDirectionProperty: function preRenderFillDirectionProperty() {
  559. return {
  560. 'removeProperty': false
  561. };
  562. },
  563. setShowTranslationIcon: function setShowTranslationIcon(showTranslationIcon) {
  564. this._showTranslationIcon = showTranslationIcon;
  565. },
  566. updateTranslationIcon: function updateTranslationIcon() {
  567. var $iconContainer = this.$el.closest('.widgetContentWrapper');
  568. if (this._showTranslationIcon) {
  569. this._translationService.appendTranslationIcon($iconContainer);
  570. } else {
  571. this._translationService.removeTranslationIcon($iconContainer);
  572. }
  573. },
  574. getDecoratorAPI: function getDecoratorAPI() {
  575. var _this2 = this;
  576. return {
  577. decorateItem: function decorateItem() {
  578. _this2.setShowTranslationIcon(true);
  579. },
  580. decoratePoint: function decoratePoint() {
  581. return null;
  582. },
  583. decorate: function decorate() {
  584. return null;
  585. },
  586. clearItemDecoration: function clearItemDecoration() {
  587. _this2.setShowTranslationIcon(false);
  588. },
  589. updateDecorations: function updateDecorations() {
  590. _this2.updateTranslationIcon();
  591. }
  592. };
  593. }
  594. });
  595. return InfographicView;
  596. });
  597. //# sourceMappingURL=InfographicView.js.map