Chart.html 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  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-Chart'>/**
  19. </span> * Charts provide a flexible way to achieve a wide range of data visualization capablitities.
  20. * Each Chart gets its data directly from a {@link Ext.data.Store Store}, and automatically
  21. * updates its display whenever data in the Store changes. In addition, the look and feel
  22. * of a Chart can be customized using {@link Ext.chart.theme.Theme Theme}s.
  23. *
  24. * ## Creating a Simple Chart
  25. *
  26. * Every Chart has three key parts - a {@link Ext.data.Store Store} that contains the data,
  27. * an array of {@link Ext.chart.axis.Axis Axes} which define the boundaries of the Chart,
  28. * and one or more {@link Ext.chart.series.Series Series} to handle the visual rendering of the data points.
  29. *
  30. * ### 1. Creating a Store
  31. *
  32. * The first step is to create a {@link Ext.data.Model Model} that represents the type of
  33. * data that will be displayed in the Chart. For example the data for a chart that displays
  34. * a weather forecast could be represented as a series of &quot;WeatherPoint&quot; data points with
  35. * two fields - &quot;temperature&quot;, and &quot;date&quot;:
  36. *
  37. * Ext.define('WeatherPoint', {
  38. * extend: 'Ext.data.Model',
  39. * fields: ['temperature', 'date']
  40. * });
  41. *
  42. * Next a {@link Ext.data.Store Store} must be created. The store contains a collection of &quot;WeatherPoint&quot; Model instances.
  43. * The data could be loaded dynamically, but for sake of ease this example uses inline data:
  44. *
  45. * var store = Ext.create('Ext.data.Store', {
  46. * model: 'WeatherPoint',
  47. * data: [
  48. * { temperature: 58, date: new Date(2011, 1, 1, 8) },
  49. * { temperature: 63, date: new Date(2011, 1, 1, 9) },
  50. * { temperature: 73, date: new Date(2011, 1, 1, 10) },
  51. * { temperature: 78, date: new Date(2011, 1, 1, 11) },
  52. * { temperature: 81, date: new Date(2011, 1, 1, 12) }
  53. * ]
  54. * });
  55. *
  56. * For additional information on Models and Stores please refer to the [Data Guide](#/guide/data).
  57. *
  58. * ### 2. Creating the Chart object
  59. *
  60. * Now that a Store has been created it can be used in a Chart:
  61. *
  62. * Ext.create('Ext.chart.Chart', {
  63. * renderTo: Ext.getBody(),
  64. * width: 400,
  65. * height: 300,
  66. * store: store
  67. * });
  68. *
  69. * That's all it takes to create a Chart instance that is backed by a Store.
  70. * However, if the above code is run in a browser, a blank screen will be displayed.
  71. * This is because the two pieces that are responsible for the visual display,
  72. * the Chart's {@link #cfg-axes axes} and {@link #cfg-series series}, have not yet been defined.
  73. *
  74. * ### 3. Configuring the Axes
  75. *
  76. * {@link Ext.chart.axis.Axis Axes} are the lines that define the boundaries of the data points that a Chart can display.
  77. * This example uses one of the most common Axes configurations - a horizontal &quot;x&quot; axis, and a vertical &quot;y&quot; axis:
  78. *
  79. * Ext.create('Ext.chart.Chart', {
  80. * ...
  81. * axes: [
  82. * {
  83. * title: 'Temperature',
  84. * type: 'Numeric',
  85. * position: 'left',
  86. * fields: ['temperature'],
  87. * minimum: 0,
  88. * maximum: 100
  89. * },
  90. * {
  91. * title: 'Time',
  92. * type: 'Time',
  93. * position: 'bottom',
  94. * fields: ['date'],
  95. * dateFormat: 'ga'
  96. * }
  97. * ]
  98. * });
  99. *
  100. * The &quot;Temperature&quot; axis is a vertical {@link Ext.chart.axis.Numeric Numeric Axis} and is positioned on the left edge of the Chart.
  101. * It represents the bounds of the data contained in the &quot;WeatherPoint&quot; Model's &quot;temperature&quot; field that was
  102. * defined above. The minimum value for this axis is &quot;0&quot;, and the maximum is &quot;100&quot;.
  103. *
  104. * The horizontal axis is a {@link Ext.chart.axis.Time Time Axis} and is positioned on the bottom edge of the Chart.
  105. * It represents the bounds of the data contained in the &quot;WeatherPoint&quot; Model's &quot;date&quot; field.
  106. * The {@link Ext.chart.axis.Time#cfg-dateFormat dateFormat}
  107. * configuration tells the Time Axis how to format it's labels.
  108. *
  109. * Here's what the Chart looks like now that it has its Axes configured:
  110. *
  111. * {@img Ext.chart.Chart/Ext.chart.Chart1.png Chart Axes}
  112. *
  113. * ### 4. Configuring the Series
  114. *
  115. * The final step in creating a simple Chart is to configure one or more {@link Ext.chart.series.Series Series}.
  116. * Series are responsible for the visual representation of the data points contained in the Store.
  117. * This example only has one Series:
  118. *
  119. * Ext.create('Ext.chart.Chart', {
  120. * ...
  121. * axes: [
  122. * ...
  123. * ],
  124. * series: [
  125. * {
  126. * type: 'line',
  127. * xField: 'date',
  128. * yField: 'temperature'
  129. * }
  130. * ]
  131. * });
  132. *
  133. * This Series is a {@link Ext.chart.series.Line Line Series}, and it uses the &quot;date&quot; and &quot;temperature&quot; fields
  134. * from the &quot;WeatherPoint&quot; Models in the Store to plot its data points:
  135. *
  136. * {@img Ext.chart.Chart/Ext.chart.Chart2.png Line Series}
  137. *
  138. * See the [Line Charts Example](#!/example/charts/Charts.html) for a live demo.
  139. *
  140. * ## Themes
  141. *
  142. * The color scheme for a Chart can be easily changed using the {@link #cfg-theme theme} configuration option:
  143. *
  144. * Ext.create('Ext.chart.Chart', {
  145. * ...
  146. * theme: 'Green',
  147. * ...
  148. * });
  149. *
  150. * {@img Ext.chart.Chart/Ext.chart.Chart3.png Green Theme}
  151. *
  152. * For more information on Charts please refer to the [Drawing and Charting Guide](#/guide/drawing_and_charting).
  153. *
  154. */
  155. Ext.define('Ext.chart.Chart', {
  156. /* Begin Definitions */
  157. alias: 'widget.chart',
  158. extend: 'Ext.draw.Component',
  159. mixins: {
  160. themeManager: 'Ext.chart.theme.Theme',
  161. mask: 'Ext.chart.Mask',
  162. navigation: 'Ext.chart.Navigation',
  163. bindable: 'Ext.util.Bindable',
  164. observable: 'Ext.util.Observable'
  165. },
  166. uses: [
  167. 'Ext.chart.series.Series'
  168. ],
  169. requires: [
  170. 'Ext.util.MixedCollection',
  171. 'Ext.data.StoreManager',
  172. 'Ext.chart.Legend',
  173. 'Ext.chart.theme.Base',
  174. 'Ext.chart.theme.Theme',
  175. 'Ext.util.DelayedTask'
  176. ],
  177. /* End Definitions */
  178. // @private
  179. viewBox: false,
  180. <span id='Ext-chart-Chart-cfg-theme'> /**
  181. </span> * @cfg {String} theme
  182. * The name of the theme to be used. A theme defines the colors and other visual displays of tick marks
  183. * on axis, text, title text, line colors, marker colors and styles, etc. Possible theme values are 'Base', 'Green',
  184. * 'Sky', 'Red', 'Purple', 'Blue', 'Yellow' and also six category themes 'Category1' to 'Category6'. Default value
  185. * is 'Base'.
  186. */
  187. <span id='Ext-chart-Chart-cfg-animate'> /**
  188. </span> * @cfg {Boolean/Object} animate
  189. * True for the default animation (easing: 'ease' and duration: 500) or a standard animation config
  190. * object to be used for default chart animations. Defaults to false.
  191. */
  192. animate: false,
  193. <span id='Ext-chart-Chart-cfg-legend'> /**
  194. </span> * @cfg {Boolean/Object} legend
  195. * True for the default legend display or a legend config object. Defaults to false.
  196. */
  197. legend: false,
  198. <span id='Ext-chart-Chart-cfg-insetPadding'> /**
  199. </span> * @cfg {Number} insetPadding
  200. * The amount of inset padding in pixels for the chart. Defaults to 10.
  201. */
  202. insetPadding: 10,
  203. <span id='Ext-chart-Chart-cfg-enginePriority'> /**
  204. </span> * @cfg {String[]} enginePriority
  205. * Defines the priority order for which Surface implementation to use. The first one supported by the current
  206. * environment will be used. Defaults to `['Svg', 'Vml']`.
  207. */
  208. enginePriority: ['Svg', 'Vml'],
  209. <span id='Ext-chart-Chart-cfg-background'> /**
  210. </span> * @cfg {Object/Boolean} background
  211. * The chart background. This can be a gradient object, image, or color. Defaults to false for no
  212. * background. For example, if `background` were to be a color we could set the object as
  213. *
  214. * background: {
  215. * //color string
  216. * fill: '#ccc'
  217. * }
  218. *
  219. * You can specify an image by using:
  220. *
  221. * background: {
  222. * image: 'http://path.to.image/'
  223. * }
  224. *
  225. * Also you can specify a gradient by using the gradient object syntax:
  226. *
  227. * background: {
  228. * gradient: {
  229. * id: 'gradientId',
  230. * angle: 45,
  231. * stops: {
  232. * 0: {
  233. * color: '#555'
  234. * }
  235. * 100: {
  236. * color: '#ddd'
  237. * }
  238. * }
  239. * }
  240. * }
  241. */
  242. background: false,
  243. <span id='Ext-chart-Chart-cfg-gradients'> /**
  244. </span> * @cfg {Object[]} gradients
  245. * Define a set of gradients that can be used as `fill` property in sprites. The gradients array is an
  246. * array of objects with the following properties:
  247. *
  248. * - **id** - string - The unique name of the gradient.
  249. * - **angle** - number, optional - The angle of the gradient in degrees.
  250. * - **stops** - object - An object with numbers as keys (from 0 to 100) and style objects as values
  251. *
  252. * For example:
  253. *
  254. * gradients: [{
  255. * id: 'gradientId',
  256. * angle: 45,
  257. * stops: {
  258. * 0: {
  259. * color: '#555'
  260. * },
  261. * 100: {
  262. * color: '#ddd'
  263. * }
  264. * }
  265. * }, {
  266. * id: 'gradientId2',
  267. * angle: 0,
  268. * stops: {
  269. * 0: {
  270. * color: '#590'
  271. * },
  272. * 20: {
  273. * color: '#599'
  274. * },
  275. * 100: {
  276. * color: '#ddd'
  277. * }
  278. * }
  279. * }]
  280. *
  281. * Then the sprites can use `gradientId` and `gradientId2` by setting the fill attributes to those ids, for example:
  282. *
  283. * sprite.setAttributes({
  284. * fill: 'url(#gradientId)'
  285. * }, true);
  286. */
  287. <span id='Ext-chart-Chart-cfg-store'> /**
  288. </span> * @cfg {Ext.data.Store} store
  289. * The store that supplies data to this chart.
  290. */
  291. <span id='Ext-chart-Chart-cfg-series'> /**
  292. </span> * @cfg {Ext.chart.series.Series[]} series
  293. * Array of {@link Ext.chart.series.Series Series} instances or config objects. For example:
  294. *
  295. * series: [{
  296. * type: 'column',
  297. * axis: 'left',
  298. * listeners: {
  299. * 'afterrender': function() {
  300. * console('afterrender');
  301. * }
  302. * },
  303. * xField: 'category',
  304. * yField: 'data1'
  305. * }]
  306. */
  307. <span id='Ext-chart-Chart-cfg-axes'> /**
  308. </span> * @cfg {Ext.chart.axis.Axis[]} axes
  309. * Array of {@link Ext.chart.axis.Axis Axis} instances or config objects. For example:
  310. *
  311. * axes: [{
  312. * type: 'Numeric',
  313. * position: 'left',
  314. * fields: ['data1'],
  315. * title: 'Number of Hits',
  316. * minimum: 0,
  317. * //one minor tick between two major ticks
  318. * minorTickSteps: 1
  319. * }, {
  320. * type: 'Category',
  321. * position: 'bottom',
  322. * fields: ['name'],
  323. * title: 'Month of the Year'
  324. * }]
  325. */
  326. constructor: function(config) {
  327. var me = this,
  328. defaultAnim;
  329. config = Ext.apply({}, config);
  330. me.initTheme(config.theme || me.theme);
  331. if (me.gradients) {
  332. Ext.apply(config, { gradients: me.gradients });
  333. }
  334. if (me.background) {
  335. Ext.apply(config, { background: me.background });
  336. }
  337. if (config.animate) {
  338. defaultAnim = {
  339. easing: 'ease',
  340. duration: 500
  341. };
  342. if (Ext.isObject(config.animate)) {
  343. config.animate = Ext.applyIf(config.animate, defaultAnim);
  344. }
  345. else {
  346. config.animate = defaultAnim;
  347. }
  348. }
  349. me.mixins.observable.constructor.call(me, config);
  350. if (config.enableMask) {
  351. me.mixins.mask.constructor.call(me);
  352. }
  353. me.mixins.navigation.constructor.call(me);
  354. me.callParent([config]);
  355. },
  356. getChartStore: function(){
  357. return this.substore || this.store;
  358. },
  359. initComponent: function() {
  360. var me = this,
  361. axes,
  362. series;
  363. me.callParent();
  364. me.addEvents(
  365. 'itemmousedown',
  366. 'itemmouseup',
  367. 'itemmouseover',
  368. 'itemmouseout',
  369. 'itemclick',
  370. 'itemdblclick',
  371. 'itemdragstart',
  372. 'itemdrag',
  373. 'itemdragend',
  374. <span id='Ext-chart-Chart-event-beforerefresh'> /**
  375. </span> * @event beforerefresh
  376. * Fires before a refresh to the chart data is called. If the beforerefresh handler returns false the
  377. * {@link #event-refresh} action will be cancelled.
  378. * @param {Ext.chart.Chart} this
  379. */
  380. 'beforerefresh',
  381. <span id='Ext-chart-Chart-event-refresh'> /**
  382. </span> * @event refresh
  383. * Fires after the chart data has been refreshed.
  384. * @param {Ext.chart.Chart} this
  385. */
  386. 'refresh'
  387. );
  388. Ext.applyIf(me, {
  389. zoom: {
  390. width: 1,
  391. height: 1,
  392. x: 0,
  393. y: 0
  394. }
  395. });
  396. me.maxGutter = [0, 0];
  397. me.store = Ext.data.StoreManager.lookup(me.store);
  398. axes = me.axes;
  399. me.axes = new Ext.util.MixedCollection(false, function(a) { return a.position; });
  400. if (axes) {
  401. me.axes.addAll(axes);
  402. }
  403. series = me.series;
  404. me.series = new Ext.util.MixedCollection(false, function(a) { return a.seriesId || (a.seriesId = Ext.id(null, 'ext-chart-series-')); });
  405. if (series) {
  406. me.series.addAll(series);
  407. }
  408. if (me.legend !== false) {
  409. me.legend = new Ext.chart.Legend(Ext.applyIf({chart:me}, me.legend));
  410. }
  411. me.on({
  412. mousemove: me.onMouseMove,
  413. mouseleave: me.onMouseLeave,
  414. mousedown: me.onMouseDown,
  415. mouseup: me.onMouseUp,
  416. click: me.onClick,
  417. dblclick: me.onDblClick,
  418. scope: me
  419. });
  420. },
  421. // @private overrides the component method to set the correct dimensions to the chart.
  422. afterComponentLayout: function(width, height) {
  423. var me = this;
  424. if (Ext.isNumber(width) &amp;&amp; Ext.isNumber(height)) {
  425. if (width !== me.curWidth || height !== me.curHeight) {
  426. me.curWidth = width;
  427. me.curHeight = height;
  428. me.redraw(true);
  429. } else if (me.needsRedraw) {
  430. delete me.needsRedraw;
  431. me.redraw();
  432. }
  433. }
  434. this.callParent(arguments);
  435. },
  436. <span id='Ext-chart-Chart-method-redraw'> /**
  437. </span> * Redraws the chart. If animations are set this will animate the chart too.
  438. * @param {Boolean} resize (optional) flag which changes the default origin points of the chart for animations.
  439. */
  440. redraw: function(resize) {
  441. var me = this,
  442. seriesItems = me.series.items,
  443. seriesLen = seriesItems.length,
  444. axesItems = me.axes.items,
  445. axesLen = axesItems.length,
  446. i,
  447. chartBBox = me.chartBBox = {
  448. x: 0,
  449. y: 0,
  450. height: me.curHeight,
  451. width: me.curWidth
  452. },
  453. legend = me.legend;
  454. me.surface.setSize(chartBBox.width, chartBBox.height);
  455. // Instantiate Series and Axes
  456. for (i = 0; i &lt; seriesLen; i++) {
  457. me.initializeSeries(seriesItems[i],i);
  458. }
  459. for (i = 0; i &lt; axesLen; i++) {
  460. me.initializeAxis(axesItems[i]);
  461. }
  462. //process all views (aggregated data etc) on stores
  463. //before rendering.
  464. for (i = 0; i &lt; axesLen; i++) {
  465. axesItems[i].processView();
  466. }
  467. for (i = 0; i &lt; axesLen; i++) {
  468. axesItems[i].drawAxis(true);
  469. }
  470. // Create legend if not already created
  471. if (legend !== false &amp;&amp; legend.visible) {
  472. if (legend.update || !legend.created) {
  473. legend.create();
  474. }
  475. }
  476. // Place axes properly, including influence from each other
  477. me.alignAxes();
  478. // Reposition legend based on new axis alignment
  479. if (legend !== false &amp;&amp; legend.visible) {
  480. legend.updatePosition();
  481. }
  482. // Find the max gutter
  483. me.getMaxGutter();
  484. // Draw axes and series
  485. me.resizing = !!resize;
  486. for (i = 0; i &lt; axesLen; i++) {
  487. axesItems[i].drawAxis();
  488. }
  489. for (i = 0; i &lt; seriesLen; i++) {
  490. me.drawCharts(seriesItems[i]);
  491. }
  492. me.resizing = false;
  493. },
  494. // @private set the store after rendering the chart.
  495. afterRender: function() {
  496. var ref,
  497. me = this;
  498. this.callParent();
  499. if (me.categoryNames) {
  500. me.setCategoryNames(me.categoryNames);
  501. }
  502. if (me.tipRenderer) {
  503. ref = me.getFunctionRef(me.tipRenderer);
  504. me.setTipRenderer(ref.fn, ref.scope);
  505. }
  506. me.bindStore(me.store, true);
  507. me.refresh();
  508. if (me.surface.engine === 'Vml') {
  509. me.on('added', me.onAddedVml, me);
  510. me.mon(Ext.container.Container.hierarchyEventSource, 'added', me.onContainerAddedVml, me);
  511. }
  512. },
  513. // When using a vml surface we need to redraw when this chart or one of its ancestors
  514. // is moved to a new container after render, because moving the vml chart causes the
  515. // vml elements to go haywire, some displaing incorrectly or not displaying at all.
  516. // This appears to be caused by the component being moved to the detached body element
  517. // before being added to the new container.
  518. onAddedVml: function() {
  519. this.needsRedraw = true; // redraw after component layout
  520. },
  521. onContainerAddedVml: function(container) {
  522. if (this.isDescendantOf(container)) {
  523. this.needsRedraw = true; // redraw after component layout
  524. }
  525. },
  526. // @private get x and y position of the mouse cursor.
  527. getEventXY: function(e) {
  528. var me = this,
  529. box = this.surface.getRegion(),
  530. pageXY = e.getXY(),
  531. x = pageXY[0] - box.left,
  532. y = pageXY[1] - box.top;
  533. return [x, y];
  534. },
  535. onClick: function(e) {
  536. this.handleClick('itemclick', e);
  537. },
  538. onDblClick: function(e) {
  539. this.handleClick('itemdblclick', e);
  540. },
  541. // @private wrap the mouse down position to delegate the event to the series.
  542. handleClick: function(name, e) {
  543. var me = this,
  544. position = me.getEventXY(e),
  545. seriesItems = me.series.items,
  546. i, ln, series,
  547. item;
  548. // Ask each series if it has an item corresponding to (not necessarily exactly
  549. // on top of) the current mouse coords. Fire itemclick event.
  550. for (i = 0, ln = seriesItems.length; i &lt; ln; i++) {
  551. series = seriesItems[i];
  552. if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
  553. if (series.getItemForPoint) {
  554. item = series.getItemForPoint(position[0], position[1]);
  555. if (item) {
  556. series.fireEvent(name, item);
  557. }
  558. }
  559. }
  560. }
  561. },
  562. // @private wrap the mouse down position to delegate the event to the series.
  563. onMouseDown: function(e) {
  564. var me = this,
  565. position = me.getEventXY(e),
  566. seriesItems = me.series.items,
  567. i, ln, series,
  568. item;
  569. if (me.enableMask) {
  570. me.mixins.mask.onMouseDown.call(me, e);
  571. }
  572. // Ask each series if it has an item corresponding to (not necessarily exactly
  573. // on top of) the current mouse coords. Fire itemmousedown event.
  574. for (i = 0, ln = seriesItems.length; i &lt; ln; i++) {
  575. series = seriesItems[i];
  576. if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
  577. if (series.getItemForPoint) {
  578. item = series.getItemForPoint(position[0], position[1]);
  579. if (item) {
  580. series.fireEvent('itemmousedown', item);
  581. }
  582. }
  583. }
  584. }
  585. },
  586. // @private wrap the mouse up event to delegate it to the series.
  587. onMouseUp: function(e) {
  588. var me = this,
  589. position = me.getEventXY(e),
  590. seriesItems = me.series.items,
  591. i, ln, series,
  592. item;
  593. if (me.enableMask) {
  594. me.mixins.mask.onMouseUp.call(me, e);
  595. }
  596. // Ask each series if it has an item corresponding to (not necessarily exactly
  597. // on top of) the current mouse coords. Fire itemmouseup event.
  598. for (i = 0, ln = seriesItems.length; i &lt; ln; i++) {
  599. series = seriesItems[i];
  600. if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
  601. if (series.getItemForPoint) {
  602. item = series.getItemForPoint(position[0], position[1]);
  603. if (item) {
  604. series.fireEvent('itemmouseup', item);
  605. }
  606. }
  607. }
  608. }
  609. },
  610. // @private wrap the mouse move event so it can be delegated to the series.
  611. onMouseMove: function(e) {
  612. var me = this,
  613. position = me.getEventXY(e),
  614. seriesItems = me.series.items,
  615. i, ln, series,
  616. item, last, storeItem, storeField;
  617. if (me.enableMask) {
  618. me.mixins.mask.onMouseMove.call(me, e);
  619. }
  620. // Ask each series if it has an item corresponding to (not necessarily exactly
  621. // on top of) the current mouse coords. Fire itemmouseover/out events.
  622. for (i = 0, ln = seriesItems.length; i &lt; ln; i++) {
  623. series = seriesItems[i];
  624. if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
  625. if (series.getItemForPoint) {
  626. item = series.getItemForPoint(position[0], position[1]);
  627. last = series._lastItemForPoint;
  628. storeItem = series._lastStoreItem;
  629. storeField = series._lastStoreField;
  630. if (item !== last || item &amp;&amp; (item.storeItem != storeItem || item.storeField != storeField)) {
  631. if (last) {
  632. series.fireEvent('itemmouseout', last);
  633. delete series._lastItemForPoint;
  634. delete series._lastStoreField;
  635. delete series._lastStoreItem;
  636. }
  637. if (item) {
  638. series.fireEvent('itemmouseover', item);
  639. series._lastItemForPoint = item;
  640. series._lastStoreItem = item.storeItem;
  641. series._lastStoreField = item.storeField;
  642. }
  643. }
  644. }
  645. } else {
  646. last = series._lastItemForPoint;
  647. if (last) {
  648. series.fireEvent('itemmouseout', last);
  649. delete series._lastItemForPoint;
  650. delete series._lastStoreField;
  651. delete series._lastStoreItem;
  652. }
  653. }
  654. }
  655. },
  656. // @private handle mouse leave event.
  657. onMouseLeave: function(e) {
  658. var me = this,
  659. seriesItems = me.series.items,
  660. i, ln, series;
  661. if (me.enableMask) {
  662. me.mixins.mask.onMouseLeave.call(me, e);
  663. }
  664. for (i = 0, ln = seriesItems.length; i &lt; ln; i++) {
  665. series = seriesItems[i];
  666. delete series._lastItemForPoint;
  667. }
  668. },
  669. // @private buffered refresh for when we update the store
  670. delayRefresh: function() {
  671. var me = this;
  672. if (!me.refreshTask) {
  673. me.refreshTask = new Ext.util.DelayedTask(me.refresh, me);
  674. }
  675. me.refreshTask.delay(me.refreshBuffer);
  676. },
  677. // @private
  678. refresh: function() {
  679. var me = this;
  680. if (me.rendered &amp;&amp; me.curWidth !== undefined &amp;&amp; me.curHeight !== undefined) {
  681. if (!me.isVisible(true) &amp;&amp; !me.refreshPending) {
  682. me.setShowListeners('mon');
  683. me.refreshPending = true;
  684. return;
  685. }
  686. if (me.fireEvent('beforerefresh', me) !== false) {
  687. me.redraw();
  688. me.fireEvent('refresh', me);
  689. }
  690. }
  691. },
  692. onShow: function(){
  693. var me = this;
  694. me.callParent(arguments);
  695. if (me.refreshPending) {
  696. me.delayRefresh();
  697. me.setShowListeners('mun');
  698. }
  699. delete me.refreshPending;
  700. },
  701. setShowListeners: function(method){
  702. var me = this;
  703. me[method](Ext.container.Container.hierarchyEventSource, {
  704. scope: me,
  705. single: true,
  706. show: me.forceRefresh,
  707. expand: me.forceRefresh
  708. });
  709. },
  710. forceRefresh: function(container) {
  711. var me = this;
  712. if (me.isDescendantOf(container) &amp;&amp; me.refreshPending) {
  713. // Add unbind here, because either expand/show could be fired,
  714. // so be sure to unbind the listener that didn't
  715. me.setShowListeners('mun');
  716. me.delayRefresh();
  717. }
  718. delete me.refreshPending;
  719. },
  720. bindStore: function(store, initial) {
  721. var me = this;
  722. me.mixins.bindable.bindStore.apply(me, arguments);
  723. if (me.store &amp;&amp; !initial) {
  724. me.refresh();
  725. }
  726. },
  727. getStoreListeners: function() {
  728. var refresh = this.refresh,
  729. delayRefresh = this.delayRefresh;
  730. return {
  731. refresh: refresh,
  732. add: delayRefresh,
  733. remove: delayRefresh,
  734. update: delayRefresh,
  735. clear: refresh
  736. };
  737. },
  738. // @private Create Axis
  739. initializeAxis: function(axis) {
  740. var me = this,
  741. chartBBox = me.chartBBox,
  742. w = chartBBox.width,
  743. h = chartBBox.height,
  744. x = chartBBox.x,
  745. y = chartBBox.y,
  746. themeAttrs = me.themeAttrs,
  747. config = {
  748. chart: me
  749. };
  750. if (themeAttrs) {
  751. config.axisStyle = Ext.apply({}, themeAttrs.axis);
  752. config.axisLabelLeftStyle = Ext.apply({}, themeAttrs.axisLabelLeft);
  753. config.axisLabelRightStyle = Ext.apply({}, themeAttrs.axisLabelRight);
  754. config.axisLabelTopStyle = Ext.apply({}, themeAttrs.axisLabelTop);
  755. config.axisLabelBottomStyle = Ext.apply({}, themeAttrs.axisLabelBottom);
  756. config.axisTitleLeftStyle = Ext.apply({}, themeAttrs.axisTitleLeft);
  757. config.axisTitleRightStyle = Ext.apply({}, themeAttrs.axisTitleRight);
  758. config.axisTitleTopStyle = Ext.apply({}, themeAttrs.axisTitleTop);
  759. config.axisTitleBottomStyle = Ext.apply({}, themeAttrs.axisTitleBottom);
  760. }
  761. switch (axis.position) {
  762. case 'top':
  763. Ext.apply(config, {
  764. length: w,
  765. width: h,
  766. x: x,
  767. y: y
  768. });
  769. break;
  770. case 'bottom':
  771. Ext.apply(config, {
  772. length: w,
  773. width: h,
  774. x: x,
  775. y: h
  776. });
  777. break;
  778. case 'left':
  779. Ext.apply(config, {
  780. length: h,
  781. width: w,
  782. x: x,
  783. y: h
  784. });
  785. break;
  786. case 'right':
  787. Ext.apply(config, {
  788. length: h,
  789. width: w,
  790. x: w,
  791. y: h
  792. });
  793. break;
  794. }
  795. if (!axis.chart) {
  796. Ext.apply(config, axis);
  797. axis = me.axes.replace(Ext.createByAlias('axis.' + axis.type.toLowerCase(), config));
  798. }
  799. else {
  800. Ext.apply(axis, config);
  801. }
  802. },
  803. <span id='Ext-chart-Chart-method-alignAxes'> /**
  804. </span> * @private Adjust the dimensions and positions of each axis and the chart body area after accounting
  805. * for the space taken up on each side by the axes and legend.
  806. */
  807. alignAxes: function() {
  808. var me = this,
  809. axes = me.axes,
  810. axesItems = axes.items,
  811. axis,
  812. legend = me.legend,
  813. edges = ['top', 'right', 'bottom', 'left'],
  814. edge,
  815. i, ln,
  816. chartBBox,
  817. insetPadding = me.insetPadding,
  818. insets = {
  819. top: insetPadding,
  820. right: insetPadding,
  821. bottom: insetPadding,
  822. left: insetPadding
  823. },
  824. isVertical, bbox, pos;
  825. function getAxis(edge) {
  826. var i = axes.findIndex('position', edge);
  827. return (i &lt; 0) ? null : axes.getAt(i);
  828. }
  829. // Find the space needed by axes and legend as a positive inset from each edge
  830. for (i = 0, ln = edges.length; i &lt; ln; i++) {
  831. edge = edges[i];
  832. isVertical = (edge === 'left' || edge === 'right');
  833. axis = getAxis(edge);
  834. // Add legend size if it's on this edge
  835. if (legend !== false) {
  836. if (legend.position === edge) {
  837. bbox = legend.getBBox();
  838. insets[edge] += (isVertical ? bbox.width : bbox.height) + insets[edge];
  839. }
  840. }
  841. // Add axis size if there's one on this edge only if it has been
  842. //drawn before.
  843. if (axis &amp;&amp; axis.bbox) {
  844. bbox = axis.bbox;
  845. insets[edge] += (isVertical ? bbox.width : bbox.height);
  846. }
  847. }
  848. // Build the chart bbox based on the collected inset values
  849. chartBBox = {
  850. x: insets.left,
  851. y: insets.top,
  852. width: me.curWidth - insets.left - insets.right,
  853. height: me.curHeight - insets.top - insets.bottom
  854. };
  855. me.chartBBox = chartBBox;
  856. // Go back through each axis and set its length and position based on the
  857. // corresponding edge of the chartBBox
  858. for (i = 0, ln = axesItems.length; i &lt; ln; i++) {
  859. axis = axesItems[i];
  860. pos = axis.position;
  861. isVertical = (pos === 'left' || pos === 'right');
  862. axis.x = (pos === 'right' ? chartBBox.x + chartBBox.width : chartBBox.x);
  863. axis.y = (pos === 'top' ? chartBBox.y : chartBBox.y + chartBBox.height);
  864. axis.width = (isVertical ? chartBBox.width : chartBBox.height);
  865. axis.length = (isVertical ? chartBBox.height : chartBBox.width);
  866. }
  867. },
  868. // @private initialize the series.
  869. initializeSeries: function(series, idx) {
  870. var me = this,
  871. themeAttrs = me.themeAttrs,
  872. seriesObj, markerObj, seriesThemes, st,
  873. markerThemes, colorArrayStyle = [],
  874. i = 0, l,
  875. config = {
  876. chart: me,
  877. seriesId: series.seriesId
  878. };
  879. if (themeAttrs) {
  880. seriesThemes = themeAttrs.seriesThemes;
  881. markerThemes = themeAttrs.markerThemes;
  882. seriesObj = Ext.apply({}, themeAttrs.series);
  883. markerObj = Ext.apply({}, themeAttrs.marker);
  884. config.seriesStyle = Ext.apply(seriesObj, seriesThemes[idx % seriesThemes.length]);
  885. config.seriesLabelStyle = Ext.apply({}, themeAttrs.seriesLabel);
  886. config.markerStyle = Ext.apply(markerObj, markerThemes[idx % markerThemes.length]);
  887. if (themeAttrs.colors) {
  888. config.colorArrayStyle = themeAttrs.colors;
  889. } else {
  890. colorArrayStyle = [];
  891. for (l = seriesThemes.length; i &lt; l; i++) {
  892. st = seriesThemes[i];
  893. if (st.fill || st.stroke) {
  894. colorArrayStyle.push(st.fill || st.stroke);
  895. }
  896. }
  897. if (colorArrayStyle.length) {
  898. config.colorArrayStyle = colorArrayStyle;
  899. }
  900. }
  901. config.seriesIdx = idx;
  902. }
  903. if (series instanceof Ext.chart.series.Series) {
  904. Ext.apply(series, config);
  905. } else {
  906. Ext.applyIf(config, series);
  907. series = me.series.replace(Ext.createByAlias('series.' + series.type.toLowerCase(), config));
  908. }
  909. if (series.initialize) {
  910. series.initialize();
  911. }
  912. },
  913. // @private
  914. getMaxGutter: function() {
  915. var me = this,
  916. seriesItems = me.series.items,
  917. i, ln, series,
  918. maxGutter = [0, 0],
  919. gutter;
  920. for (i = 0, ln = seriesItems.length; i &lt; ln; i++) {
  921. series = seriesItems[i];
  922. gutter = series.getGutters &amp;&amp; series.getGutters() || [0, 0];
  923. maxGutter[0] = Math.max(maxGutter[0], gutter[0]);
  924. maxGutter[1] = Math.max(maxGutter[1], gutter[1]);
  925. }
  926. me.maxGutter = maxGutter;
  927. },
  928. // @private draw axis.
  929. drawAxis: function(axis) {
  930. axis.drawAxis();
  931. },
  932. // @private draw series.
  933. drawCharts: function(series) {
  934. series.triggerafterrender = false;
  935. series.drawSeries();
  936. if (!this.animate) {
  937. series.fireEvent('afterrender');
  938. }
  939. },
  940. <span id='Ext-chart-Chart-method-save'> /**
  941. </span> * Saves the chart by either triggering a download or returning a string containing the chart data
  942. * as SVG. The action depends on the export type specified in the passed configuration. The chart
  943. * will be exported using either the {@link Ext.draw.engine.SvgExporter} or the {@link Ext.draw.engine.ImageExporter}
  944. * classes.
  945. *
  946. * Possible export types:
  947. *
  948. * - 'image/png'
  949. * - 'image/jpeg',
  950. * - 'image/svg+xml'
  951. *
  952. * If 'image/svg+xml' is specified, the SvgExporter will be used.
  953. * If 'image/png' or 'image/jpeg' are specified, the ImageExporter will be used. This exporter
  954. * must post the SVG data to a remote server to have the data processed, see the {@link Ext.draw.engine.ImageExporter}
  955. * for more details.
  956. *
  957. * Example usage:
  958. *
  959. * chart.save({
  960. * type: 'image/png'
  961. * });
  962. *
  963. * @param {Object} [config] The configuration to be passed to the exporter.
  964. * See the export method for the appropriate exporter for the relevant
  965. * configuration options
  966. * @return {Object} See the return types for the appropriate exporter
  967. */
  968. save: function(config){
  969. return Ext.draw.Surface.save(this.surface, config);
  970. },
  971. // @private remove gently.
  972. destroy: function() {
  973. Ext.destroy(this.surface);
  974. this.bindStore(null);
  975. this.callParent(arguments);
  976. }
  977. });
  978. </pre>
  979. </body>
  980. </html>