Show:
  1. /**
  2. * Provides base functionality for drawing chart axes.
  3. *
  4. * @module charts
  5. * @submodule axis
  6. */
  7. var CONFIG = Y.config,
  8. DOCUMENT = CONFIG.doc,
  9. Y_Lang = Y.Lang,
  10. IS_STRING = Y_Lang.isString,
  11. Y_DOM = Y.DOM,
  12. LeftAxisLayout,
  13. RightAxisLayout,
  14. BottomAxisLayout,
  15. TopAxisLayout;
  16. /**
  17. * Algorithmic strategy for rendering a left axis.
  18. *
  19. * @class LeftAxisLayout
  20. * @constructor
  21. * @submodule axis
  22. */
  23. LeftAxisLayout = function() {};
  24. LeftAxisLayout.prototype = {
  25. /**
  26. * Default margins for text fields.
  27. *
  28. * @private
  29. * @method _getDefaultMargins
  30. * @return Object
  31. */
  32. _getDefaultMargins: function()
  33. {
  34. return {
  35. top: 0,
  36. left: 0,
  37. right: 4,
  38. bottom: 0
  39. };
  40. },
  41. /**
  42. * Sets the length of the tick on either side of the axis line.
  43. *
  44. * @method setTickOffset
  45. * @protected
  46. */
  47. setTickOffsets: function()
  48. {
  49. var host = this,
  50. majorTicks = host.get("styles").majorTicks,
  51. tickLength = majorTicks.length,
  52. halfTick = tickLength * 0.5,
  53. display = majorTicks.display;
  54. host.set("topTickOffset", 0);
  55. host.set("bottomTickOffset", 0);
  56. switch(display)
  57. {
  58. case "inside" :
  59. host.set("rightTickOffset", tickLength);
  60. host.set("leftTickOffset", 0);
  61. break;
  62. case "outside" :
  63. host.set("rightTickOffset", 0);
  64. host.set("leftTickOffset", tickLength);
  65. break;
  66. case "cross":
  67. host.set("rightTickOffset", halfTick);
  68. host.set("leftTickOffset", halfTick);
  69. break;
  70. default:
  71. host.set("rightTickOffset", 0);
  72. host.set("leftTickOffset", 0);
  73. break;
  74. }
  75. },
  76. /**
  77. * Draws a tick
  78. *
  79. * @method drawTick
  80. * @param {Path} path reference to the path `Path` element in which to draw the tick.
  81. * @param {Object} pt Point on the axis in which the tick will intersect.
  82. * @param {Object} tickStyle Hash of properties to apply to the tick.
  83. * @protected
  84. */
  85. drawTick: function(path, pt, tickStyles)
  86. {
  87. var host = this,
  88. style = host.get("styles"),
  89. padding = style.padding,
  90. tickLength = tickStyles.length,
  91. start = {x:padding.left, y:pt.y},
  92. end = {x:tickLength + padding.left, y:pt.y};
  93. host.drawLine(path, start, end);
  94. },
  95. /**
  96. * Calculates the coordinates for the first point on an axis.
  97. *
  98. * @method getLineStart
  99. * @return {Object}
  100. * @protected
  101. */
  102. getLineStart: function()
  103. {
  104. var style = this.get("styles"),
  105. padding = style.padding,
  106. majorTicks = style.majorTicks,
  107. tickLength = majorTicks.length,
  108. display = majorTicks.display,
  109. pt = {x:padding.left, y:0};
  110. if(display === "outside")
  111. {
  112. pt.x += tickLength;
  113. }
  114. else if(display === "cross")
  115. {
  116. pt.x += tickLength/2;
  117. }
  118. return pt;
  119. },
  120. /**
  121. * Calculates the point for a label.
  122. *
  123. * @method getLabelPoint
  124. * @param {Object} point Point on the axis in which the tick will intersect.
  125. * @return {Object}
  126. * @protected
  127. */
  128. getLabelPoint: function(point)
  129. {
  130. return {x:point.x - this.get("leftTickOffset"), y:point.y};
  131. },
  132. /**
  133. * Updates the value for the `maxLabelSize` for use in calculating total size.
  134. *
  135. * @method updateMaxLabelSize
  136. * @param {HTMLElement} label to measure
  137. * @protected
  138. */
  139. updateMaxLabelSize: function(labelWidth, labelHeight)
  140. {
  141. var host = this,
  142. props = this._labelRotationProps,
  143. rot = props.rot,
  144. absRot = props.absRot,
  145. sinRadians = props.sinRadians,
  146. cosRadians = props.cosRadians,
  147. max;
  148. if(rot === 0)
  149. {
  150. max = labelWidth;
  151. }
  152. else if(absRot === 90)
  153. {
  154. max = labelHeight;
  155. }
  156. else
  157. {
  158. max = (cosRadians * labelWidth) + (sinRadians * labelHeight);
  159. }
  160. host._maxLabelSize = Math.max(host._maxLabelSize, max);
  161. },
  162. /**
  163. * Determines the available label width when the axis width has been explicitly set.
  164. *
  165. * @method getExplicitlySized
  166. * @return Boolean
  167. * @protected
  168. */
  169. getExplicitlySized: function(styles)
  170. {
  171. if(this._explicitWidth)
  172. {
  173. var host = this,
  174. w = host._explicitWidth,
  175. totalTitleSize = host._totalTitleSize,
  176. leftTickOffset = host.get("leftTickOffset"),
  177. margin = styles.label.margin.right;
  178. host._maxLabelSize = w - (leftTickOffset + margin + totalTitleSize);
  179. return true;
  180. }
  181. return false;
  182. },
  183. /**
  184. * Rotate and position title.
  185. *
  186. * @method positionTitle
  187. * @param {HTMLElement} label to rotate position
  188. * @protected
  189. */
  190. positionTitle: function(label)
  191. {
  192. var host = this,
  193. bounds = host._titleBounds,
  194. margin = host.get("styles").title.margin,
  195. props = host._titleRotationProps,
  196. w = bounds.right - bounds.left,
  197. labelWidth = label.offsetWidth,
  198. labelHeight = label.offsetHeight,
  199. x = (labelWidth * -0.5) + (w * 0.5),
  200. y = (host.get("height") * 0.5) - (labelHeight * 0.5);
  201. props.labelWidth = labelWidth;
  202. props.labelHeight = labelHeight;
  203. if(margin && margin.left)
  204. {
  205. x += margin.left;
  206. }
  207. props.x = x;
  208. props.y = y;
  209. props.transformOrigin = [0.5, 0.5];
  210. host._rotate(label, props);
  211. },
  212. /**
  213. * Rotate and position labels.
  214. *
  215. * @method positionLabel
  216. * @param {HTMLElement} label to rotate position
  217. * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned
  218. * against.
  219. * @protected
  220. */
  221. positionLabel: function(label, pt, styles, i)
  222. {
  223. var host = this,
  224. offset = parseFloat(styles.label.offset),
  225. tickOffset = host.get("leftTickOffset"),
  226. totalTitleSize = this._totalTitleSize,
  227. leftOffset = pt.x + totalTitleSize - tickOffset,
  228. topOffset = pt.y,
  229. props = this._labelRotationProps,
  230. rot = props.rot,
  231. absRot = props.absRot,
  232. maxLabelSize = host._maxLabelSize,
  233. labelWidth = this._labelWidths[i],
  234. labelHeight = this._labelHeights[i];
  235. if(rot === 0)
  236. {
  237. leftOffset -= labelWidth;
  238. topOffset -= labelHeight * offset;
  239. }
  240. else if(rot === 90)
  241. {
  242. leftOffset -= labelWidth * 0.5;
  243. topOffset = topOffset + labelWidth/2 - (labelWidth * offset);
  244. }
  245. else if(rot === -90)
  246. {
  247. leftOffset -= labelWidth * 0.5;
  248. topOffset = topOffset - labelHeight + labelWidth/2 - (labelWidth * offset);
  249. }
  250. else
  251. {
  252. leftOffset -= labelWidth + (labelHeight * absRot/360);
  253. topOffset -= labelHeight * offset;
  254. }
  255. props.labelWidth = labelWidth;
  256. props.labelHeight = labelHeight;
  257. props.x = Math.round(maxLabelSize + leftOffset);
  258. props.y = Math.round(topOffset);
  259. this._rotate(label, props);
  260. },
  261. /**
  262. * Adjusts the coordinates of an axis label based on the rotation.
  263. *
  264. * @method _setRotationCoords
  265. * @param {Object} props Coordinates, dimension and rotation properties of the label.
  266. * @protected
  267. */
  268. _setRotationCoords: function(props)
  269. {
  270. var rot = props.rot,
  271. absRot = props.absRot,
  272. leftOffset,
  273. topOffset,
  274. labelWidth = props.labelWidth,
  275. labelHeight = props.labelHeight;
  276. if(rot === 0)
  277. {
  278. leftOffset = labelWidth;
  279. topOffset = labelHeight * 0.5;
  280. }
  281. else if(rot === 90)
  282. {
  283. topOffset = 0;
  284. leftOffset = labelWidth * 0.5;
  285. }
  286. else if(rot === -90)
  287. {
  288. leftOffset = labelWidth * 0.5;
  289. topOffset = labelHeight;
  290. }
  291. else
  292. {
  293. leftOffset = labelWidth + (labelHeight * absRot/360);
  294. topOffset = labelHeight * 0.5;
  295. }
  296. props.x -= leftOffset;
  297. props.y -= topOffset;
  298. },
  299. /**
  300. * Returns the transformOrigin to use for an axis label based on the position of the axis
  301. * and the rotation of the label.
  302. *
  303. * @method _getTransformOrigin
  304. * @param {Number} rot The rotation (in degrees) of the label.
  305. * @return Array
  306. * @protected
  307. */
  308. _getTransformOrigin: function(rot)
  309. {
  310. var transformOrigin;
  311. if(rot === 0)
  312. {
  313. transformOrigin = [0, 0];
  314. }
  315. else if(rot === 90)
  316. {
  317. transformOrigin = [0.5, 0];
  318. }
  319. else if(rot === -90)
  320. {
  321. transformOrigin = [0.5, 1];
  322. }
  323. else
  324. {
  325. transformOrigin = [1, 0.5];
  326. }
  327. return transformOrigin;
  328. },
  329. /**
  330. * Adjust the position of the Axis widget's content box for internal axes.
  331. *
  332. * @method offsetNodeForTick
  333. * @param {Node} cb contentBox of the axis
  334. * @protected
  335. */
  336. offsetNodeForTick: function()
  337. {
  338. },
  339. /**
  340. * Sets the width of the axis based on its contents.
  341. *
  342. * @method setCalculatedSize
  343. * @protected
  344. */
  345. setCalculatedSize: function()
  346. {
  347. var host = this,
  348. graphic = this.get("graphic"),
  349. style = host.get("styles"),
  350. label = style.label,
  351. tickOffset = host.get("leftTickOffset"),
  352. max = host._maxLabelSize,
  353. totalTitleSize = this._totalTitleSize,
  354. ttl = Math.round(totalTitleSize + tickOffset + max + label.margin.right);
  355. if(this._explicitWidth)
  356. {
  357. ttl = this._explicitWidth;
  358. }
  359. this.set("calculatedWidth", ttl);
  360. graphic.set("x", ttl - tickOffset);
  361. }
  362. };
  363. Y.LeftAxisLayout = LeftAxisLayout;