Legend.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-chart-Legend'>/**
  19. </span> * @class Ext.chart.Legend
  20. *
  21. * Defines a legend for a chart's series.
  22. * The 'chart' member must be set prior to rendering.
  23. * The legend class displays a list of legend items each of them related with a
  24. * series being rendered. In order to render the legend item of the proper series
  25. * the series configuration object must have `showInLegend` set to true.
  26. *
  27. * The legend configuration object accepts a `position` as parameter.
  28. * The `position` parameter can be `left`, `right`
  29. * `top` or `bottom`. For example:
  30. *
  31. * legend: {
  32. * position: 'right'
  33. * },
  34. *
  35. * ## Example
  36. *
  37. * @example
  38. * var store = Ext.create('Ext.data.JsonStore', {
  39. * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
  40. * data: [
  41. * { 'name': 'metric one', 'data1': 10, 'data2': 12, 'data3': 14, 'data4': 8, 'data5': 13 },
  42. * { 'name': 'metric two', 'data1': 7, 'data2': 8, 'data3': 16, 'data4': 10, 'data5': 3 },
  43. * { 'name': 'metric three', 'data1': 5, 'data2': 2, 'data3': 14, 'data4': 12, 'data5': 7 },
  44. * { 'name': 'metric four', 'data1': 2, 'data2': 14, 'data3': 6, 'data4': 1, 'data5': 23 },
  45. * { 'name': 'metric five', 'data1': 27, 'data2': 38, 'data3': 36, 'data4': 13, 'data5': 33 }
  46. * ]
  47. * });
  48. *
  49. * Ext.create('Ext.chart.Chart', {
  50. * renderTo: Ext.getBody(),
  51. * width: 500,
  52. * height: 300,
  53. * animate: true,
  54. * store: store,
  55. * shadow: true,
  56. * theme: 'Category1',
  57. * legend: {
  58. * position: 'top'
  59. * },
  60. * axes: [
  61. * {
  62. * type: 'Numeric',
  63. * grid: true,
  64. * position: 'left',
  65. * fields: ['data1', 'data2', 'data3', 'data4', 'data5'],
  66. * title: 'Sample Values',
  67. * grid: {
  68. * odd: {
  69. * opacity: 1,
  70. * fill: '#ddd',
  71. * stroke: '#bbb',
  72. * 'stroke-width': 1
  73. * }
  74. * },
  75. * minimum: 0,
  76. * adjustMinimumByMajorUnit: 0
  77. * },
  78. * {
  79. * type: 'Category',
  80. * position: 'bottom',
  81. * fields: ['name'],
  82. * title: 'Sample Metrics',
  83. * grid: true,
  84. * label: {
  85. * rotate: {
  86. * degrees: 315
  87. * }
  88. * }
  89. * }
  90. * ],
  91. * series: [{
  92. * type: 'area',
  93. * highlight: false,
  94. * axis: 'left',
  95. * xField: 'name',
  96. * yField: ['data1', 'data2', 'data3', 'data4', 'data5'],
  97. * style: {
  98. * opacity: 0.93
  99. * }
  100. * }]
  101. * });
  102. */
  103. Ext.define('Ext.chart.Legend', {
  104. /* Begin Definitions */
  105. requires: ['Ext.chart.LegendItem'],
  106. /* End Definitions */
  107. <span id='Ext-chart-Legend-cfg-visible'> /**
  108. </span> * @cfg {Boolean} visible
  109. * Whether or not the legend should be displayed.
  110. */
  111. visible: true,
  112. <span id='Ext-chart-Legend-cfg-update'> /**
  113. </span> * @cfg {Boolean} update
  114. * If set to true the legend will be refreshed when the chart is.
  115. * This is useful to update the legend items if series are
  116. * added/removed/updated from the chart. Default is true.
  117. */
  118. update: true,
  119. <span id='Ext-chart-Legend-cfg-position'> /**
  120. </span> * @cfg {String} position
  121. * The position of the legend in relation to the chart. One of: &quot;top&quot;,
  122. * &quot;bottom&quot;, &quot;left&quot;, &quot;right&quot;, or &quot;float&quot;. If set to &quot;float&quot;, then the legend
  123. * box will be positioned at the point denoted by the x and y parameters.
  124. */
  125. position: 'bottom',
  126. <span id='Ext-chart-Legend-cfg-x'> /**
  127. </span> * @cfg {Number} x
  128. * X-position of the legend box. Used directly if position is set to &quot;float&quot;, otherwise
  129. * it will be calculated dynamically.
  130. */
  131. x: 0,
  132. <span id='Ext-chart-Legend-cfg-y'> /**
  133. </span> * @cfg {Number} y
  134. * Y-position of the legend box. Used directly if position is set to &quot;float&quot;, otherwise
  135. * it will be calculated dynamically.
  136. */
  137. y: 0,
  138. <span id='Ext-chart-Legend-cfg-labelColor'> /**
  139. </span> * @cfg {String} labelColor
  140. * Color to be used for the legend labels, eg '#000'
  141. */
  142. labelColor: '#000',
  143. <span id='Ext-chart-Legend-cfg-labelFont'> /**
  144. </span> * @cfg {String} labelFont
  145. * Font to be used for the legend labels, eg '12px Helvetica'
  146. */
  147. labelFont: '12px Helvetica, sans-serif',
  148. <span id='Ext-chart-Legend-cfg-boxStroke'> /**
  149. </span> * @cfg {String} boxStroke
  150. * Style of the stroke for the legend box
  151. */
  152. boxStroke: '#000',
  153. <span id='Ext-chart-Legend-cfg-boxStrokeWidth'> /**
  154. </span> * @cfg {String} boxStrokeWidth
  155. * Width of the stroke for the legend box
  156. */
  157. boxStrokeWidth: 1,
  158. <span id='Ext-chart-Legend-cfg-boxFill'> /**
  159. </span> * @cfg {String} boxFill
  160. * Fill style for the legend box
  161. */
  162. boxFill: '#FFF',
  163. <span id='Ext-chart-Legend-cfg-itemSpacing'> /**
  164. </span> * @cfg {Number} itemSpacing
  165. * Amount of space between legend items
  166. */
  167. itemSpacing: 10,
  168. <span id='Ext-chart-Legend-cfg-padding'> /**
  169. </span> * @cfg {Number} padding
  170. * Amount of padding between the legend box's border and its items
  171. */
  172. padding: 5,
  173. // @private
  174. width: 0,
  175. // @private
  176. height: 0,
  177. <span id='Ext-chart-Legend-cfg-boxZIndex'> /**
  178. </span> * @cfg {Number} boxZIndex
  179. * Sets the z-index for the legend. Defaults to 100.
  180. */
  181. boxZIndex: 100,
  182. <span id='Ext-chart-Legend-method-constructor'> /**
  183. </span> * Creates new Legend.
  184. * @param {Object} config (optional) Config object.
  185. */
  186. constructor: function(config) {
  187. var me = this;
  188. if (config) {
  189. Ext.apply(me, config);
  190. }
  191. me.items = [];
  192. <span id='Ext-chart-Legend-property-isVertical'> /**
  193. </span> * Whether the legend box is oriented vertically, i.e. if it is on the left or right side or floating.
  194. * @type {Boolean}
  195. */
  196. me.isVertical = (&quot;left|right|float&quot;.indexOf(me.position) !== -1);
  197. // cache these here since they may get modified later on
  198. me.origX = me.x;
  199. me.origY = me.y;
  200. },
  201. <span id='Ext-chart-Legend-method-create'> /**
  202. </span> * @private Create all the sprites for the legend
  203. */
  204. create: function() {
  205. var me = this,
  206. seriesItems = me.chart.series.items,
  207. i, ln, series;
  208. me.createBox();
  209. if (me.rebuild !== false) {
  210. me.createItems();
  211. }
  212. if (!me.created &amp;&amp; me.isDisplayed()) {
  213. me.created = true;
  214. // Listen for changes to series titles to trigger regeneration of the legend
  215. for (i = 0, ln = seriesItems.length; i &lt; ln; i++) {
  216. series = seriesItems[i];
  217. series.on('titlechange', function() {
  218. me.create();
  219. me.updatePosition();
  220. });
  221. }
  222. }
  223. },
  224. <span id='Ext-chart-Legend-method-isDisplayed'> /**
  225. </span> * @private Determine whether the legend should be displayed. Looks at the legend's 'visible' config,
  226. * and also the 'showInLegend' config for each of the series.
  227. */
  228. isDisplayed: function() {
  229. return this.visible &amp;&amp; this.chart.series.findIndex('showInLegend', true) !== -1;
  230. },
  231. <span id='Ext-chart-Legend-method-createItems'> /**
  232. </span> * @private Create the series markers and labels
  233. */
  234. createItems: function() {
  235. var me = this,
  236. chart = me.chart,
  237. seriesItems = chart.series.items,
  238. ln, series,
  239. surface = chart.surface,
  240. items = me.items,
  241. padding = me.padding,
  242. itemSpacing = me.itemSpacing,
  243. spacingOffset = 2,
  244. maxWidth = 0,
  245. maxHeight = 0,
  246. totalWidth = 0,
  247. totalHeight = 0,
  248. vertical = me.isVertical,
  249. math = Math,
  250. mfloor = math.floor,
  251. mmax = math.max,
  252. index = 0,
  253. i = 0,
  254. len = items ? items.length : 0,
  255. x, y, spacing, item, bbox, height, width,
  256. fields, field, nFields, j;
  257. //remove all legend items
  258. if (len) {
  259. for (; i &lt; len; i++) {
  260. items[i].destroy();
  261. }
  262. }
  263. //empty array
  264. items.length = [];
  265. // Create all the item labels, collecting their dimensions and positioning each one
  266. // properly in relation to the previous item
  267. for (i = 0, ln = seriesItems.length; i &lt; ln; i++) {
  268. series = seriesItems[i];
  269. if (series.showInLegend) {
  270. fields = [].concat(series.yField);
  271. for (j = 0, nFields = fields.length; j &lt; nFields; j++) {
  272. field = fields[j];
  273. item = new Ext.chart.LegendItem({
  274. legend: this,
  275. series: series,
  276. surface: chart.surface,
  277. yFieldIndex: j
  278. });
  279. bbox = item.getBBox();
  280. //always measure from x=0, since not all markers go all the way to the left
  281. width = bbox.width;
  282. height = bbox.height;
  283. if (i + j === 0) {
  284. spacing = vertical ? padding + height / 2 : padding;
  285. }
  286. else {
  287. spacing = itemSpacing / (vertical ? 2 : 1);
  288. }
  289. // Set the item's position relative to the legend box
  290. item.x = mfloor(vertical ? padding : totalWidth + spacing);
  291. item.y = mfloor(vertical ? totalHeight + spacing : padding + height / 2);
  292. // Collect cumulative dimensions
  293. totalWidth += width + spacing;
  294. totalHeight += height + spacing;
  295. maxWidth = mmax(maxWidth, width);
  296. maxHeight = mmax(maxHeight, height);
  297. items.push(item);
  298. }
  299. }
  300. }
  301. // Store the collected dimensions for later
  302. me.width = mfloor((vertical ? maxWidth : totalWidth) + padding * 2);
  303. if (vertical &amp;&amp; items.length === 1) {
  304. spacingOffset = 1;
  305. }
  306. me.height = mfloor((vertical ? totalHeight - spacingOffset * spacing : maxHeight) + (padding * 2));
  307. me.itemHeight = maxHeight;
  308. },
  309. <span id='Ext-chart-Legend-method-getBBox'> /**
  310. </span> * @private Get the bounds for the legend's outer box
  311. */
  312. getBBox: function() {
  313. var me = this;
  314. return {
  315. x: Math.round(me.x) - me.boxStrokeWidth / 2,
  316. y: Math.round(me.y) - me.boxStrokeWidth / 2,
  317. width: me.width,
  318. height: me.height
  319. };
  320. },
  321. <span id='Ext-chart-Legend-method-createBox'> /**
  322. </span> * @private Create the box around the legend items
  323. */
  324. createBox: function() {
  325. var me = this,
  326. box, bbox;
  327. if (me.boxSprite) {
  328. me.boxSprite.destroy();
  329. }
  330. bbox = me.getBBox();
  331. //if some of the dimensions are NaN this means that we
  332. //cannot set a specific width/height for the legend
  333. //container. One possibility for this is that there are
  334. //actually no items to show in the legend, and the legend
  335. //should be hidden.
  336. if (isNaN(bbox.width) || isNaN(bbox.height)) {
  337. me.boxSprite = false;
  338. return;
  339. }
  340. box = me.boxSprite = me.chart.surface.add(Ext.apply({
  341. type: 'rect',
  342. stroke: me.boxStroke,
  343. &quot;stroke-width&quot;: me.boxStrokeWidth,
  344. fill: me.boxFill,
  345. zIndex: me.boxZIndex
  346. }, bbox));
  347. box.redraw();
  348. },
  349. <span id='Ext-chart-Legend-method-updatePosition'> /**
  350. </span> * @private Update the position of all the legend's sprites to match its current x/y values
  351. */
  352. updatePosition: function() {
  353. var me = this,
  354. items = me.items,
  355. i, ln,
  356. x, y,
  357. legendWidth = me.width || 0,
  358. legendHeight = me.height || 0,
  359. padding = me.padding,
  360. chart = me.chart,
  361. chartBBox = chart.chartBBox,
  362. insets = chart.insetPadding,
  363. chartWidth = chartBBox.width - (insets * 2),
  364. chartHeight = chartBBox.height - (insets * 2),
  365. chartX = chartBBox.x + insets,
  366. chartY = chartBBox.y + insets,
  367. surface = chart.surface,
  368. mfloor = Math.floor,
  369. bbox;
  370. if (me.isDisplayed()) {
  371. // Find the position based on the dimensions
  372. switch(me.position) {
  373. case &quot;left&quot;:
  374. x = insets;
  375. y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
  376. break;
  377. case &quot;right&quot;:
  378. x = mfloor(surface.width - legendWidth) - insets;
  379. y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
  380. break;
  381. case &quot;top&quot;:
  382. x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
  383. y = insets;
  384. break;
  385. case &quot;bottom&quot;:
  386. x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
  387. y = mfloor(surface.height - legendHeight) - insets;
  388. break;
  389. default:
  390. x = mfloor(me.origX) + insets;
  391. y = mfloor(me.origY) + insets;
  392. }
  393. me.x = x;
  394. me.y = y;
  395. // Update the position of each item
  396. for (i = 0, ln = items.length; i &lt; ln; i++) {
  397. items[i].updatePosition();
  398. }
  399. bbox = me.getBBox();
  400. //if some of the dimensions are NaN this means that we
  401. //cannot set a specific width/height for the legend
  402. //container. One possibility for this is that there are
  403. //actually no items to show in the legend, and the legend
  404. //should be hidden.
  405. if (isNaN(bbox.width) || isNaN(bbox.height)) {
  406. if (me.boxSprite) {
  407. me.boxSprite.hide(true);
  408. }
  409. } else {
  410. if (!me.boxSprite) {
  411. me.createBox();
  412. }
  413. // Update the position of the outer box
  414. me.boxSprite.setAttributes(bbox, true);
  415. me.boxSprite.show(true);
  416. }
  417. }
  418. },
  419. <span id='Ext-chart-Legend-method-toggle'> /** toggle
  420. </span> * @param {Boolean} Whether to show or hide the legend.
  421. *
  422. */
  423. toggle: function(show) {
  424. var me = this,
  425. i = 0,
  426. items = me.items,
  427. len = items.length;
  428. if (me.boxSprite) {
  429. if (show) {
  430. me.boxSprite.show(true);
  431. } else {
  432. me.boxSprite.hide(true);
  433. }
  434. }
  435. for (; i &lt; len; ++i) {
  436. if (show) {
  437. items[i].show(true);
  438. } else {
  439. items[i].hide(true);
  440. }
  441. }
  442. me.visible = show;
  443. }
  444. });
  445. </pre>
  446. </body>
  447. </html>