Default.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. define("dojox/charting/axis2d/Default", ["dojo/_base/lang", "dojo/_base/array","dojo/_base/sniff", "dojo/_base/declare",
  2. "dojo/_base/connect", "dojo/_base/html", "dojo/dom-geometry", "./Invisible",
  3. "../scaler/common", "../scaler/linear", "./common", "dojox/gfx", "dojox/lang/utils"],
  4. function(lang, arr, has, declare, connect, html, domGeom, Invisible, scommon,
  5. lin, acommon, g, du){
  6. /*=====
  7. dojox.charting.axis2d.__AxisCtorArgs = function(
  8. vertical, fixUpper, fixLower, natural, leftBottom,
  9. includeZero, fixed, majorLabels, minorTicks, minorLabels, microTicks, htmlLabels,
  10. min, max, from, to, majorTickStep, minorTickStep, microTickStep,
  11. labels, labelFunc, maxLabelSize,
  12. stroke, majorTick, minorTick, microTick, tick,
  13. font, fontColor
  14. ){
  15. // summary:
  16. // Optional arguments used in the definition of an axis.
  17. //
  18. // vertical: Boolean?
  19. // A flag that says whether an axis is vertical (i.e. y axis) or horizontal. Default is false (horizontal).
  20. // fixUpper: String?
  21. // Align the greatest value on the axis with the specified tick level. Options are "major", "minor", "micro", or "none". Defaults to "none".
  22. // fixLower: String?
  23. // Align the smallest value on the axis with the specified tick level. Options are "major", "minor", "micro", or "none". Defaults to "none".
  24. // natural: Boolean?
  25. // Ensure tick marks are made on "natural" numbers. Defaults to false.
  26. // leftBottom: Boolean?
  27. // The position of a vertical axis; if true, will be placed against the left-bottom corner of the chart. Defaults to true.
  28. // includeZero: Boolean?
  29. // Include 0 on the axis rendering. Default is false.
  30. // fixed: Boolean?
  31. // Force all axis labels to be fixed numbers. Default is true.
  32. // majorLabels: Boolean?
  33. // Flag to draw all labels at major ticks. Default is true.
  34. // minorTicks: Boolean?
  35. // Flag to draw minor ticks on an axis. Default is true.
  36. // minorLabels: Boolean?
  37. // Flag to draw labels on minor ticks. Default is true.
  38. // microTicks: Boolean?
  39. // Flag to draw micro ticks on an axis. Default is false.
  40. // htmlLabels: Boolean?
  41. // Flag to use HTML (as opposed to the native vector graphics engine) to draw labels. Default is true.
  42. // min: Number?
  43. // The smallest value on an axis. Default is 0.
  44. // max: Number?
  45. // The largest value on an axis. Default is 1.
  46. // from: Number?
  47. // Force the chart to render data visible from this value. Default is 0.
  48. // to: Number?
  49. // Force the chart to render data visible to this value. Default is 1.
  50. // majorTickStep: Number?
  51. // The amount to skip before a major tick is drawn. Default is 4.
  52. // minorTickStep: Number?
  53. // The amount to skip before a minor tick is drawn. Default is 2.
  54. // microTickStep: Number?
  55. // The amount to skip before a micro tick is drawn. Default is 1.
  56. // labels: Object[]?
  57. // An array of labels for major ticks, with corresponding numeric values, ordered by value.
  58. // labelFunc: Function?
  59. // An optional function used to compute label values.
  60. // maxLabelSize: Number?
  61. // The maximum size, in pixels, for a label. To be used with the optional label function.
  62. // stroke: dojox.gfx.Stroke?
  63. // An optional stroke to be used for drawing an axis.
  64. // majorTick: Object?
  65. // An object containing a dojox.gfx.Stroke, and a length (number) for a major tick.
  66. // minorTick: Object?
  67. // An object containing a dojox.gfx.Stroke, and a length (number) for a minor tick.
  68. // microTick: Object?
  69. // An object containing a dojox.gfx.Stroke, and a length (number) for a micro tick.
  70. // tick: Object?
  71. // An object containing a dojox.gfx.Stroke, and a length (number) for a tick.
  72. // font: String?
  73. // An optional font definition (as used in the CSS font property) for labels.
  74. // fontColor: String|dojo.Color?
  75. // An optional color to be used in drawing labels.
  76. // enableCache: Boolean?
  77. // Whether the ticks and labels are cached from one rendering to another. This improves the rendering performance of
  78. // successive rendering but penalize the first rendering. For labels it is only working with gfx labels
  79. // not html ones. Default false.
  80. this.vertical = vertical;
  81. this.fixUpper = fixUpper;
  82. this.fixLower = fixLower;
  83. this.natural = natural;
  84. this.leftBottom = leftBottom;
  85. this.includeZero = includeZero;
  86. this.fixed = fixed;
  87. this.majorLabels = majorLabels;
  88. this.minorTicks = minorTicks;
  89. this.minorLabels = minorLabels;
  90. this.microTicks = microTicks;
  91. this.htmlLabels = htmlLabels;
  92. this.min = min;
  93. this.max = max;
  94. this.from = from;
  95. this.to = to;
  96. this.majorTickStep = majorTickStep;
  97. this.minorTickStep = minorTickStep;
  98. this.microTickStep = microTickStep;
  99. this.labels = labels;
  100. this.labelFunc = labelFunc;
  101. this.maxLabelSize = maxLabelSize;
  102. this.stroke = stroke;
  103. this.majorTick = majorTick;
  104. this.minorTick = minorTick;
  105. this.microTick = microTick;
  106. this.tick = tick;
  107. this.font = font;
  108. this.fontColor = fontColor;
  109. this.enableCache = enableCache;
  110. }
  111. var Invisible = dojox.charting.axis2d.Invisible
  112. =====*/
  113. var labelGap = 4, // in pixels
  114. centerAnchorLimit = 45; // in degrees
  115. return declare("dojox.charting.axis2d.Default", Invisible, {
  116. // summary:
  117. // The default axis object used in dojox.charting. See dojox.charting.Chart.addAxis for details.
  118. //
  119. // defaultParams: Object
  120. // The default parameters used to define any axis.
  121. // optionalParams: Object
  122. // Any optional parameters needed to define an axis.
  123. /*
  124. // TODO: the documentation tools need these to be pre-defined in order to pick them up
  125. // correctly, but the code here is partially predicated on whether or not the properties
  126. // actually exist. For now, we will leave these undocumented but in the code for later. -- TRT
  127. // opt: Object
  128. // The actual options used to define this axis, created at initialization.
  129. // scalar: Object
  130. // The calculated helper object to tell charts how to draw an axis and any data.
  131. // ticks: Object
  132. // The calculated tick object that helps a chart draw the scaling on an axis.
  133. // dirty: Boolean
  134. // The state of the axis (whether it needs to be redrawn or not)
  135. // scale: Number
  136. // The current scale of the axis.
  137. // offset: Number
  138. // The current offset of the axis.
  139. opt: null,
  140. scalar: null,
  141. ticks: null,
  142. dirty: true,
  143. scale: 1,
  144. offset: 0,
  145. */
  146. defaultParams: {
  147. vertical: false, // true for vertical axis
  148. fixUpper: "none", // align the upper on ticks: "major", "minor", "micro", "none"
  149. fixLower: "none", // align the lower on ticks: "major", "minor", "micro", "none"
  150. natural: false, // all tick marks should be made on natural numbers
  151. leftBottom: true, // position of the axis, used with "vertical"
  152. includeZero: false, // 0 should be included
  153. fixed: true, // all labels are fixed numbers
  154. majorLabels: true, // draw major labels
  155. minorTicks: true, // draw minor ticks
  156. minorLabels: true, // draw minor labels
  157. microTicks: false, // draw micro ticks
  158. rotation: 0, // label rotation angle in degrees
  159. htmlLabels: true, // use HTML to draw labels
  160. enableCache: false // whether we cache or not
  161. },
  162. optionalParams: {
  163. min: 0, // minimal value on this axis
  164. max: 1, // maximal value on this axis
  165. from: 0, // visible from this value
  166. to: 1, // visible to this value
  167. majorTickStep: 4, // major tick step
  168. minorTickStep: 2, // minor tick step
  169. microTickStep: 1, // micro tick step
  170. labels: [], // array of labels for major ticks
  171. // with corresponding numeric values
  172. // ordered by values
  173. labelFunc: null, // function to compute label values
  174. maxLabelSize: 0, // size in px. For use with labelFunc
  175. maxLabelCharCount: 0, // size in word count.
  176. trailingSymbol: null,
  177. // TODO: add support for minRange!
  178. // minRange: 1, // smallest distance from min allowed on the axis
  179. // theme components
  180. stroke: {}, // stroke for an axis
  181. majorTick: {}, // stroke + length for a tick
  182. minorTick: {}, // stroke + length for a tick
  183. microTick: {}, // stroke + length for a tick
  184. tick: {}, // stroke + length for a tick
  185. font: "", // font for labels
  186. fontColor: "", // color for labels as a string
  187. title: "", // axis title
  188. titleGap: 0, // gap between axis title and axis label
  189. titleFont: "", // axis title font
  190. titleFontColor: "", // axis title font color
  191. titleOrientation: "" // "axis" means the title facing the axis, "away" means facing away
  192. },
  193. constructor: function(chart, kwArgs){
  194. // summary:
  195. // The constructor for an axis.
  196. // chart: dojox.charting.Chart
  197. // The chart the axis belongs to.
  198. // kwArgs: dojox.charting.axis2d.__AxisCtorArgs?
  199. // Any optional keyword arguments to be used to define this axis.
  200. this.opt = lang.clone(this.defaultParams);
  201. du.updateWithObject(this.opt, kwArgs);
  202. du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
  203. if(this.opt.enableCache){
  204. this._textFreePool = [];
  205. this._lineFreePool = [];
  206. this._textUsePool = [];
  207. this._lineUsePool = [];
  208. }
  209. },
  210. getOffsets: function(){
  211. // summary:
  212. // Get the physical offset values for this axis (used in drawing data series).
  213. // returns: Object
  214. // The calculated offsets in the form of { l, r, t, b } (left, right, top, bottom).
  215. var s = this.scaler, offsets = { l: 0, r: 0, t: 0, b: 0 };
  216. if(!s){
  217. return offsets;
  218. }
  219. var o = this.opt, labelWidth = 0, a, b, c, d,
  220. gl = scommon.getNumericLabel,
  221. offset = 0, ma = s.major, mi = s.minor,
  222. ta = this.chart.theme.axis,
  223. // TODO: we use one font --- of major tick, we need to use major and minor fonts
  224. taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font),
  225. taTitleFont = o.titleFont || (ta.tick && ta.tick.titleFont),
  226. taTitleGap = (o.titleGap==0) ? 0 : o.titleGap || (ta.tick && ta.tick.titleGap) || 15,
  227. taMajorTick = this.chart.theme.getTick("major", o),
  228. taMinorTick = this.chart.theme.getTick("minor", o),
  229. size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0,
  230. tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0,
  231. rotation = o.rotation % 360, leftBottom = o.leftBottom,
  232. cosr = Math.abs(Math.cos(rotation * Math.PI / 180)),
  233. sinr = Math.abs(Math.sin(rotation * Math.PI / 180));
  234. this.trailingSymbol = (o.trailingSymbol === undefined || o.trailingSymbol === null) ? this.trailingSymbol : o.trailingSymbol;
  235. if(rotation < 0){
  236. rotation += 360;
  237. }
  238. if(size){
  239. // we need width of all labels
  240. if(this.labels){
  241. labelWidth = this._groupLabelWidth(this.labels, taFont, o.maxLabelCharCount);
  242. }else{
  243. labelWidth = this._groupLabelWidth([
  244. gl(ma.start, ma.prec, o),
  245. gl(ma.start + ma.count * ma.tick, ma.prec, o),
  246. gl(mi.start, mi.prec, o),
  247. gl(mi.start + mi.count * mi.tick, mi.prec, o)
  248. ], taFont, o.maxLabelCharCount);
  249. }
  250. labelWidth = o.maxLabelSize ? Math.min(o.maxLabelSize, labelWidth) : labelWidth;
  251. if(this.vertical){
  252. var side = leftBottom ? "l" : "r";
  253. switch(rotation){
  254. case 0:
  255. case 180:
  256. offsets[side] = labelWidth;
  257. offsets.t = offsets.b = size / 2;
  258. break;
  259. case 90:
  260. case 270:
  261. offsets[side] = size;
  262. offsets.t = offsets.b = labelWidth / 2;
  263. break;
  264. default:
  265. if(rotation <= centerAnchorLimit || (180 < rotation && rotation <= (180 + centerAnchorLimit))){
  266. offsets[side] = size * sinr / 2 + labelWidth * cosr;
  267. offsets[leftBottom ? "t" : "b"] = size * cosr / 2 + labelWidth * sinr;
  268. offsets[leftBottom ? "b" : "t"] = size * cosr / 2;
  269. }else if(rotation > (360 - centerAnchorLimit) || (180 > rotation && rotation > (180 - centerAnchorLimit))){
  270. offsets[side] = size * sinr / 2 + labelWidth * cosr;
  271. offsets[leftBottom ? "b" : "t"] = size * cosr / 2 + labelWidth * sinr;
  272. offsets[leftBottom ? "t" : "b"] = size * cosr / 2;
  273. }else if(rotation < 90 || (180 < rotation && rotation < 270)){
  274. offsets[side] = size * sinr + labelWidth * cosr;
  275. offsets[leftBottom ? "t" : "b"] = size * cosr + labelWidth * sinr;
  276. }else{
  277. offsets[side] = size * sinr + labelWidth * cosr;
  278. offsets[leftBottom ? "b" : "t"] = size * cosr + labelWidth * sinr;
  279. }
  280. break;
  281. }
  282. offsets[side] += labelGap + Math.max(taMajorTick.length, taMinorTick.length) + (o.title ? (tsize + taTitleGap) : 0);
  283. }else{
  284. var side = leftBottom ? "b" : "t";
  285. switch(rotation){
  286. case 0:
  287. case 180:
  288. offsets[side] = size;
  289. offsets.l = offsets.r = labelWidth / 2;
  290. break;
  291. case 90:
  292. case 270:
  293. offsets[side] = labelWidth;
  294. offsets.l = offsets.r = size / 2;
  295. break;
  296. default:
  297. if((90 - centerAnchorLimit) <= rotation && rotation <= 90 || (270 - centerAnchorLimit) <= rotation && rotation <= 270){
  298. offsets[side] = size * sinr / 2 + labelWidth * cosr;
  299. offsets[leftBottom ? "r" : "l"] = size * cosr / 2 + labelWidth * sinr;
  300. offsets[leftBottom ? "l" : "r"] = size * cosr / 2;
  301. }else if(90 <= rotation && rotation <= (90 + centerAnchorLimit) || 270 <= rotation && rotation <= (270 + centerAnchorLimit)){
  302. offsets[side] = size * sinr / 2 + labelWidth * cosr;
  303. offsets[leftBottom ? "l" : "r"] = size * cosr / 2 + labelWidth * sinr;
  304. offsets[leftBottom ? "r" : "l"] = size * cosr / 2;
  305. }else if(rotation < centerAnchorLimit || (180 < rotation && rotation < (180 - centerAnchorLimit))){
  306. offsets[side] = size * sinr + labelWidth * cosr;
  307. offsets[leftBottom ? "r" : "l"] = size * cosr + labelWidth * sinr;
  308. }else{
  309. offsets[side] = size * sinr + labelWidth * cosr;
  310. offsets[leftBottom ? "l" : "r"] = size * cosr + labelWidth * sinr;
  311. }
  312. break;
  313. }
  314. offsets[side] += labelGap + Math.max(taMajorTick.length, taMinorTick.length) + (o.title ? (tsize + taTitleGap) : 0);
  315. }
  316. }
  317. if(labelWidth){
  318. this._cachedLabelWidth = labelWidth;
  319. }
  320. return offsets; // Object
  321. },
  322. cleanGroup: function(creator){
  323. if(this.opt.enableCache && this.group){
  324. this._lineFreePool = this._lineFreePool.concat(this._lineUsePool);
  325. this._lineUsePool = [];
  326. this._textFreePool = this._textFreePool.concat(this._textUsePool);
  327. this._textUsePool = [];
  328. }
  329. this.inherited(arguments);
  330. },
  331. createText: function(labelType, creator, x, y, align, textContent, font, fontColor, labelWidth){
  332. if(!this.opt.enableCache || labelType=="html"){
  333. return acommon.createText[labelType](
  334. this.chart,
  335. creator,
  336. x,
  337. y,
  338. align,
  339. textContent,
  340. font,
  341. fontColor,
  342. labelWidth
  343. );
  344. }
  345. var text;
  346. if (this._textFreePool.length > 0){
  347. text = this._textFreePool.pop();
  348. text.setShape({x: x, y: y, text: textContent, align: align});
  349. // For now all items share the same font, no need to re-set it
  350. //.setFont(font).setFill(fontColor);
  351. // was cleared, add it back
  352. creator.add(text);
  353. }else{
  354. text = acommon.createText[labelType](
  355. this.chart,
  356. creator,
  357. x,
  358. y,
  359. align,
  360. textContent,
  361. font,
  362. fontColor,
  363. labelWidth
  364. ); }
  365. this._textUsePool.push(text);
  366. return text;
  367. },
  368. createLine: function(creator, params){
  369. var line;
  370. if(this.opt.enableCache && this._lineFreePool.length > 0){
  371. line = this._lineFreePool.pop();
  372. line.setShape(params);
  373. // was cleared, add it back
  374. creator.add(line);
  375. }else{
  376. line = creator.createLine(params);
  377. }
  378. if(this.opt.enableCache){
  379. this._lineUsePool.push(line);
  380. }
  381. return line;
  382. },
  383. render: function(dim, offsets){
  384. // summary:
  385. // Render/draw the axis.
  386. // dim: Object
  387. // An object of the form { width, height}.
  388. // offsets: Object
  389. // An object of the form { l, r, t, b }.
  390. // returns: dojox.charting.axis2d.Default
  391. // The reference to the axis for functional chaining.
  392. if(!this.dirty){
  393. return this; // dojox.charting.axis2d.Default
  394. }
  395. // prepare variable
  396. var o = this.opt, ta = this.chart.theme.axis, leftBottom = o.leftBottom, rotation = o.rotation % 360,
  397. start, stop, titlePos, titleRotation=0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign,
  398. // TODO: we use one font --- of major tick, we need to use major and minor fonts
  399. taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font),
  400. taTitleFont = o.titleFont || (ta.tick && ta.tick.titleFont),
  401. // TODO: we use one font color --- we need to use different colors
  402. taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black",
  403. taTitleFontColor = o.titleFontColor || (ta.tick && ta.tick.titleFontColor) || "black",
  404. taTitleGap = (o.titleGap==0) ? 0 : o.titleGap || (ta.tick && ta.tick.titleGap) || 15,
  405. taTitleOrientation = o.titleOrientation || (ta.tick && ta.tick.titleOrientation) || "axis",
  406. taMajorTick = this.chart.theme.getTick("major", o),
  407. taMinorTick = this.chart.theme.getTick("minor", o),
  408. taMicroTick = this.chart.theme.getTick("micro", o),
  409. tickSize = Math.max(taMajorTick.length, taMinorTick.length, taMicroTick.length),
  410. taStroke = "stroke" in o ? o.stroke : ta.stroke,
  411. size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0,
  412. cosr = Math.abs(Math.cos(rotation * Math.PI / 180)),
  413. sinr = Math.abs(Math.sin(rotation * Math.PI / 180)),
  414. tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0;
  415. if(rotation < 0){
  416. rotation += 360;
  417. }
  418. if(this.vertical){
  419. start = {y: dim.height - offsets.b};
  420. stop = {y: offsets.t};
  421. titlePos = {y: (dim.height - offsets.b + offsets.t)/2};
  422. titleOffset = size * sinr + (this._cachedLabelWidth || 0) * cosr + labelGap + Math.max(taMajorTick.length, taMinorTick.length) + tsize + taTitleGap;
  423. axisVector = {x: 0, y: -1};
  424. labelOffset = {x: 0, y: 0};
  425. tickVector = {x: 1, y: 0};
  426. anchorOffset = {x: labelGap, y: 0};
  427. switch(rotation){
  428. case 0:
  429. labelAlign = "end";
  430. labelOffset.y = size * 0.4;
  431. break;
  432. case 90:
  433. labelAlign = "middle";
  434. labelOffset.x = -size;
  435. break;
  436. case 180:
  437. labelAlign = "start";
  438. labelOffset.y = -size * 0.4;
  439. break;
  440. case 270:
  441. labelAlign = "middle";
  442. break;
  443. default:
  444. if(rotation < centerAnchorLimit){
  445. labelAlign = "end";
  446. labelOffset.y = size * 0.4;
  447. }else if(rotation < 90){
  448. labelAlign = "end";
  449. labelOffset.y = size * 0.4;
  450. }else if(rotation < (180 - centerAnchorLimit)){
  451. labelAlign = "start";
  452. }else if(rotation < (180 + centerAnchorLimit)){
  453. labelAlign = "start";
  454. labelOffset.y = -size * 0.4;
  455. }else if(rotation < 270){
  456. labelAlign = "start";
  457. labelOffset.x = leftBottom ? 0 : size * 0.4;
  458. }else if(rotation < (360 - centerAnchorLimit)){
  459. labelAlign = "end";
  460. labelOffset.x = leftBottom ? 0 : size * 0.4;
  461. }else{
  462. labelAlign = "end";
  463. labelOffset.y = size * 0.4;
  464. }
  465. }
  466. if(leftBottom){
  467. start.x = stop.x = offsets.l;
  468. titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 90 : 270;
  469. titlePos.x = offsets.l - titleOffset + (titleRotation == 270 ? tsize : 0);
  470. tickVector.x = -1;
  471. anchorOffset.x = -anchorOffset.x;
  472. }else{
  473. start.x = stop.x = dim.width - offsets.r;
  474. titleRotation = (taTitleOrientation && taTitleOrientation == "axis") ? 90 : 270;
  475. titlePos.x = dim.width - offsets.r + titleOffset - (titleRotation == 270 ? 0 : tsize);
  476. switch(labelAlign){
  477. case "start":
  478. labelAlign = "end";
  479. break;
  480. case "end":
  481. labelAlign = "start";
  482. break;
  483. case "middle":
  484. labelOffset.x += size;
  485. break;
  486. }
  487. }
  488. }else{
  489. start = {x: offsets.l};
  490. stop = {x: dim.width - offsets.r};
  491. titlePos = {x: (dim.width - offsets.r + offsets.l)/2};
  492. titleOffset = size * cosr + (this._cachedLabelWidth || 0) * sinr + labelGap + Math.max(taMajorTick.length, taMinorTick.length) + tsize + taTitleGap;
  493. axisVector = {x: 1, y: 0};
  494. labelOffset = {x: 0, y: 0};
  495. tickVector = {x: 0, y: 1};
  496. anchorOffset = {x: 0, y: labelGap};
  497. switch(rotation){
  498. case 0:
  499. labelAlign = "middle";
  500. labelOffset.y = size;
  501. break;
  502. case 90:
  503. labelAlign = "start";
  504. labelOffset.x = -size * 0.4;
  505. break;
  506. case 180:
  507. labelAlign = "middle";
  508. break;
  509. case 270:
  510. labelAlign = "end";
  511. labelOffset.x = size * 0.4;
  512. break;
  513. default:
  514. if(rotation < (90 - centerAnchorLimit)){
  515. labelAlign = "start";
  516. labelOffset.y = leftBottom ? size : 0;
  517. }else if(rotation < (90 + centerAnchorLimit)){
  518. labelAlign = "start";
  519. labelOffset.x = -size * 0.4;
  520. }else if(rotation < 180){
  521. labelAlign = "start";
  522. labelOffset.y = leftBottom ? 0 : -size;
  523. }else if(rotation < (270 - centerAnchorLimit)){
  524. labelAlign = "end";
  525. labelOffset.y = leftBottom ? 0 : -size;
  526. }else if(rotation < (270 + centerAnchorLimit)){
  527. labelAlign = "end";
  528. labelOffset.y = leftBottom ? size * 0.4 : 0;
  529. }else{
  530. labelAlign = "end";
  531. labelOffset.y = leftBottom ? size : 0;
  532. }
  533. }
  534. if(leftBottom){
  535. start.y = stop.y = dim.height - offsets.b;
  536. titleRotation = (taTitleOrientation && taTitleOrientation == "axis") ? 180 : 0;
  537. titlePos.y = dim.height - offsets.b + titleOffset - (titleRotation ? tsize : 0);
  538. }else{
  539. start.y = stop.y = offsets.t;
  540. titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0;
  541. titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize);
  542. tickVector.y = -1;
  543. anchorOffset.y = -anchorOffset.y;
  544. switch(labelAlign){
  545. case "start":
  546. labelAlign = "end";
  547. break;
  548. case "end":
  549. labelAlign = "start";
  550. break;
  551. case "middle":
  552. labelOffset.y -= size;
  553. break;
  554. }
  555. }
  556. }
  557. // render shapes
  558. this.cleanGroup();
  559. try{
  560. var s = this.group,
  561. c = this.scaler,
  562. t = this.ticks,
  563. canLabel,
  564. f = lin.getTransformerFromModel(this.scaler),
  565. // GFX Canvas now supports labels, so let's _not_ fallback to HTML anymore on canvas, just use
  566. // HTML labels if explicitly asked + no rotation + no IE + no Opera
  567. labelType = (!o.title || !titleRotation) && !rotation && this.opt.htmlLabels && !has("ie") && !has("opera") ? "html" : "gfx",
  568. dx = tickVector.x * taMajorTick.length,
  569. dy = tickVector.y * taMajorTick.length;
  570. s.createLine({
  571. x1: start.x,
  572. y1: start.y,
  573. x2: stop.x,
  574. y2: stop.y
  575. }).setStroke(taStroke);
  576. //create axis title
  577. if(o.title){
  578. var axisTitle = acommon.createText[labelType](
  579. this.chart,
  580. s,
  581. titlePos.x,
  582. titlePos.y,
  583. "middle",
  584. o.title,
  585. taTitleFont,
  586. taTitleFontColor
  587. );
  588. if(labelType == "html"){
  589. this.htmlElements.push(axisTitle);
  590. }else{
  591. //as soon as rotation is provided, labelType won't be "html"
  592. //rotate gfx labels
  593. axisTitle.setTransform(g.matrix.rotategAt(titleRotation, titlePos.x, titlePos.y));
  594. }
  595. }
  596. // go out nicely instead of try/catch
  597. if(t==null){
  598. this.dirty = false;
  599. return this;
  600. }
  601. arr.forEach(t.major, function(tick){
  602. var offset = f(tick.value), elem,
  603. x = start.x + axisVector.x * offset,
  604. y = start.y + axisVector.y * offset;
  605. this.createLine(s, {
  606. x1: x, y1: y,
  607. x2: x + dx,
  608. y2: y + dy
  609. }).setStroke(taMajorTick);
  610. if(tick.label){
  611. var label = o.maxLabelCharCount ? this.getTextWithLimitCharCount(tick.label, taFont, o.maxLabelCharCount) : {
  612. text: tick.label,
  613. truncated: false
  614. };
  615. label = o.maxLabelSize ? this.getTextWithLimitLength(label.text, taFont, o.maxLabelSize, label.truncated) : label;
  616. elem = this.createText(labelType,
  617. s,
  618. x + dx + anchorOffset.x + (rotation ? 0 : labelOffset.x),
  619. y + dy + anchorOffset.y + (rotation ? 0 : labelOffset.y),
  620. labelAlign,
  621. label.text,
  622. taFont,
  623. taFontColor
  624. //this._cachedLabelWidth
  625. );
  626. // if bidi support was required, the textDir is "auto" and truncation
  627. // took place, we need to update the dir of the element for cases as:
  628. // Fool label: 111111W (W for bidi character)
  629. // truncated label: 11...
  630. // in this case for auto textDir the dir will be "ltr" which is wrong.
  631. if(this.chart.truncateBidi && label.truncated){
  632. this.chart.truncateBidi(elem, tick.label, labelType);
  633. }
  634. label.truncated && this.labelTooltip(elem, this.chart, tick.label, label.text, taFont, labelType);
  635. if(labelType == "html"){
  636. this.htmlElements.push(elem);
  637. }else if(rotation){
  638. elem.setTransform([
  639. {dx: labelOffset.x, dy: labelOffset.y},
  640. g.matrix.rotategAt(
  641. rotation,
  642. x + dx + anchorOffset.x,
  643. y + dy + anchorOffset.y
  644. )
  645. ]);
  646. }
  647. }
  648. }, this);
  649. dx = tickVector.x * taMinorTick.length;
  650. dy = tickVector.y * taMinorTick.length;
  651. canLabel = c.minMinorStep <= c.minor.tick * c.bounds.scale;
  652. arr.forEach(t.minor, function(tick){
  653. var offset = f(tick.value), elem,
  654. x = start.x + axisVector.x * offset,
  655. y = start.y + axisVector.y * offset;
  656. this.createLine(s, {
  657. x1: x, y1: y,
  658. x2: x + dx,
  659. y2: y + dy
  660. }).setStroke(taMinorTick);
  661. if(canLabel && tick.label){
  662. var label = o.maxLabelCharCount ? this.getTextWithLimitCharCount(tick.label, taFont, o.maxLabelCharCount) : {
  663. text: tick.label,
  664. truncated: false
  665. };
  666. label = o.maxLabelSize ? this.getTextWithLimitLength(label.text, taFont, o.maxLabelSize, label.truncated) : label;
  667. elem = this.createText(labelType,
  668. s,
  669. x + dx + anchorOffset.x + (rotation ? 0 : labelOffset.x),
  670. y + dy + anchorOffset.y + (rotation ? 0 : labelOffset.y),
  671. labelAlign,
  672. label.text,
  673. taFont,
  674. taFontColor
  675. //this._cachedLabelWidth
  676. );
  677. // if bidi support was required, the textDir is "auto" and truncation
  678. // took place, we need to update the dir of the element for cases as:
  679. // Fool label: 111111W (W for bidi character)
  680. // truncated label: 11...
  681. // in this case for auto textDir the dir will be "ltr" which is wrong.
  682. if(this.chart.getTextDir && label.truncated){
  683. this.chart.truncateBidi(elem, tick.label, labelType);
  684. }
  685. label.truncated && this.labelTooltip(elem, this.chart, tick.label, label.text, taFont, labelType);
  686. if(labelType == "html"){
  687. this.htmlElements.push(elem);
  688. }else if(rotation){
  689. elem.setTransform([
  690. {dx: labelOffset.x, dy: labelOffset.y},
  691. g.matrix.rotategAt(
  692. rotation,
  693. x + dx + anchorOffset.x,
  694. y + dy + anchorOffset.y
  695. )
  696. ]);
  697. }
  698. }
  699. }, this);
  700. dx = tickVector.x * taMicroTick.length;
  701. dy = tickVector.y * taMicroTick.length;
  702. arr.forEach(t.micro, function(tick){
  703. var offset = f(tick.value), elem,
  704. x = start.x + axisVector.x * offset,
  705. y = start.y + axisVector.y * offset;
  706. this.createLine(s, {
  707. x1: x, y1: y,
  708. x2: x + dx,
  709. y2: y + dy
  710. }).setStroke(taMicroTick);
  711. }, this);
  712. }catch(e){
  713. // squelch
  714. }
  715. this.dirty = false;
  716. return this; // dojox.charting.axis2d.Default
  717. },
  718. labelTooltip: function(elem, chart, label, truncatedLabel, font, elemType){
  719. var modules = ["dijit/Tooltip"];
  720. var aroundRect = {type: "rect"}, position = ["above", "below"],
  721. fontWidth = g._base._getTextBox(truncatedLabel, {font: font}).w || 0,
  722. fontHeight = font ? g.normalizedLength(g.splitFontString(font).size) : 0;
  723. if(elemType == "html"){
  724. lang.mixin(aroundRect, html.coords(elem.firstChild, true));
  725. aroundRect.width = Math.ceil(fontWidth);
  726. aroundRect.height = Math.ceil(fontHeight);
  727. this._events.push({
  728. shape: dojo,
  729. handle: connect.connect(elem.firstChild, "onmouseover", this, function(e){
  730. require(modules, function(Tooltip){
  731. Tooltip.show(label, aroundRect, position);
  732. });
  733. })
  734. });
  735. this._events.push({
  736. shape: dojo,
  737. handle: connect.connect(elem.firstChild, "onmouseout", this, function(e){
  738. require(modules, function(Tooltip){
  739. Tooltip.hide(aroundRect);
  740. });
  741. })
  742. });
  743. }else{
  744. var shp = elem.getShape(),
  745. lt = html.coords(chart.node, true);
  746. aroundRect = lang.mixin(aroundRect, {
  747. x: shp.x - fontWidth / 2,
  748. y: shp.y
  749. });
  750. aroundRect.x += lt.x;
  751. aroundRect.y += lt.y;
  752. aroundRect.x = Math.round(aroundRect.x);
  753. aroundRect.y = Math.round(aroundRect.y);
  754. aroundRect.width = Math.ceil(fontWidth);
  755. aroundRect.height = Math.ceil(fontHeight);
  756. this._events.push({
  757. shape: elem,
  758. handle: elem.connect("onmouseenter", this, function(e){
  759. require(modules, function(Tooltip){
  760. Tooltip.show(label, aroundRect, position);
  761. });
  762. })
  763. });
  764. this._events.push({
  765. shape: elem,
  766. handle: elem.connect("onmouseleave", this, function(e){
  767. require(modules, function(Tooltip){
  768. Tooltip.hide(aroundRect);
  769. });
  770. })
  771. });
  772. }
  773. }
  774. });
  775. });