legend.js 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. /**
  2. * DevExtreme (viz/components/legend.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var _utils = require("../core/utils");
  11. var _extend2 = require("../../core/utils/extend");
  12. var _layout_element = require("../core/layout_element");
  13. var _type = require("../../core/utils/type");
  14. var _title = require("../core/title");
  15. var _title2 = _interopRequireDefault(_title);
  16. var _object = require("../../core/utils/object");
  17. var _common = require("../../core/utils/common");
  18. function _interopRequireDefault(obj) {
  19. return obj && obj.__esModule ? obj : {
  20. "default": obj
  21. }
  22. }
  23. var _Number = Number;
  24. var _math = Math;
  25. var _round = _math.round;
  26. var _max = _math.max;
  27. var _min = _math.min;
  28. var _ceil = _math.ceil;
  29. var _isDefined = _type.isDefined;
  30. var _isFunction = _type.isFunction;
  31. var _enumParser = _utils.enumParser;
  32. var _normalizeEnum = _utils.normalizeEnum;
  33. var _extend = _extend2.extend;
  34. var DEFAULT_MARGIN = 10;
  35. var DEFAULT_MARKER_HATCHING_WIDTH = 2;
  36. var DEFAULT_MARKER_HATCHING_STEP = 5;
  37. var CENTER = "center";
  38. var RIGHT = "right";
  39. var LEFT = "left";
  40. var TOP = "top";
  41. var BOTTOM = "bottom";
  42. var HORIZONTAL = "horizontal";
  43. var VERTICAL = "vertical";
  44. var INSIDE = "inside";
  45. var OUTSIDE = "outside";
  46. var NONE = "none";
  47. var HEIGHT = "height";
  48. var WIDTH = "width";
  49. var parseHorizontalAlignment = _enumParser([LEFT, CENTER, RIGHT]);
  50. var parseVerticalAlignment = _enumParser([TOP, BOTTOM]);
  51. var parseOrientation = _enumParser([VERTICAL, HORIZONTAL]);
  52. var parseItemTextPosition = _enumParser([LEFT, RIGHT, TOP, BOTTOM]);
  53. var parsePosition = _enumParser([OUTSIDE, INSIDE]);
  54. var parseItemsAlignment = _enumParser([LEFT, CENTER, RIGHT]);
  55. function getState(state, color) {
  56. if (!state) {
  57. return
  58. }
  59. var colorFromAction = state.fill;
  60. return {
  61. fill: colorFromAction === NONE ? color : colorFromAction,
  62. hatching: _extend({}, state.hatching, {
  63. step: DEFAULT_MARKER_HATCHING_STEP,
  64. width: DEFAULT_MARKER_HATCHING_WIDTH
  65. })
  66. }
  67. }
  68. function parseMargins(options) {
  69. var margin = options.margin;
  70. if (margin >= 0) {
  71. margin = _Number(options.margin);
  72. margin = {
  73. top: margin,
  74. bottom: margin,
  75. left: margin,
  76. right: margin
  77. }
  78. } else {
  79. margin = {
  80. top: margin.top >= 0 ? _Number(margin.top) : DEFAULT_MARGIN,
  81. bottom: margin.bottom >= 0 ? _Number(margin.bottom) : DEFAULT_MARGIN,
  82. left: margin.left >= 0 ? _Number(margin.left) : DEFAULT_MARGIN,
  83. right: margin.right >= 0 ? _Number(margin.right) : DEFAULT_MARGIN
  84. }
  85. }
  86. options.margin = margin
  87. }
  88. function getSizeItem(options, markerSize, labelBBox) {
  89. var defaultXMargin = 7;
  90. var defaultTopMargin = 4;
  91. var width;
  92. var height;
  93. switch (options.itemTextPosition) {
  94. case LEFT:
  95. case RIGHT:
  96. width = markerSize + defaultXMargin + labelBBox.width;
  97. height = _max(markerSize, labelBBox.height);
  98. break;
  99. case TOP:
  100. case BOTTOM:
  101. width = _max(markerSize, labelBBox.width);
  102. height = markerSize + defaultTopMargin + labelBBox.height
  103. }
  104. return {
  105. width: width,
  106. height: height
  107. }
  108. }
  109. function calculateBBoxLabelAndMarker(markerBBox, labelBBox) {
  110. var bBox = {};
  111. bBox.left = _min(markerBBox.x, labelBBox.x);
  112. bBox.top = _min(markerBBox.y, labelBBox.y);
  113. bBox.right = _max(markerBBox.x + markerBBox.width, labelBBox.x + labelBBox.width);
  114. bBox.bottom = _max(markerBBox.y + markerBBox.height, labelBBox.y + labelBBox.height);
  115. return bBox
  116. }
  117. function applyMarkerState(id, idToIndexMap, items, stateName) {
  118. var item = idToIndexMap && items[idToIndexMap[id]];
  119. if (item) {
  120. item.marker.smartAttr(item.states[stateName])
  121. }
  122. }
  123. function parseOptions(options, textField, allowInsidePosition) {
  124. if (!options) {
  125. return null
  126. }
  127. parseMargins(options);
  128. options.horizontalAlignment = parseHorizontalAlignment(options.horizontalAlignment, RIGHT);
  129. options.verticalAlignment = parseVerticalAlignment(options.verticalAlignment, options.horizontalAlignment === CENTER ? BOTTOM : TOP);
  130. options.orientation = parseOrientation(options.orientation, options.horizontalAlignment === CENTER ? HORIZONTAL : VERTICAL);
  131. options.itemTextPosition = parseItemTextPosition(options.itemTextPosition, options.orientation === HORIZONTAL ? BOTTOM : RIGHT);
  132. options.position = allowInsidePosition ? parsePosition(options.position, OUTSIDE) : OUTSIDE;
  133. options.itemsAlignment = parseItemsAlignment(options.itemsAlignment, null);
  134. options.hoverMode = _normalizeEnum(options.hoverMode);
  135. options.customizeText = _isFunction(options.customizeText) ? options.customizeText : function() {
  136. return this[textField]
  137. };
  138. options.customizeHint = _isFunction(options.customizeHint) ? options.customizeHint : _common.noop;
  139. options._incidentOccurred = options._incidentOccurred || _common.noop;
  140. return options
  141. }
  142. function createSquareMarker(renderer, size) {
  143. return renderer.rect(0, 0, size, size)
  144. }
  145. function createCircleMarker(renderer, size) {
  146. return renderer.circle(size / 2, size / 2, size / 2)
  147. }
  148. function isCircle(type) {
  149. return "circle" === _normalizeEnum(type)
  150. }
  151. function inRect(rect, x, y) {
  152. return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
  153. }
  154. function checkLinesSize(lines, layoutOptions, countItems, margins) {
  155. var position = {
  156. x: 0,
  157. y: 0
  158. };
  159. var maxMeasureLength = 0;
  160. var maxAltMeasureLength = 0;
  161. var margin = 0;
  162. if ("y" === layoutOptions.direction) {
  163. margin = margins.top + margins.bottom
  164. } else {
  165. margin = margins.left + margins.right
  166. }
  167. lines.forEach(function(line, i) {
  168. var firstItem = line[0];
  169. var lineLength = line.length;
  170. line.forEach(function(item, index) {
  171. var offset = item.offset || layoutOptions.spacing;
  172. position[layoutOptions.direction] += item[layoutOptions.measure] + (index !== lineLength - 1 ? offset : 0);
  173. maxMeasureLength = _max(maxMeasureLength, position[layoutOptions.direction])
  174. });
  175. position[layoutOptions.direction] = 0;
  176. position[layoutOptions.altDirection] += firstItem[layoutOptions.altMeasure] + firstItem.altOffset || layoutOptions.altSpacing;
  177. maxAltMeasureLength = _max(maxAltMeasureLength, position[layoutOptions.altDirection])
  178. });
  179. if (maxMeasureLength + margin > layoutOptions.length) {
  180. layoutOptions.countItem = decreaseItemCount(layoutOptions, countItems);
  181. return true
  182. }
  183. }
  184. function decreaseItemCount(layoutOptions, countItems) {
  185. layoutOptions.altCountItem++;
  186. return _ceil(countItems / layoutOptions.altCountItem)
  187. }
  188. function getLineLength(line, layoutOptions) {
  189. return line.reduce(function(lineLength, item) {
  190. var offset = item.offset || layoutOptions.spacing;
  191. return lineLength + item[layoutOptions.measure] + offset
  192. }, 0)
  193. }
  194. function getMaxLineLength(lines, layoutOptions) {
  195. return lines.reduce(function(maxLineLength, line) {
  196. return _max(maxLineLength, getLineLength(line, layoutOptions))
  197. }, 0)
  198. }
  199. function getInitPositionForDirection(line, layoutOptions, maxLineLength) {
  200. var lineLength = getLineLength(line, layoutOptions);
  201. var initPosition;
  202. switch (layoutOptions.itemsAlignment) {
  203. case RIGHT:
  204. initPosition = maxLineLength - lineLength;
  205. break;
  206. case CENTER:
  207. initPosition = (maxLineLength - lineLength) / 2;
  208. break;
  209. default:
  210. initPosition = 0
  211. }
  212. return initPosition
  213. }
  214. function getPos(layoutOptions) {
  215. switch (layoutOptions.itemTextPosition) {
  216. case BOTTOM:
  217. return {
  218. horizontal: CENTER, vertical: TOP
  219. };
  220. case TOP:
  221. return {
  222. horizontal: CENTER, vertical: BOTTOM
  223. };
  224. case LEFT:
  225. return {
  226. horizontal: RIGHT, vertical: CENTER
  227. };
  228. case RIGHT:
  229. return {
  230. horizontal: LEFT, vertical: CENTER
  231. }
  232. }
  233. }
  234. function getLines(lines, layoutOptions, itemIndex) {
  235. var tableLine = {};
  236. if (itemIndex % layoutOptions.countItem === 0) {
  237. if (layoutOptions.markerOffset) {
  238. lines.push([], [])
  239. } else {
  240. lines.push([])
  241. }
  242. }
  243. if (layoutOptions.markerOffset) {
  244. tableLine.firstLine = lines[lines.length - 1];
  245. tableLine.secondLine = lines[lines.length - 2]
  246. } else {
  247. tableLine.firstLine = tableLine.secondLine = lines[lines.length - 1]
  248. }
  249. return tableLine
  250. }
  251. function setMaxInLine(line, measure) {
  252. var maxLineSize = line.reduce(function(maxLineSize, item) {
  253. var itemMeasure = item ? item[measure] : maxLineSize;
  254. return _max(maxLineSize, itemMeasure)
  255. }, 0);
  256. line.forEach(function(item) {
  257. if (item) {
  258. item[measure] = maxLineSize
  259. }
  260. })
  261. }
  262. function transpose(array) {
  263. var width = array.length;
  264. var height = array[0].length;
  265. var i;
  266. var j;
  267. var transposeArray = [];
  268. for (i = 0; i < height; i++) {
  269. transposeArray[i] = [];
  270. for (j = 0; j < width; j++) {
  271. transposeArray[i][j] = array[j][i]
  272. }
  273. }
  274. return transposeArray
  275. }
  276. function getAlign(position) {
  277. switch (position) {
  278. case TOP:
  279. case BOTTOM:
  280. return CENTER;
  281. case LEFT:
  282. return RIGHT;
  283. case RIGHT:
  284. return LEFT
  285. }
  286. }
  287. var getMarkerCreator = function(type) {
  288. return isCircle(type) ? createCircleMarker : createSquareMarker
  289. };
  290. function getTitleHorizontalAlignment(options) {
  291. if (options.horizontalAlignment === CENTER) {
  292. return CENTER
  293. } else {
  294. if (options.itemTextPosition === RIGHT) {
  295. return LEFT
  296. } else {
  297. if (options.itemTextPosition === LEFT) {
  298. return RIGHT
  299. } else {
  300. return CENTER
  301. }
  302. }
  303. }
  304. }
  305. var _Legend = exports.Legend = function(settings) {
  306. var that = this;
  307. that._renderer = settings.renderer;
  308. that._legendGroup = settings.group;
  309. that._backgroundClass = settings.backgroundClass;
  310. that._itemGroupClass = settings.itemGroupClass;
  311. that._textField = settings.textField;
  312. that._getCustomizeObject = settings.getFormatObject;
  313. that._titleGroupClass = settings.titleGroupClass;
  314. that._allowInsidePosition = settings.allowInsidePosition
  315. };
  316. var legendPrototype = _Legend.prototype = (0, _object.clone)(_layout_element.LayoutElement.prototype);
  317. (0, _extend2.extend)(legendPrototype, {
  318. constructor: _Legend,
  319. getOptions: function() {
  320. return this._options
  321. },
  322. update: function(data, options) {
  323. var themeManagerTitleOptions = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {};
  324. var that = this;
  325. options = that._options = parseOptions(options, that._textField, that._allowInsidePosition) || {};
  326. that._data = data && options.customizeItems && options.customizeItems(data.slice()) || data;
  327. that._boundingRect = {
  328. width: 0,
  329. height: 0,
  330. x: 0,
  331. y: 0
  332. };
  333. if (that.isVisible() && !that._title) {
  334. that._title = new _title2.default.Title({
  335. renderer: that._renderer,
  336. cssClass: that._titleGroupClass,
  337. root: that._legendGroup
  338. })
  339. }
  340. if (that._title) {
  341. var titleOptions = options.title;
  342. themeManagerTitleOptions.horizontalAlignment = getTitleHorizontalAlignment(options);
  343. that._title.update(themeManagerTitleOptions, titleOptions)
  344. }
  345. this.erase();
  346. return that
  347. },
  348. isVisible: function() {
  349. return this._options && this._options.visible
  350. },
  351. draw: function(width, height) {
  352. var that = this;
  353. var options = that._options;
  354. var items = that._getItemData();
  355. that._size = {
  356. width: width,
  357. height: height
  358. };
  359. that.erase();
  360. if (!(that.isVisible() && items && items.length)) {
  361. return that
  362. }
  363. that._insideLegendGroup = that._renderer.g().enableLinks().append(that._legendGroup);
  364. that._title.changeLink(that._insideLegendGroup);
  365. that._createBackground();
  366. if (that._title.hasText()) {
  367. var horizontalPadding = that._background ? 2 * that._options.paddingLeftRight : 0;
  368. that._title.draw(width - horizontalPadding, height)
  369. }
  370. that._markersGroup = that._renderer.g().attr({
  371. "class": that._itemGroupClass
  372. }).append(that._insideLegendGroup);
  373. that._createItems(items);
  374. that._locateElements(options);
  375. that._finalUpdate(options);
  376. var size = that.getLayoutOptions();
  377. if (size.width > width || size.height > height) {
  378. that.freeSpace()
  379. }
  380. return that
  381. },
  382. probeDraw: function(width, height) {
  383. return this.draw(width, height)
  384. },
  385. _createItems: function(items) {
  386. var that = this;
  387. var options = that._options;
  388. var initMarkerSize = options.markerSize;
  389. var renderer = that._renderer;
  390. var bBox;
  391. var maxBBoxHeight = 0;
  392. var createMarker = getMarkerCreator(options.markerShape);
  393. that._markersId = {};
  394. that._items = (items || []).map(function(dataItem, i) {
  395. var group = that._markersGroup;
  396. var markerSize = _Number(dataItem.size > 0 ? dataItem.size : initMarkerSize);
  397. var stateOfDataItem = dataItem.states;
  398. var normalState = stateOfDataItem.normal;
  399. var normalStateFill = normalState.fill;
  400. var marker = createMarker(renderer, markerSize).attr({
  401. fill: normalStateFill || options.markerColor || options.defaultColor,
  402. opacity: normalState.opacity
  403. }).append(group);
  404. var label = that._createLabel(dataItem, group);
  405. var states = {
  406. normal: {
  407. fill: normalStateFill
  408. },
  409. hovered: getState(stateOfDataItem.hover, normalStateFill),
  410. selected: getState(stateOfDataItem.selection, normalStateFill)
  411. };
  412. var labelBBox = label.getBBox();
  413. if (void 0 !== dataItem.id) {
  414. that._markersId[dataItem.id] = i
  415. }
  416. bBox = getSizeItem(options, markerSize, labelBBox);
  417. maxBBoxHeight = _max(maxBBoxHeight, bBox.height);
  418. that._createHint(dataItem, label, marker);
  419. return {
  420. label: label,
  421. labelBBox: labelBBox,
  422. group: group,
  423. bBox: bBox,
  424. marker: marker,
  425. markerSize: markerSize,
  426. tracker: {
  427. id: dataItem.id,
  428. argument: dataItem.argument,
  429. argumentIndex: dataItem.argumentIndex
  430. },
  431. states: states,
  432. itemTextPosition: options.itemTextPosition,
  433. markerOffset: 0,
  434. bBoxes: []
  435. }
  436. });
  437. if (options.equalRowHeight) {
  438. that._items.forEach(function(item) {
  439. return item.bBox.height = maxBBoxHeight
  440. })
  441. }
  442. },
  443. _getItemData: function() {
  444. var items = this._data || [];
  445. var options = this._options || {};
  446. if (options.inverted) {
  447. items = items.slice().reverse()
  448. }
  449. return items.filter(function(i) {
  450. return i.visible
  451. })
  452. },
  453. _finalUpdate: function(options) {
  454. this._adjustBackgroundSettings(options);
  455. this._setBoundingRect(options.margin)
  456. },
  457. erase: function() {
  458. var that = this;
  459. var insideLegendGroup = that._insideLegendGroup;
  460. insideLegendGroup && insideLegendGroup.dispose();
  461. that._insideLegendGroup = that._markersGroup = that._x1 = that._x2 = that._y2 = that._y2 = null;
  462. return that
  463. },
  464. _locateElements: function(locationOptions) {
  465. this._moveInInitialValues();
  466. this._locateRowsColumns(locationOptions)
  467. },
  468. _moveInInitialValues: function() {
  469. var that = this;
  470. that._title.hasText() && that._title.move([0, 0]);
  471. that._legendGroup && that._legendGroup.move(0, 0);
  472. that._background && that._background.attr({
  473. x: 0,
  474. y: 0,
  475. width: 0,
  476. height: 0
  477. })
  478. },
  479. applySelected: function(id) {
  480. applyMarkerState(id, this._markersId, this._items, "selected");
  481. return this
  482. },
  483. applyHover: function(id) {
  484. applyMarkerState(id, this._markersId, this._items, "hovered");
  485. return this
  486. },
  487. resetItem: function(id) {
  488. applyMarkerState(id, this._markersId, this._items, "normal");
  489. return this
  490. },
  491. _createLabel: function(data, group) {
  492. var labelFormatObject = this._getCustomizeObject(data);
  493. var options = this._options;
  494. var align = getAlign(options.itemTextPosition);
  495. var text = options.customizeText.call(labelFormatObject, labelFormatObject);
  496. var fontStyle = _isDefined(data.textOpacity) ? _extend({}, options.font, {
  497. opacity: data.textOpacity
  498. }) : options.font;
  499. return this._renderer.text(text, 0, 0).css((0, _utils.patchFontOptions)(fontStyle)).attr({
  500. align: align,
  501. "class": options.cssClass
  502. }).append(group)
  503. },
  504. _createHint: function(data, label, marker) {
  505. var labelFormatObject = this._getCustomizeObject(data);
  506. var text = this._options.customizeHint.call(labelFormatObject, labelFormatObject);
  507. if (_isDefined(text) && "" !== text) {
  508. label.setTitle(text);
  509. marker.setTitle(text)
  510. }
  511. },
  512. _createBackground: function() {
  513. var that = this;
  514. var isInside = that._options.position === INSIDE;
  515. var color = that._options.backgroundColor;
  516. var fill = color || (isInside ? that._options.containerBackgroundColor : NONE);
  517. if (that._options.border.visible || (isInside || color) && color !== NONE) {
  518. that._background = that._renderer.rect(0, 0, 0, 0).attr({
  519. fill: fill,
  520. "class": that._backgroundClass
  521. }).append(that._insideLegendGroup)
  522. }
  523. },
  524. _locateRowsColumns: function(options) {
  525. var that = this;
  526. var iteration = 0;
  527. var layoutOptions = that._getItemsLayoutOptions();
  528. var countItems = that._items.length;
  529. var lines;
  530. do {
  531. lines = [];
  532. that._createLines(lines, layoutOptions);
  533. that._alignLines(lines, layoutOptions);
  534. iteration++
  535. } while (checkLinesSize(lines, layoutOptions, countItems, options.margin) && iteration < countItems);
  536. that._applyItemPosition(lines, layoutOptions)
  537. },
  538. _createLines: function(lines, layoutOptions) {
  539. this._items.forEach(function(item, i) {
  540. var tableLine = getLines(lines, layoutOptions, i);
  541. var labelBox = {
  542. width: item.labelBBox.width,
  543. height: item.labelBBox.height,
  544. element: item.label,
  545. bBox: item.labelBBox,
  546. pos: getPos(layoutOptions),
  547. itemIndex: i
  548. };
  549. var markerBox = {
  550. width: item.markerSize,
  551. height: item.markerSize,
  552. element: item.marker,
  553. pos: {
  554. horizontal: CENTER,
  555. vertical: CENTER
  556. },
  557. bBox: {
  558. width: item.markerSize,
  559. height: item.markerSize,
  560. x: 0,
  561. y: 0
  562. },
  563. itemIndex: i
  564. };
  565. var firstItem;
  566. var secondItem;
  567. var offsetDirection = layoutOptions.markerOffset ? "altOffset" : "offset";
  568. if (layoutOptions.inverseLabelPosition) {
  569. firstItem = labelBox;
  570. secondItem = markerBox
  571. } else {
  572. firstItem = markerBox;
  573. secondItem = labelBox
  574. }
  575. firstItem[offsetDirection] = layoutOptions.labelOffset;
  576. tableLine.secondLine.push(firstItem);
  577. tableLine.firstLine.push(secondItem)
  578. })
  579. },
  580. _alignLines: function(lines, layoutOptions) {
  581. var i;
  582. var measure = layoutOptions.altMeasure;
  583. lines.forEach(function(line) {
  584. return setMaxInLine(line, measure)
  585. });
  586. measure = layoutOptions.measure;
  587. if (layoutOptions.itemsAlignment) {
  588. if (layoutOptions.markerOffset) {
  589. for (i = 0; i < lines.length;) {
  590. transpose([lines[i++], lines[i++]]).forEach(processLine)
  591. }
  592. }
  593. } else {
  594. transpose(lines).forEach(processLine)
  595. }
  596. function processLine(line) {
  597. setMaxInLine(line, measure)
  598. }
  599. },
  600. _applyItemPosition: function(lines, layoutOptions) {
  601. var that = this;
  602. var position = {
  603. x: 0,
  604. y: 0
  605. };
  606. var maxLineLength = getMaxLineLength(lines, layoutOptions);
  607. lines.forEach(function(line) {
  608. var firstItem = line[0];
  609. var altOffset = firstItem.altOffset || layoutOptions.altSpacing;
  610. position[layoutOptions.direction] = getInitPositionForDirection(line, layoutOptions, maxLineLength);
  611. line.forEach(function(item) {
  612. var offset = item.offset || layoutOptions.spacing;
  613. var wrap = new _layout_element.WrapperLayoutElement(item.element, item.bBox);
  614. var itemBBoxOptions = {
  615. x: position.x,
  616. y: position.y,
  617. width: item.width,
  618. height: item.height
  619. };
  620. var itemBBox = new _layout_element.WrapperLayoutElement(null, itemBBoxOptions);
  621. var itemLegend = that._items[item.itemIndex];
  622. wrap.position({
  623. of: itemBBox,
  624. my: item.pos,
  625. at: item.pos
  626. });
  627. itemLegend.bBoxes.push(itemBBox);
  628. position[layoutOptions.direction] += item[layoutOptions.measure] + offset
  629. });
  630. position[layoutOptions.altDirection] += firstItem[layoutOptions.altMeasure] + altOffset
  631. });
  632. this._items.forEach(function(item) {
  633. var itemBBox = calculateBBoxLabelAndMarker(item.bBoxes[0].getLayoutOptions(), item.bBoxes[1].getLayoutOptions());
  634. var horizontal = that._options.columnItemSpacing / 2;
  635. var vertical = that._options.rowItemSpacing / 2;
  636. item.tracker.left = itemBBox.left - horizontal;
  637. item.tracker.right = itemBBox.right + horizontal;
  638. item.tracker.top = itemBBox.top - vertical;
  639. item.tracker.bottom = itemBBox.bottom + vertical
  640. })
  641. },
  642. _getItemsLayoutOptions: function() {
  643. var that = this;
  644. var options = that._options;
  645. var orientation = options.orientation;
  646. var layoutOptions = {
  647. itemsAlignment: options.itemsAlignment,
  648. orientation: options.orientation
  649. };
  650. var width = that._size.width - (that._background ? 2 * options.paddingLeftRight : 0);
  651. var height = that._size.height - (that._background ? 2 * options.paddingTopBottom : 0);
  652. if (orientation === HORIZONTAL) {
  653. layoutOptions.length = width;
  654. layoutOptions.spacing = options.columnItemSpacing;
  655. layoutOptions.direction = "x";
  656. layoutOptions.measure = WIDTH;
  657. layoutOptions.altMeasure = HEIGHT;
  658. layoutOptions.altDirection = "y";
  659. layoutOptions.altSpacing = options.rowItemSpacing;
  660. layoutOptions.countItem = options.columnCount;
  661. layoutOptions.altCountItem = options.rowCount;
  662. layoutOptions.marginTextLabel = 4;
  663. layoutOptions.labelOffset = 7;
  664. if (options.itemTextPosition === BOTTOM || options.itemTextPosition === TOP) {
  665. layoutOptions.labelOffset = 4;
  666. layoutOptions.markerOffset = true
  667. }
  668. } else {
  669. layoutOptions.length = height;
  670. layoutOptions.spacing = options.rowItemSpacing;
  671. layoutOptions.direction = "y";
  672. layoutOptions.measure = HEIGHT;
  673. layoutOptions.altMeasure = WIDTH;
  674. layoutOptions.altDirection = "x";
  675. layoutOptions.altSpacing = options.columnItemSpacing;
  676. layoutOptions.countItem = options.rowCount;
  677. layoutOptions.altCountItem = options.columnCount;
  678. layoutOptions.marginTextLabel = 7;
  679. layoutOptions.labelOffset = 4;
  680. if (options.itemTextPosition === RIGHT || options.itemTextPosition === LEFT) {
  681. layoutOptions.labelOffset = 7;
  682. layoutOptions.markerOffset = true
  683. }
  684. }
  685. if (!layoutOptions.countItem) {
  686. if (layoutOptions.altCountItem) {
  687. layoutOptions.countItem = _ceil(that._items.length / layoutOptions.altCountItem)
  688. } else {
  689. layoutOptions.countItem = that._items.length
  690. }
  691. }
  692. if (options.itemTextPosition === TOP || options.itemTextPosition === LEFT) {
  693. layoutOptions.inverseLabelPosition = true
  694. }
  695. layoutOptions.itemTextPosition = options.itemTextPosition;
  696. layoutOptions.altCountItem = layoutOptions.altCountItem || _ceil(that._items.length / layoutOptions.countItem);
  697. return layoutOptions
  698. },
  699. _adjustBackgroundSettings: function(locationOptions) {
  700. if (!this._background) {
  701. return
  702. }
  703. var border = locationOptions.border;
  704. var legendBox = this._calculateTotalBox();
  705. var backgroundSettings = {
  706. x: _round(legendBox.x - locationOptions.paddingLeftRight),
  707. y: _round(legendBox.y - locationOptions.paddingTopBottom),
  708. width: _round(legendBox.width) + 2 * locationOptions.paddingLeftRight,
  709. height: _round(legendBox.height),
  710. opacity: locationOptions.backgroundOpacity
  711. };
  712. if (border.visible && border.width && border.color && border.color !== NONE) {
  713. backgroundSettings["stroke-width"] = border.width;
  714. backgroundSettings.stroke = border.color;
  715. backgroundSettings["stroke-opacity"] = border.opacity;
  716. backgroundSettings.dashStyle = border.dashStyle;
  717. backgroundSettings.rx = border.cornerRadius || 0;
  718. backgroundSettings.ry = border.cornerRadius || 0
  719. }
  720. this._background.attr(backgroundSettings)
  721. },
  722. _setBoundingRect: function(margin) {
  723. if (!this._insideLegendGroup) {
  724. return
  725. }
  726. var box = this._calculateTotalBox();
  727. box.height += margin.top + margin.bottom;
  728. box.widthWithoutMargins = box.width;
  729. box.width += margin.left + margin.right;
  730. box.x -= margin.left;
  731. box.y -= margin.top;
  732. this._boundingRect = box
  733. },
  734. _calculateTotalBox: function() {
  735. var markerBox = this._markersGroup.getBBox();
  736. var titleBox = this._title.getCorrectedLayoutOptions();
  737. var box = this._insideLegendGroup.getBBox();
  738. var verticalPadding = this._background ? 2 * this._options.paddingTopBottom : 0;
  739. box.height = markerBox.height + titleBox.height + verticalPadding;
  740. titleBox.width > box.width && (box.width = titleBox.width);
  741. return box
  742. },
  743. getActionCallback: function(point) {
  744. var that = this;
  745. if (that._options.visible) {
  746. return function(act) {
  747. that[act](point.index)
  748. }
  749. } else {
  750. return _common.noop
  751. }
  752. },
  753. getLayoutOptions: function() {
  754. var options = this._options;
  755. var boundingRect = this._insideLegendGroup ? this._boundingRect : {
  756. width: 0,
  757. height: 0,
  758. x: 0,
  759. y: 0
  760. };
  761. if (options) {
  762. boundingRect.verticalAlignment = options.verticalAlignment;
  763. boundingRect.horizontalAlignment = options.horizontalAlignment;
  764. if (options.orientation === HORIZONTAL) {
  765. boundingRect.cutLayoutSide = options.verticalAlignment;
  766. boundingRect.cutSide = "vertical"
  767. } else {
  768. if (options.horizontalAlignment === CENTER) {
  769. boundingRect.cutLayoutSide = options.verticalAlignment;
  770. boundingRect.cutSide = "vertical"
  771. } else {
  772. boundingRect.cutLayoutSide = options.horizontalAlignment;
  773. boundingRect.cutSide = "horizontal"
  774. }
  775. }
  776. boundingRect.position = {
  777. horizontal: options.horizontalAlignment,
  778. vertical: options.verticalAlignment
  779. };
  780. return boundingRect
  781. }
  782. return null
  783. },
  784. shift: function(x, y) {
  785. var that = this;
  786. var box = {};
  787. if (that._insideLegendGroup) {
  788. that._insideLegendGroup.attr({
  789. translateX: x - that._boundingRect.x,
  790. translateY: y - that._boundingRect.y
  791. })
  792. }
  793. that._title && that._shiftTitle(that._boundingRect.widthWithoutMargins);
  794. that._markersGroup && that._shiftMarkers();
  795. if (that._insideLegendGroup) {
  796. box = that._legendGroup.getBBox()
  797. }
  798. that._x1 = box.x;
  799. that._y1 = box.y;
  800. that._x2 = box.x + box.width;
  801. that._y2 = box.y + box.height;
  802. return that
  803. },
  804. _shiftTitle: function(boxWidth) {
  805. var that = this;
  806. var title = that._title;
  807. var titleBox = title.getCorrectedLayoutOptions();
  808. if (!titleBox || !title.hasText()) {
  809. return
  810. }
  811. var width = boxWidth - (that._background ? 2 * that._options.paddingLeftRight : 0);
  812. var titleOptions = title.getOptions();
  813. var titleY = titleBox.y + titleOptions.margin.top;
  814. var titleX = 0;
  815. if (titleOptions.verticalAlignment === BOTTOM) {
  816. titleY += that._markersGroup.getBBox().height
  817. }
  818. if (titleOptions.horizontalAlignment === RIGHT) {
  819. titleX = width - titleBox.width
  820. } else {
  821. if (titleOptions.horizontalAlignment === CENTER) {
  822. titleX = (width - titleBox.width) / 2
  823. }
  824. }
  825. title.shift(titleX, titleY)
  826. },
  827. _shiftMarkers: function() {
  828. var titleBox = this._title.getLayoutOptions();
  829. var markerBox = this._markersGroup.getBBox();
  830. var titleOptions = this._title.getOptions() || {};
  831. var center = 0;
  832. var y = 0;
  833. if (titleBox.width > markerBox.width && this._options.horizontalAlignment === CENTER) {
  834. center = titleBox.width / 2 - markerBox.width / 2
  835. }
  836. if (titleOptions.verticalAlignment === TOP) {
  837. y = titleBox.height
  838. }
  839. if (0 !== center || 0 !== y) {
  840. this._markersGroup.attr({
  841. translateX: center,
  842. translateY: y
  843. });
  844. this._items.forEach(function(item) {
  845. item.tracker.left += center;
  846. item.tracker.right += center;
  847. item.tracker.top += y;
  848. item.tracker.bottom += y
  849. })
  850. }
  851. },
  852. getPosition: function() {
  853. return this._options.position
  854. },
  855. coordsIn: function(x, y) {
  856. return x >= this._x1 && x <= this._x2 && y >= this._y1 && y <= this._y2
  857. },
  858. getItemByCoord: function(x, y) {
  859. var items = this._items;
  860. var legendGroup = this._insideLegendGroup;
  861. x -= legendGroup.attr("translateX");
  862. y -= legendGroup.attr("translateY");
  863. for (var i = 0; i < items.length; i++) {
  864. if (inRect(items[i].tracker, x, y)) {
  865. return items[i].tracker
  866. }
  867. }
  868. return null
  869. },
  870. dispose: function() {
  871. var that = this;
  872. that._title && that._title.dispose();
  873. that._legendGroup = that._insideLegendGroup = that._title = that._renderer = that._options = that._data = that._items = null;
  874. return that
  875. },
  876. layoutOptions: function() {
  877. if (!this.isVisible()) {
  878. return null
  879. }
  880. var pos = this.getLayoutOptions();
  881. return {
  882. horizontalAlignment: this._options.horizontalAlignment,
  883. verticalAlignment: this._options.verticalAlignment,
  884. side: pos.cutSide,
  885. priority: 1,
  886. position: this.getPosition()
  887. }
  888. },
  889. measure: function(size) {
  890. this.draw(size[0], size[1]);
  891. var rect = this.getLayoutOptions();
  892. return [rect.width, rect.height]
  893. },
  894. move: function(rect) {
  895. this.shift(rect[0], rect[1])
  896. },
  897. freeSpace: function() {
  898. this._options._incidentOccurred("W2104");
  899. this.erase()
  900. }
  901. });
  902. exports.plugin = {
  903. name: "legend",
  904. init: function() {
  905. var that = this;
  906. var group = this._renderer.g().attr({
  907. "class": this._rootClassPrefix + "-legend"
  908. }).enableLinks().append(that._renderer.root);
  909. that._legend = new exports.Legend({
  910. renderer: that._renderer,
  911. group: group,
  912. itemGroupClass: this._rootClassPrefix + "-item",
  913. titleGroupClass: this._rootClassPrefix + "-title",
  914. textField: "text",
  915. getFormatObject: function(data) {
  916. return {
  917. item: data.item,
  918. text: data.text
  919. }
  920. }
  921. });
  922. that._layout.add(that._legend)
  923. },
  924. extenders: {
  925. _applyTilesAppearance: function() {
  926. var that = this;
  927. this._items.forEach(function(item) {
  928. that._applyLegendItemStyle(item.id, item.getState())
  929. })
  930. },
  931. _buildNodes: function() {
  932. this._createLegendItems()
  933. }
  934. },
  935. members: {
  936. _applyLegendItemStyle: function(id, state) {
  937. var legend = this._legend;
  938. switch (state) {
  939. case "hover":
  940. legend.applyHover(id);
  941. break;
  942. case "selection":
  943. legend.applySelected(id);
  944. break;
  945. default:
  946. legend.resetItem(id)
  947. }
  948. },
  949. _createLegendItems: function() {
  950. if (this._legend.update(this._getLegendData(), this._getOption("legend"), this._themeManager.theme("legend").title)) {
  951. this._requestChange(["LAYOUT"])
  952. }
  953. }
  954. },
  955. dispose: function() {
  956. this._legend.dispose()
  957. },
  958. customize: function(constructor) {
  959. constructor.prototype._proxyData.push(function(x, y) {
  960. if (this._legend.coordsIn(x, y)) {
  961. var item = this._legend.getItemByCoord(x, y);
  962. if (item) {
  963. return {
  964. id: item.id,
  965. type: "legend"
  966. }
  967. }
  968. }
  969. });
  970. constructor.addChange({
  971. code: "LEGEND",
  972. handler: function() {
  973. this._createLegendItems()
  974. },
  975. isThemeDependent: true,
  976. option: "legend",
  977. isOptionChange: true
  978. })
  979. }
  980. };