[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('axis', function (Y, NAME) { 9 10 /** 11 * Provides base functionality for drawing chart axes. 12 * 13 * @module charts 14 * @submodule axis 15 */ 16 var CONFIG = Y.config, 17 DOCUMENT = CONFIG.doc, 18 Y_Lang = Y.Lang, 19 IS_STRING = Y_Lang.isString, 20 Y_DOM = Y.DOM, 21 LeftAxisLayout, 22 RightAxisLayout, 23 BottomAxisLayout, 24 TopAxisLayout; 25 /** 26 * Algorithmic strategy for rendering a left axis. 27 * 28 * @class LeftAxisLayout 29 * @constructor 30 * @submodule axis 31 */ 32 LeftAxisLayout = function() {}; 33 34 LeftAxisLayout.prototype = { 35 /** 36 * Default margins for text fields. 37 * 38 * @private 39 * @method _getDefaultMargins 40 * @return Object 41 */ 42 _getDefaultMargins: function() 43 { 44 return { 45 top: 0, 46 left: 0, 47 right: 4, 48 bottom: 0 49 }; 50 }, 51 52 /** 53 * Sets the length of the tick on either side of the axis line. 54 * 55 * @method setTickOffset 56 * @protected 57 */ 58 setTickOffsets: function() 59 { 60 var host = this, 61 majorTicks = host.get("styles").majorTicks, 62 tickLength = majorTicks.length, 63 halfTick = tickLength * 0.5, 64 display = majorTicks.display; 65 host.set("topTickOffset", 0); 66 host.set("bottomTickOffset", 0); 67 68 switch(display) 69 { 70 case "inside" : 71 host.set("rightTickOffset", tickLength); 72 host.set("leftTickOffset", 0); 73 break; 74 case "outside" : 75 host.set("rightTickOffset", 0); 76 host.set("leftTickOffset", tickLength); 77 break; 78 case "cross": 79 host.set("rightTickOffset", halfTick); 80 host.set("leftTickOffset", halfTick); 81 break; 82 default: 83 host.set("rightTickOffset", 0); 84 host.set("leftTickOffset", 0); 85 break; 86 } 87 }, 88 89 /** 90 * Draws a tick 91 * 92 * @method drawTick 93 * @param {Path} path reference to the path `Path` element in which to draw the tick. 94 * @param {Object} pt Point on the axis in which the tick will intersect. 95 * @param {Object} tickStyle Hash of properties to apply to the tick. 96 * @protected 97 */ 98 drawTick: function(path, pt, tickStyles) 99 { 100 var host = this, 101 style = host.get("styles"), 102 padding = style.padding, 103 tickLength = tickStyles.length, 104 start = {x:padding.left, y:pt.y}, 105 end = {x:tickLength + padding.left, y:pt.y}; 106 host.drawLine(path, start, end); 107 }, 108 109 /** 110 * Calculates the coordinates for the first point on an axis. 111 * 112 * @method getLineStart 113 * @return {Object} 114 * @protected 115 */ 116 getLineStart: function() 117 { 118 var style = this.get("styles"), 119 padding = style.padding, 120 majorTicks = style.majorTicks, 121 tickLength = majorTicks.length, 122 display = majorTicks.display, 123 pt = {x:padding.left, y:0}; 124 if(display === "outside") 125 { 126 pt.x += tickLength; 127 } 128 else if(display === "cross") 129 { 130 pt.x += tickLength/2; 131 } 132 return pt; 133 }, 134 135 /** 136 * Calculates the point for a label. 137 * 138 * @method getLabelPoint 139 * @param {Object} point Point on the axis in which the tick will intersect. 140 * @return {Object} 141 * @protected 142 */ 143 getLabelPoint: function(point) 144 { 145 return {x:point.x - this.get("leftTickOffset"), y:point.y}; 146 }, 147 148 /** 149 * Updates the value for the `maxLabelSize` for use in calculating total size. 150 * 151 * @method updateMaxLabelSize 152 * @param {HTMLElement} label to measure 153 * @protected 154 */ 155 updateMaxLabelSize: function(labelWidth, labelHeight) 156 { 157 var host = this, 158 props = this._labelRotationProps, 159 rot = props.rot, 160 absRot = props.absRot, 161 sinRadians = props.sinRadians, 162 cosRadians = props.cosRadians, 163 max; 164 if(rot === 0) 165 { 166 max = labelWidth; 167 } 168 else if(absRot === 90) 169 { 170 max = labelHeight; 171 } 172 else 173 { 174 max = (cosRadians * labelWidth) + (sinRadians * labelHeight); 175 } 176 host._maxLabelSize = Math.max(host._maxLabelSize, max); 177 }, 178 179 /** 180 * Determines the available label width when the axis width has been explicitly set. 181 * 182 * @method getExplicitlySized 183 * @return Boolean 184 * @protected 185 */ 186 getExplicitlySized: function(styles) 187 { 188 if(this._explicitWidth) 189 { 190 var host = this, 191 w = host._explicitWidth, 192 totalTitleSize = host._totalTitleSize, 193 leftTickOffset = host.get("leftTickOffset"), 194 margin = styles.label.margin.right; 195 host._maxLabelSize = w - (leftTickOffset + margin + totalTitleSize); 196 return true; 197 } 198 return false; 199 }, 200 201 /** 202 * Rotate and position title. 203 * 204 * @method positionTitle 205 * @param {HTMLElement} label to rotate position 206 * @protected 207 */ 208 positionTitle: function(label) 209 { 210 var host = this, 211 bounds = host._titleBounds, 212 margin = host.get("styles").title.margin, 213 props = host._titleRotationProps, 214 w = bounds.right - bounds.left, 215 labelWidth = label.offsetWidth, 216 labelHeight = label.offsetHeight, 217 x = (labelWidth * -0.5) + (w * 0.5), 218 y = (host.get("height") * 0.5) - (labelHeight * 0.5); 219 props.labelWidth = labelWidth; 220 props.labelHeight = labelHeight; 221 if(margin && margin.left) 222 { 223 x += margin.left; 224 } 225 props.x = x; 226 props.y = y; 227 props.transformOrigin = [0.5, 0.5]; 228 host._rotate(label, props); 229 }, 230 231 /** 232 * Rotate and position labels. 233 * 234 * @method positionLabel 235 * @param {HTMLElement} label to rotate position 236 * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned 237 * against. 238 * @protected 239 */ 240 positionLabel: function(label, pt, styles, i) 241 { 242 var host = this, 243 offset = parseFloat(styles.label.offset), 244 tickOffset = host.get("leftTickOffset"), 245 totalTitleSize = this._totalTitleSize, 246 leftOffset = pt.x + totalTitleSize - tickOffset, 247 topOffset = pt.y, 248 props = this._labelRotationProps, 249 rot = props.rot, 250 absRot = props.absRot, 251 maxLabelSize = host._maxLabelSize, 252 labelWidth = this._labelWidths[i], 253 labelHeight = this._labelHeights[i]; 254 if(rot === 0) 255 { 256 leftOffset -= labelWidth; 257 topOffset -= labelHeight * offset; 258 } 259 else if(rot === 90) 260 { 261 leftOffset -= labelWidth * 0.5; 262 topOffset = topOffset + labelWidth/2 - (labelWidth * offset); 263 } 264 else if(rot === -90) 265 { 266 leftOffset -= labelWidth * 0.5; 267 topOffset = topOffset - labelHeight + labelWidth/2 - (labelWidth * offset); 268 } 269 else 270 { 271 leftOffset -= labelWidth + (labelHeight * absRot/360); 272 topOffset -= labelHeight * offset; 273 } 274 props.labelWidth = labelWidth; 275 props.labelHeight = labelHeight; 276 props.x = Math.round(maxLabelSize + leftOffset); 277 props.y = Math.round(topOffset); 278 this._rotate(label, props); 279 }, 280 281 /** 282 * Adjusts the coordinates of an axis label based on the rotation. 283 * 284 * @method _setRotationCoords 285 * @param {Object} props Coordinates, dimension and rotation properties of the label. 286 * @protected 287 */ 288 _setRotationCoords: function(props) 289 { 290 var rot = props.rot, 291 absRot = props.absRot, 292 leftOffset, 293 topOffset, 294 labelWidth = props.labelWidth, 295 labelHeight = props.labelHeight; 296 if(rot === 0) 297 { 298 leftOffset = labelWidth; 299 topOffset = labelHeight * 0.5; 300 } 301 else if(rot === 90) 302 { 303 topOffset = 0; 304 leftOffset = labelWidth * 0.5; 305 } 306 else if(rot === -90) 307 { 308 leftOffset = labelWidth * 0.5; 309 topOffset = labelHeight; 310 } 311 else 312 { 313 leftOffset = labelWidth + (labelHeight * absRot/360); 314 topOffset = labelHeight * 0.5; 315 } 316 props.x -= leftOffset; 317 props.y -= topOffset; 318 }, 319 320 /** 321 * Returns the transformOrigin to use for an axis label based on the position of the axis 322 * and the rotation of the label. 323 * 324 * @method _getTransformOrigin 325 * @param {Number} rot The rotation (in degrees) of the label. 326 * @return Array 327 * @protected 328 */ 329 _getTransformOrigin: function(rot) 330 { 331 var transformOrigin; 332 if(rot === 0) 333 { 334 transformOrigin = [0, 0]; 335 } 336 else if(rot === 90) 337 { 338 transformOrigin = [0.5, 0]; 339 } 340 else if(rot === -90) 341 { 342 transformOrigin = [0.5, 1]; 343 } 344 else 345 { 346 transformOrigin = [1, 0.5]; 347 } 348 return transformOrigin; 349 }, 350 351 /** 352 * Adjust the position of the Axis widget's content box for internal axes. 353 * 354 * @method offsetNodeForTick 355 * @param {Node} cb contentBox of the axis 356 * @protected 357 */ 358 offsetNodeForTick: function() 359 { 360 }, 361 362 /** 363 * Sets the width of the axis based on its contents. 364 * 365 * @method setCalculatedSize 366 * @protected 367 */ 368 setCalculatedSize: function() 369 { 370 var host = this, 371 graphic = this.get("graphic"), 372 style = host.get("styles"), 373 label = style.label, 374 tickOffset = host.get("leftTickOffset"), 375 max = host._maxLabelSize, 376 totalTitleSize = this._totalTitleSize, 377 ttl = Math.round(totalTitleSize + tickOffset + max + label.margin.right); 378 if(this._explicitWidth) 379 { 380 ttl = this._explicitWidth; 381 } 382 this.set("calculatedWidth", ttl); 383 graphic.set("x", ttl - tickOffset); 384 } 385 }; 386 387 Y.LeftAxisLayout = LeftAxisLayout; 388 /** 389 * RightAxisLayout contains algorithms for rendering a right axis. 390 * 391 * @class RightAxisLayout 392 * @constructor 393 * @submodule axis 394 */ 395 RightAxisLayout = function(){}; 396 397 RightAxisLayout.prototype = { 398 /** 399 * Default margins for text fields. 400 * 401 * @private 402 * @method _getDefaultMargins 403 * @return Object 404 */ 405 _getDefaultMargins: function() 406 { 407 return { 408 top: 0, 409 left: 4, 410 right: 0, 411 bottom: 0 412 }; 413 }, 414 415 /** 416 * Sets the length of the tick on either side of the axis line. 417 * 418 * @method setTickOffset 419 * @protected 420 */ 421 setTickOffsets: function() 422 { 423 var host = this, 424 majorTicks = host.get("styles").majorTicks, 425 tickLength = majorTicks.length, 426 halfTick = tickLength * 0.5, 427 display = majorTicks.display; 428 host.set("topTickOffset", 0); 429 host.set("bottomTickOffset", 0); 430 431 switch(display) 432 { 433 case "inside" : 434 host.set("leftTickOffset", tickLength); 435 host.set("rightTickOffset", 0); 436 break; 437 case "outside" : 438 host.set("leftTickOffset", 0); 439 host.set("rightTickOffset", tickLength); 440 break; 441 case "cross" : 442 host.set("rightTickOffset", halfTick); 443 host.set("leftTickOffset", halfTick); 444 break; 445 default: 446 host.set("leftTickOffset", 0); 447 host.set("rightTickOffset", 0); 448 break; 449 } 450 }, 451 452 /** 453 * Draws a tick 454 * 455 * @method drawTick 456 * @param {Path} path reference to the path `Path` element in which to draw the tick. 457 * @param {Object} pt Point on the axis in which the tick will intersect. 458 * @param {Object} tickStyle Hash of properties to apply to the tick. 459 * @protected 460 */ 461 drawTick: function(path, pt, tickStyles) 462 { 463 var host = this, 464 style = host.get("styles"), 465 padding = style.padding, 466 tickLength = tickStyles.length, 467 start = {x:padding.left, y:pt.y}, 468 end = {x:padding.left + tickLength, y:pt.y}; 469 host.drawLine(path, start, end); 470 }, 471 472 /** 473 * Calculates the coordinates for the first point on an axis. 474 * 475 * @method getLineStart 476 * @return {Object} 477 * @protected 478 */ 479 getLineStart: function() 480 { 481 var host = this, 482 style = host.get("styles"), 483 padding = style.padding, 484 majorTicks = style.majorTicks, 485 tickLength = majorTicks.length, 486 display = majorTicks.display, 487 pt = {x:padding.left, y:padding.top}; 488 if(display === "inside") 489 { 490 pt.x += tickLength; 491 } 492 else if(display === "cross") 493 { 494 pt.x += tickLength/2; 495 } 496 return pt; 497 }, 498 499 /** 500 * Calculates the point for a label. 501 * 502 * @method getLabelPoint 503 * @param {Object} point Point on the axis in which the tick will intersect. 504 * @return {Object} 505 * @protected 506 */ 507 getLabelPoint: function(point) 508 { 509 return {x:point.x + this.get("rightTickOffset"), y:point.y}; 510 }, 511 512 /** 513 * Updates the value for the `maxLabelSize` for use in calculating total size. 514 * 515 * @method updateMaxLabelSize 516 * @param {HTMLElement} label to measure 517 * @protected 518 */ 519 updateMaxLabelSize: function(labelWidth, labelHeight) 520 { 521 var host = this, 522 props = this._labelRotationProps, 523 rot = props.rot, 524 absRot = props.absRot, 525 sinRadians = props.sinRadians, 526 cosRadians = props.cosRadians, 527 max; 528 if(rot === 0) 529 { 530 max = labelWidth; 531 } 532 else if(absRot === 90) 533 { 534 max = labelHeight; 535 } 536 else 537 { 538 max = (cosRadians * labelWidth) + (sinRadians * labelHeight); 539 } 540 host._maxLabelSize = Math.max(host._maxLabelSize, max); 541 }, 542 543 /** 544 * Determines the available label width when the axis width has been explicitly set. 545 * 546 * @method getExplicitlySized 547 * @return Boolean 548 * @protected 549 */ 550 getExplicitlySized: function(styles) 551 { 552 if(this._explicitWidth) 553 { 554 var host = this, 555 w = host._explicitWidth, 556 totalTitleSize = this._totalTitleSize, 557 rightTickOffset = host.get("rightTickOffset"), 558 margin = styles.label.margin.right; 559 host._maxLabelSize = w - (rightTickOffset + margin + totalTitleSize); 560 return true; 561 } 562 return false; 563 }, 564 565 /** 566 * Rotate and position title. 567 * 568 * @method positionTitle 569 * @param {HTMLElement} label to rotate position 570 * @protected 571 */ 572 positionTitle: function(label) 573 { 574 var host = this, 575 bounds = host._titleBounds, 576 margin = host.get("styles").title.margin, 577 props = host._titleRotationProps, 578 labelWidth = label.offsetWidth, 579 labelHeight = label.offsetHeight, 580 w = bounds.right - bounds.left, 581 x = this.get("width") - (labelWidth * 0.5) - (w * 0.5), 582 y = (host.get("height") * 0.5) - (labelHeight * 0.5); 583 props.labelWidth = labelWidth; 584 props.labelHeight = labelHeight; 585 if(margin && margin.right) 586 { 587 x -= margin.left; 588 } 589 props.x = x; 590 props.y = y; 591 props.transformOrigin = [0.5, 0.5]; 592 host._rotate(label, props); 593 }, 594 595 /** 596 * Rotate and position labels. 597 * 598 * @method positionLabel 599 * @param {HTMLElement} label to rotate position 600 * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned 601 * against. 602 * @protected 603 */ 604 positionLabel: function(label, pt, styles, i) 605 { 606 var host = this, 607 offset = parseFloat(styles.label.offset), 608 tickOffset = host.get("rightTickOffset"), 609 labelStyles = styles.label, 610 margin = 0, 611 leftOffset = pt.x, 612 topOffset = pt.y, 613 props = this._labelRotationProps, 614 rot = props.rot, 615 absRot = props.absRot, 616 labelWidth = this._labelWidths[i], 617 labelHeight = this._labelHeights[i]; 618 if(labelStyles.margin && labelStyles.margin.left) 619 { 620 margin = labelStyles.margin.left; 621 } 622 if(rot === 0) 623 { 624 topOffset -= labelHeight * offset; 625 } 626 else if(rot === 90) 627 { 628 leftOffset -= labelWidth * 0.5; 629 topOffset = topOffset - labelHeight + labelWidth/2 - (labelWidth * offset); 630 } 631 else if(rot === -90) 632 { 633 topOffset = topOffset + labelWidth/2 - (labelWidth * offset); 634 leftOffset -= labelWidth * 0.5; 635 } 636 else 637 { 638 topOffset -= labelHeight * offset; 639 leftOffset += labelHeight/2 * absRot/90; 640 } 641 leftOffset += margin; 642 leftOffset += tickOffset; 643 props.labelWidth = labelWidth; 644 props.labelHeight = labelHeight; 645 props.x = Math.round(leftOffset); 646 props.y = Math.round(topOffset); 647 this._rotate(label, props); 648 }, 649 650 /** 651 * Adjusts the coordinates of an axis label based on the rotation. 652 * 653 * @method _setRotationCoords 654 * @param {Object} props Coordinates, dimension and rotation properties of the label. 655 * @protected 656 */ 657 _setRotationCoords: function(props) 658 { 659 var rot = props.rot, 660 absRot = props.absRot, 661 leftOffset = 0, 662 topOffset = 0, 663 labelWidth = props.labelWidth, 664 labelHeight = props.labelHeight; 665 if(rot === 0) 666 { 667 topOffset = labelHeight * 0.5; 668 } 669 else if(rot === 90) 670 { 671 leftOffset = labelWidth * 0.5; 672 topOffset = labelHeight; 673 } 674 else if(rot === -90) 675 { 676 leftOffset = labelWidth * 0.5; 677 } 678 else 679 { 680 topOffset = labelHeight * 0.5; 681 leftOffset = labelHeight/2 * absRot/90; 682 } 683 props.x -= leftOffset; 684 props.y -= topOffset; 685 }, 686 687 /** 688 * Returns the transformOrigin to use for an axis label based on the position of the axis 689 * and the rotation of the label. 690 * 691 * @method _getTransformOrigin 692 * @param {Number} rot The rotation (in degrees) of the label. 693 * @return Array 694 * @protected 695 */ 696 _getTransformOrigin: function(rot) 697 { 698 var transformOrigin; 699 if(rot === 0) 700 { 701 transformOrigin = [0, 0]; 702 } 703 else if(rot === 90) 704 { 705 transformOrigin = [0.5, 1]; 706 } 707 else if(rot === -90) 708 { 709 transformOrigin = [0.5, 0]; 710 } 711 else 712 { 713 transformOrigin = [0, 0.5]; 714 } 715 return transformOrigin; 716 }, 717 718 /** 719 * Adjusts position for inner ticks. 720 * 721 * @method offsetNodeForTick 722 * @param {Node} cb contentBox of the axis 723 * @protected 724 */ 725 offsetNodeForTick: function(cb) 726 { 727 var host = this, 728 tickOffset = host.get("leftTickOffset"), 729 offset = 0 - tickOffset; 730 cb.setStyle("left", offset); 731 }, 732 733 /** 734 * Assigns a height based on the size of the contents. 735 * 736 * @method setCalculatedSize 737 * @protected 738 */ 739 setCalculatedSize: function() 740 { 741 var host = this, 742 styles = host.get("styles"), 743 labelStyle = styles.label, 744 totalTitleSize = this._totalTitleSize, 745 ttl = Math.round(host.get("rightTickOffset") + host._maxLabelSize + totalTitleSize + labelStyle.margin.left); 746 if(this._explicitWidth) 747 { 748 ttl = this._explicitWidth; 749 } 750 host.set("calculatedWidth", ttl); 751 host.get("contentBox").setStyle("width", ttl); 752 } 753 }; 754 755 Y.RightAxisLayout = RightAxisLayout; 756 /** 757 * Contains algorithms for rendering a bottom axis. 758 * 759 * @class BottomAxisLayout 760 * @Constructor 761 * @submodule axis 762 */ 763 BottomAxisLayout = function(){}; 764 765 BottomAxisLayout.prototype = { 766 /** 767 * Default margins for text fields. 768 * 769 * @private 770 * @method _getDefaultMargins 771 * @return Object 772 */ 773 _getDefaultMargins: function() 774 { 775 return { 776 top: 4, 777 left: 0, 778 right: 0, 779 bottom: 0 780 }; 781 }, 782 783 /** 784 * Sets the length of the tick on either side of the axis line. 785 * 786 * @method setTickOffsets 787 * @protected 788 */ 789 setTickOffsets: function() 790 { 791 var host = this, 792 majorTicks = host.get("styles").majorTicks, 793 tickLength = majorTicks.length, 794 halfTick = tickLength * 0.5, 795 display = majorTicks.display; 796 host.set("leftTickOffset", 0); 797 host.set("rightTickOffset", 0); 798 799 switch(display) 800 { 801 case "inside" : 802 host.set("topTickOffset", tickLength); 803 host.set("bottomTickOffset", 0); 804 break; 805 case "outside" : 806 host.set("topTickOffset", 0); 807 host.set("bottomTickOffset", tickLength); 808 break; 809 case "cross": 810 host.set("topTickOffset", halfTick); 811 host.set("bottomTickOffset", halfTick); 812 break; 813 default: 814 host.set("topTickOffset", 0); 815 host.set("bottomTickOffset", 0); 816 break; 817 } 818 }, 819 820 /** 821 * Calculates the coordinates for the first point on an axis. 822 * 823 * @method getLineStart 824 * @protected 825 */ 826 getLineStart: function() 827 { 828 var style = this.get("styles"), 829 padding = style.padding, 830 majorTicks = style.majorTicks, 831 tickLength = majorTicks.length, 832 display = majorTicks.display, 833 pt = {x:0, y:padding.top}; 834 if(display === "inside") 835 { 836 pt.y += tickLength; 837 } 838 else if(display === "cross") 839 { 840 pt.y += tickLength/2; 841 } 842 return pt; 843 }, 844 845 /** 846 * Draws a tick 847 * 848 * @method drawTick 849 * @param {Path} path reference to the path `Path` element in which to draw the tick. 850 * @param {Object} pt hash containing x and y coordinates 851 * @param {Object} tickStyles hash of properties used to draw the tick 852 * @protected 853 */ 854 drawTick: function(path, pt, tickStyles) 855 { 856 var host = this, 857 style = host.get("styles"), 858 padding = style.padding, 859 tickLength = tickStyles.length, 860 start = {x:pt.x, y:padding.top}, 861 end = {x:pt.x, y:tickLength + padding.top}; 862 host.drawLine(path, start, end); 863 }, 864 865 /** 866 * Calculates the point for a label. 867 * 868 * @method getLabelPoint 869 * @param {Object} pt Object containing x and y coordinates 870 * @return Object 871 * @protected 872 */ 873 getLabelPoint: function(point) 874 { 875 return {x:point.x, y:point.y + this.get("bottomTickOffset")}; 876 }, 877 878 /** 879 * Updates the value for the `maxLabelSize` for use in calculating total size. 880 * 881 * @method updateMaxLabelSize 882 * @param {HTMLElement} label to measure 883 * @protected 884 */ 885 updateMaxLabelSize: function(labelWidth, labelHeight) 886 { 887 var host = this, 888 props = this._labelRotationProps, 889 rot = props.rot, 890 absRot = props.absRot, 891 sinRadians = props.sinRadians, 892 cosRadians = props.cosRadians, 893 max; 894 if(rot === 0) 895 { 896 max = labelHeight; 897 } 898 else if(absRot === 90) 899 { 900 max = labelWidth; 901 } 902 else 903 { 904 max = (sinRadians * labelWidth) + (cosRadians * labelHeight); 905 } 906 host._maxLabelSize = Math.max(host._maxLabelSize, max); 907 }, 908 909 /** 910 * Determines the available label height when the axis width has been explicitly set. 911 * 912 * @method getExplicitlySized 913 * @return Boolean 914 * @protected 915 */ 916 getExplicitlySized: function(styles) 917 { 918 if(this._explicitHeight) 919 { 920 var host = this, 921 h = host._explicitHeight, 922 totalTitleSize = host._totalTitleSize, 923 bottomTickOffset = host.get("bottomTickOffset"), 924 margin = styles.label.margin.right; 925 host._maxLabelSize = h - (bottomTickOffset + margin + totalTitleSize); 926 return true; 927 } 928 return false; 929 }, 930 931 /** 932 * Rotate and position title. 933 * 934 * @method positionTitle 935 * @param {HTMLElement} label to rotate position 936 * @protected 937 */ 938 positionTitle: function(label) 939 { 940 var host = this, 941 bounds = host._titleBounds, 942 margin = host.get("styles").title.margin, 943 props = host._titleRotationProps, 944 h = bounds.bottom - bounds.top, 945 labelWidth = label.offsetWidth, 946 labelHeight = label.offsetHeight, 947 x = (host.get("width") * 0.5) - (labelWidth * 0.5), 948 y = host.get("height") - labelHeight/2 - h/2; 949 props.labelWidth = labelWidth; 950 props.labelHeight = labelHeight; 951 if(margin && margin.bottom) 952 { 953 y -= margin.bottom; 954 } 955 props.x = x; 956 props.y = y; 957 props.transformOrigin = [0.5, 0.5]; 958 host._rotate(label, props); 959 }, 960 961 /** 962 * Rotate and position labels. 963 * 964 * @method positionLabel 965 * @param {HTMLElement} label to rotate position 966 * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned 967 * against. 968 * @protected 969 */ 970 positionLabel: function(label, pt, styles, i) 971 { 972 var host = this, 973 offset = parseFloat(styles.label.offset), 974 tickOffset = host.get("bottomTickOffset"), 975 labelStyles = styles.label, 976 margin = 0, 977 props = host._labelRotationProps, 978 rot = props.rot, 979 absRot = props.absRot, 980 leftOffset = Math.round(pt.x), 981 topOffset = Math.round(pt.y), 982 labelWidth = host._labelWidths[i], 983 labelHeight = host._labelHeights[i]; 984 if(labelStyles.margin && labelStyles.margin.top) 985 { 986 margin = labelStyles.margin.top; 987 } 988 if(rot === 90) 989 { 990 topOffset -= labelHeight/2 * rot/90; 991 leftOffset = leftOffset + labelHeight/2 - (labelHeight * offset); 992 } 993 else if(rot === -90) 994 { 995 topOffset -= labelHeight/2 * absRot/90; 996 leftOffset = leftOffset - labelWidth + labelHeight/2 - (labelHeight * offset); 997 } 998 else if(rot > 0) 999 { 1000 leftOffset = leftOffset + labelHeight/2 - (labelHeight * offset); 1001 topOffset -= labelHeight/2 * rot/90; 1002 } 1003 else if(rot < 0) 1004 { 1005 leftOffset = leftOffset - labelWidth + labelHeight/2 - (labelHeight * offset); 1006 topOffset -= labelHeight/2 * absRot/90; 1007 } 1008 else 1009 { 1010 leftOffset -= labelWidth * offset; 1011 } 1012 topOffset += margin; 1013 topOffset += tickOffset; 1014 props.labelWidth = labelWidth; 1015 props.labelHeight = labelHeight; 1016 props.x = leftOffset; 1017 props.y = topOffset; 1018 host._rotate(label, props); 1019 }, 1020 1021 /** 1022 * Adjusts the coordinates of an axis label based on the rotation. 1023 * 1024 * @method _setRotationCoords 1025 * @param {Object} props Coordinates, dimension and rotation properties of the label. 1026 * @protected 1027 */ 1028 _setRotationCoords: function(props) 1029 { 1030 var rot = props.rot, 1031 absRot = props.absRot, 1032 labelWidth = props.labelWidth, 1033 labelHeight = props.labelHeight, 1034 leftOffset, 1035 topOffset; 1036 1037 if(rot > 0) 1038 { 1039 leftOffset = 0; 1040 topOffset = labelHeight/2 * rot/90; 1041 } 1042 else if(rot < 0) 1043 { 1044 leftOffset = labelWidth; 1045 topOffset = labelHeight/2 * absRot/90; 1046 } 1047 else 1048 { 1049 leftOffset = labelWidth * 0.5; 1050 topOffset = 0; 1051 } 1052 props.x -= leftOffset; 1053 props.y -= topOffset; 1054 }, 1055 1056 /** 1057 * Returns the transformOrigin to use for an axis label based on the position of the axis 1058 * and the rotation of the label. 1059 * 1060 * @method _getTransformOrigin 1061 * @param {Number} rot The rotation (in degrees) of the label. 1062 * @return Array 1063 * @protected 1064 */ 1065 _getTransformOrigin: function(rot) 1066 { 1067 var transformOrigin; 1068 if(rot > 0) 1069 { 1070 transformOrigin = [0, 0.5]; 1071 } 1072 else if(rot < 0) 1073 { 1074 transformOrigin = [1, 0.5]; 1075 } 1076 else 1077 { 1078 transformOrigin = [0, 0]; 1079 } 1080 return transformOrigin; 1081 }, 1082 1083 /** 1084 * Adjusts position for inner ticks. 1085 * 1086 * @method offsetNodeForTick 1087 * @param {Node} cb contentBox of the axis 1088 * @protected 1089 */ 1090 offsetNodeForTick: function(cb) 1091 { 1092 var host = this; 1093 cb.setStyle("top", 0 - host.get("topTickOffset")); 1094 }, 1095 1096 /** 1097 * Assigns a height based on the size of the contents. 1098 * 1099 * @method setCalculatedSize 1100 * @protected 1101 */ 1102 setCalculatedSize: function() 1103 { 1104 var host = this, 1105 styles = host.get("styles"), 1106 labelStyle = styles.label, 1107 totalTitleSize = host._totalTitleSize, 1108 ttl = Math.round(host.get("bottomTickOffset") + host._maxLabelSize + labelStyle.margin.top + totalTitleSize); 1109 if(host._explicitHeight) 1110 { 1111 ttl = host._explicitHeight; 1112 } 1113 host.set("calculatedHeight", ttl); 1114 } 1115 }; 1116 Y.BottomAxisLayout = BottomAxisLayout; 1117 /** 1118 * Contains algorithms for rendering a top axis. 1119 * 1120 * @class TopAxisLayout 1121 * @constructor 1122 * @submodule axis 1123 */ 1124 TopAxisLayout = function(){}; 1125 1126 TopAxisLayout.prototype = { 1127 /** 1128 * Default margins for text fields. 1129 * 1130 * @private 1131 * @method _getDefaultMargins 1132 * @return Object 1133 */ 1134 _getDefaultMargins: function() 1135 { 1136 return { 1137 top: 0, 1138 left: 0, 1139 right: 0, 1140 bottom: 4 1141 }; 1142 }, 1143 1144 /** 1145 * Sets the length of the tick on either side of the axis line. 1146 * 1147 * @method setTickOffsets 1148 * @protected 1149 */ 1150 setTickOffsets: function() 1151 { 1152 var host = this, 1153 majorTicks = host.get("styles").majorTicks, 1154 tickLength = majorTicks.length, 1155 halfTick = tickLength * 0.5, 1156 display = majorTicks.display; 1157 host.set("leftTickOffset", 0); 1158 host.set("rightTickOffset", 0); 1159 switch(display) 1160 { 1161 case "inside" : 1162 host.set("bottomTickOffset", tickLength); 1163 host.set("topTickOffset", 0); 1164 break; 1165 case "outside" : 1166 host.set("bottomTickOffset", 0); 1167 host.set("topTickOffset", tickLength); 1168 break; 1169 case "cross" : 1170 host.set("topTickOffset", halfTick); 1171 host.set("bottomTickOffset", halfTick); 1172 break; 1173 default: 1174 host.set("topTickOffset", 0); 1175 host.set("bottomTickOffset", 0); 1176 break; 1177 } 1178 }, 1179 1180 /** 1181 * Calculates the coordinates for the first point on an axis. 1182 * 1183 * @method getLineStart 1184 * @protected 1185 */ 1186 getLineStart: function() 1187 { 1188 var host = this, 1189 style = host.get("styles"), 1190 padding = style.padding, 1191 majorTicks = style.majorTicks, 1192 tickLength = majorTicks.length, 1193 display = majorTicks.display, 1194 pt = {x:0, y:padding.top}; 1195 if(display === "outside") 1196 { 1197 pt.y += tickLength; 1198 } 1199 else if(display === "cross") 1200 { 1201 pt.y += tickLength/2; 1202 } 1203 return pt; 1204 }, 1205 1206 /** 1207 * Draws a tick 1208 * 1209 * @method drawTick 1210 * @param {Path} path reference to the path `Path` element in which to draw the tick. 1211 * @param {Object} pt hash containing x and y coordinates 1212 * @param {Object} tickStyles hash of properties used to draw the tick 1213 * @protected 1214 */ 1215 drawTick: function(path, pt, tickStyles) 1216 { 1217 var host = this, 1218 style = host.get("styles"), 1219 padding = style.padding, 1220 tickLength = tickStyles.length, 1221 start = {x:pt.x, y:padding.top}, 1222 end = {x:pt.x, y:tickLength + padding.top}; 1223 host.drawLine(path, start, end); 1224 }, 1225 1226 /** 1227 * Calculates the point for a label. 1228 * 1229 * @method getLabelPoint 1230 * @param {Object} pt hash containing x and y coordinates 1231 * @return Object 1232 * @protected 1233 */ 1234 getLabelPoint: function(pt) 1235 { 1236 return {x:pt.x, y:pt.y - this.get("topTickOffset")}; 1237 }, 1238 1239 /** 1240 * Updates the value for the `maxLabelSize` for use in calculating total size. 1241 * 1242 * @method updateMaxLabelSize 1243 * @param {HTMLElement} label to measure 1244 * @protected 1245 */ 1246 updateMaxLabelSize: function(labelWidth, labelHeight) 1247 { 1248 var host = this, 1249 props = this._labelRotationProps, 1250 rot = props.rot, 1251 absRot = props.absRot, 1252 sinRadians = props.sinRadians, 1253 cosRadians = props.cosRadians, 1254 max; 1255 if(rot === 0) 1256 { 1257 max = labelHeight; 1258 } 1259 else if(absRot === 90) 1260 { 1261 max = labelWidth; 1262 } 1263 else 1264 { 1265 max = (sinRadians * labelWidth) + (cosRadians * labelHeight); 1266 } 1267 host._maxLabelSize = Math.max(host._maxLabelSize, max); 1268 }, 1269 1270 /** 1271 * Determines the available label height when the axis width has been explicitly set. 1272 * 1273 * @method getExplicitlySized 1274 * @return Boolean 1275 * @protected 1276 */ 1277 getExplicitlySized: function(styles) 1278 { 1279 if(this._explicitHeight) 1280 { 1281 var host = this, 1282 h = host._explicitHeight, 1283 totalTitleSize = host._totalTitleSize, 1284 topTickOffset = host.get("topTickOffset"), 1285 margin = styles.label.margin.right; 1286 host._maxLabelSize = h - (topTickOffset + margin + totalTitleSize); 1287 return true; 1288 } 1289 return false; 1290 }, 1291 1292 /** 1293 * Rotate and position title. 1294 * 1295 * @method positionTitle 1296 * @param {HTMLElement} label to rotate position 1297 * @protected 1298 */ 1299 positionTitle: function(label) 1300 { 1301 var host = this, 1302 bounds = host._titleBounds, 1303 margin = host.get("styles").title.margin, 1304 props = host._titleRotationProps, 1305 labelWidth = label.offsetWidth, 1306 labelHeight = label.offsetHeight, 1307 h = bounds.bottom - bounds.top, 1308 x = (host.get("width") * 0.5) - (labelWidth * 0.5), 1309 y = h/2 - labelHeight/2; 1310 props.labelWidth = labelWidth; 1311 props.labelHeight = labelHeight; 1312 if(margin && margin.top) 1313 { 1314 y += margin.top; 1315 } 1316 props.x = x; 1317 props.y = y; 1318 props.transformOrigin = [0.5, 0.5]; 1319 host._rotate(label, props); 1320 }, 1321 1322 /** 1323 * Rotate and position labels. 1324 * 1325 * @method positionLabel 1326 * @param {HTMLElement} label to rotate position 1327 * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned 1328 * against. 1329 * @protected 1330 */ 1331 positionLabel: function(label, pt, styles, i) 1332 { 1333 var host = this, 1334 offset = parseFloat(styles.label.offset), 1335 totalTitleSize = this._totalTitleSize, 1336 maxLabelSize = host._maxLabelSize, 1337 leftOffset = pt.x, 1338 topOffset = pt.y + totalTitleSize + maxLabelSize, 1339 props = this._labelRotationProps, 1340 rot = props.rot, 1341 absRot = props.absRot, 1342 labelWidth = this._labelWidths[i], 1343 labelHeight = this._labelHeights[i]; 1344 if(rot === 0) 1345 { 1346 leftOffset -= labelWidth * offset; 1347 topOffset -= labelHeight; 1348 } 1349 else 1350 { 1351 if(rot === 90) 1352 { 1353 leftOffset = leftOffset - labelWidth + labelHeight/2 - (labelHeight * offset); 1354 topOffset -= (labelHeight * 0.5); 1355 } 1356 else if (rot === -90) 1357 { 1358 leftOffset = leftOffset + labelHeight/2 - (labelHeight * offset); 1359 topOffset -= (labelHeight * 0.5); 1360 } 1361 else if(rot > 0) 1362 { 1363 leftOffset = leftOffset - labelWidth + labelHeight/2 - (labelHeight * offset); 1364 topOffset -= labelHeight - (labelHeight * rot/180); 1365 } 1366 else 1367 { 1368 leftOffset = leftOffset + labelHeight/2 - (labelHeight * offset); 1369 topOffset -= labelHeight - (labelHeight * absRot/180); 1370 } 1371 } 1372 props.x = Math.round(leftOffset); 1373 props.y = Math.round(topOffset); 1374 props.labelWidth = labelWidth; 1375 props.labelHeight = labelHeight; 1376 this._rotate(label, props); 1377 }, 1378 1379 /** 1380 * Adjusts the coordinates of an axis label based on the rotation. 1381 * 1382 * @method _setRotationCoords 1383 * @param {Object} props Coordinates, dimension and rotation properties of the label. 1384 * @protected 1385 */ 1386 _setRotationCoords: function(props) 1387 { 1388 var rot = props.rot, 1389 absRot = props.absRot, 1390 labelWidth = props.labelWidth, 1391 labelHeight = props.labelHeight, 1392 leftOffset, 1393 topOffset; 1394 if(rot === 0) 1395 { 1396 leftOffset = labelWidth * 0.5; 1397 topOffset = labelHeight; 1398 } 1399 else 1400 { 1401 if(rot === 90) 1402 { 1403 leftOffset = labelWidth; 1404 topOffset = (labelHeight * 0.5); 1405 } 1406 else if (rot === -90) 1407 { 1408 topOffset = (labelHeight * 0.5); 1409 } 1410 else if(rot > 0) 1411 { 1412 leftOffset = labelWidth; 1413 topOffset = labelHeight - (labelHeight * rot/180); 1414 } 1415 else 1416 { 1417 topOffset = labelHeight - (labelHeight * absRot/180); 1418 } 1419 } 1420 props.x -= leftOffset; 1421 props.y -= topOffset; 1422 }, 1423 1424 /** 1425 * Returns the transformOrigin to use for an axis label based on the position of the axis 1426 * and the rotation of the label. 1427 * 1428 * @method _getTransformOrigin 1429 * @param {Number} rot The rotation (in degrees) of the label. 1430 * @return Array 1431 * @protected 1432 */ 1433 _getTransformOrigin: function(rot) 1434 { 1435 var transformOrigin; 1436 if(rot === 0) 1437 { 1438 transformOrigin = [0, 0]; 1439 } 1440 else 1441 { 1442 if(rot === 90) 1443 { 1444 transformOrigin = [1, 0.5]; 1445 } 1446 else if (rot === -90) 1447 { 1448 transformOrigin = [0, 0.5]; 1449 } 1450 else if(rot > 0) 1451 { 1452 transformOrigin = [1, 0.5]; 1453 } 1454 else 1455 { 1456 transformOrigin = [0, 0.5]; 1457 } 1458 } 1459 return transformOrigin; 1460 }, 1461 1462 /** 1463 * Adjusts position for inner ticks. 1464 * 1465 * @method offsetNodeForTick 1466 * @param {Node} cb contentBox of the axis 1467 * @protected 1468 */ 1469 offsetNodeForTick: function() 1470 { 1471 }, 1472 1473 /** 1474 * Assigns a height based on the size of the contents. 1475 * 1476 * @method setCalculatedSize 1477 * @protected 1478 */ 1479 setCalculatedSize: function() 1480 { 1481 var host = this, 1482 graphic = host.get("graphic"), 1483 styles = host.get("styles"), 1484 labelMargin = styles.label.margin, 1485 totalLabelSize = labelMargin.bottom + host._maxLabelSize, 1486 totalTitleSize = host._totalTitleSize, 1487 topTickOffset = this.get("topTickOffset"), 1488 ttl = Math.round(topTickOffset + totalLabelSize + totalTitleSize); 1489 if(this._explicitHeight) 1490 { 1491 ttl = this._explicitHeight; 1492 } 1493 host.set("calculatedHeight", ttl); 1494 graphic.set("y", ttl - topTickOffset); 1495 } 1496 }; 1497 Y.TopAxisLayout = TopAxisLayout; 1498 1499 /** 1500 * An abstract class that provides the core functionality for draw a chart axis. Axis is used by the following classes: 1501 * <ul> 1502 * <li>{{#crossLink "CategoryAxis"}}{{/crossLink}}</li> 1503 * <li>{{#crossLink "NumericAxis"}}{{/crossLink}}</li> 1504 * <li>{{#crossLink "StackedAxis"}}{{/crossLink}}</li> 1505 * <li>{{#crossLink "TimeAxis"}}{{/crossLink}}</li> 1506 * </ul> 1507 * 1508 * @class Axis 1509 * @extends Widget 1510 * @uses AxisBase 1511 * @uses TopAxisLayout 1512 * @uses RightAxisLayout 1513 * @uses BottomAxisLayout 1514 * @uses LeftAxisLayout 1515 * @constructor 1516 * @param {Object} config (optional) Configuration parameters. 1517 * @submodule axis 1518 */ 1519 Y.Axis = Y.Base.create("axis", Y.Widget, [Y.AxisBase], { 1520 /** 1521 * Calculates and returns a value based on the number of labels and the index of 1522 * the current label. 1523 * 1524 * @method getLabelByIndex 1525 * @param {Number} i Index of the label. 1526 * @param {Number} l Total number of labels. 1527 * @return String 1528 */ 1529 getLabelByIndex: function(i, l) 1530 { 1531 var position = this.get("position"), 1532 direction = position === "left" || position === "right" ? "vertical" : "horizontal"; 1533 return this._getLabelByIndex(i, l, direction); 1534 }, 1535 1536 /** 1537 * @method bindUI 1538 * @private 1539 */ 1540 bindUI: function() 1541 { 1542 this.after("dataReady", Y.bind(this._dataChangeHandler, this)); 1543 this.after("dataUpdate", Y.bind(this._dataChangeHandler, this)); 1544 this.after("stylesChange", this._updateHandler); 1545 this.after("overlapGraphChange", this._updateHandler); 1546 this.after("positionChange", this._positionChangeHandler); 1547 this.after("widthChange", this._handleSizeChange); 1548 this.after("heightChange", this._handleSizeChange); 1549 this.after("calculatedWidthChange", this._handleSizeChange); 1550 this.after("calculatedHeightChange", this._handleSizeChange); 1551 }, 1552 /** 1553 * Storage for calculatedWidth value. 1554 * 1555 * @property _calculatedWidth 1556 * @type Number 1557 * @private 1558 */ 1559 _calculatedWidth: 0, 1560 1561 /** 1562 * Storage for calculatedHeight value. 1563 * 1564 * @property _calculatedHeight 1565 * @type Number 1566 * @private 1567 */ 1568 _calculatedHeight: 0, 1569 1570 /** 1571 * Handles change to the dataProvider 1572 * 1573 * @method _dataChangeHandler 1574 * @param {Object} e Event object 1575 * @private 1576 */ 1577 _dataChangeHandler: function() 1578 { 1579 if(this.get("rendered")) 1580 { 1581 this._drawAxis(); 1582 } 1583 }, 1584 1585 /** 1586 * Handles change to the position attribute 1587 * 1588 * @method _positionChangeHandler 1589 * @param {Object} e Event object 1590 * @private 1591 */ 1592 _positionChangeHandler: function(e) 1593 { 1594 this._updateGraphic(e.newVal); 1595 this._updateHandler(); 1596 }, 1597 1598 /** 1599 * Updates the the Graphic instance 1600 * 1601 * @method _updateGraphic 1602 * @param {String} position Position of axis 1603 * @private 1604 */ 1605 _updateGraphic: function(position) 1606 { 1607 var graphic = this.get("graphic"); 1608 if(position === "none") 1609 { 1610 if(graphic) 1611 { 1612 graphic.destroy(); 1613 } 1614 } 1615 else 1616 { 1617 if(!graphic) 1618 { 1619 this._setCanvas(); 1620 } 1621 } 1622 }, 1623 1624 /** 1625 * Handles changes to axis. 1626 * 1627 * @method _updateHandler 1628 * @param {Object} e Event object 1629 * @private 1630 */ 1631 _updateHandler: function() 1632 { 1633 if(this.get("rendered")) 1634 { 1635 this._drawAxis(); 1636 } 1637 }, 1638 1639 /** 1640 * @method renderUI 1641 * @private 1642 */ 1643 renderUI: function() 1644 { 1645 this._updateGraphic(this.get("position")); 1646 }, 1647 1648 /** 1649 * @method syncUI 1650 * @private 1651 */ 1652 syncUI: function() 1653 { 1654 var layout = this._layout, 1655 defaultMargins, 1656 styles, 1657 label, 1658 title, 1659 i; 1660 if(layout) 1661 { 1662 defaultMargins = layout._getDefaultMargins(); 1663 styles = this.get("styles"); 1664 label = styles.label.margin; 1665 title =styles.title.margin; 1666 //need to defaultMargins method to the layout classes. 1667 for(i in defaultMargins) 1668 { 1669 if(defaultMargins.hasOwnProperty(i)) 1670 { 1671 label[i] = label[i] === undefined ? defaultMargins[i] : label[i]; 1672 title[i] = title[i] === undefined ? defaultMargins[i] : title[i]; 1673 } 1674 } 1675 } 1676 this._drawAxis(); 1677 }, 1678 1679 /** 1680 * Creates a graphic instance to be used for the axis line and ticks. 1681 * 1682 * @method _setCanvas 1683 * @private 1684 */ 1685 _setCanvas: function() 1686 { 1687 var cb = this.get("contentBox"), 1688 bb = this.get("boundingBox"), 1689 p = this.get("position"), 1690 pn = this._parentNode, 1691 w = this.get("width"), 1692 h = this.get("height"); 1693 bb.setStyle("position", "absolute"); 1694 bb.setStyle("zIndex", 2); 1695 w = w ? w + "px" : pn.getStyle("width"); 1696 h = h ? h + "px" : pn.getStyle("height"); 1697 if(p === "top" || p === "bottom") 1698 { 1699 cb.setStyle("width", w); 1700 } 1701 else 1702 { 1703 cb.setStyle("height", h); 1704 } 1705 cb.setStyle("position", "relative"); 1706 cb.setStyle("left", "0px"); 1707 cb.setStyle("top", "0px"); 1708 this.set("graphic", new Y.Graphic()); 1709 this.get("graphic").render(cb); 1710 }, 1711 1712 /** 1713 * Gets the default value for the `styles` attribute. Overrides 1714 * base implementation. 1715 * 1716 * @method _getDefaultStyles 1717 * @return Object 1718 * @protected 1719 */ 1720 _getDefaultStyles: function() 1721 { 1722 var axisstyles = { 1723 majorTicks: { 1724 display:"inside", 1725 length:4, 1726 color:"#dad8c9", 1727 weight:1, 1728 alpha:1 1729 }, 1730 minorTicks: { 1731 display:"none", 1732 length:2, 1733 color:"#dad8c9", 1734 weight:1 1735 }, 1736 line: { 1737 weight:1, 1738 color:"#dad8c9", 1739 alpha:1 1740 }, 1741 majorUnit: { 1742 determinant:"count", 1743 count:11, 1744 distance:75 1745 }, 1746 top: "0px", 1747 left: "0px", 1748 width: "100px", 1749 height: "100px", 1750 label: { 1751 color:"#808080", 1752 alpha: 1, 1753 fontSize:"85%", 1754 rotation: 0, 1755 offset: 0.5, 1756 margin: { 1757 top: undefined, 1758 right: undefined, 1759 bottom: undefined, 1760 left: undefined 1761 } 1762 }, 1763 title: { 1764 color:"#808080", 1765 alpha: 1, 1766 fontSize:"85%", 1767 rotation: undefined, 1768 margin: { 1769 top: undefined, 1770 right: undefined, 1771 bottom: undefined, 1772 left: undefined 1773 } 1774 }, 1775 hideOverlappingLabelTicks: false 1776 }; 1777 1778 return Y.merge(Y.Renderer.prototype._getDefaultStyles(), axisstyles); 1779 }, 1780 1781 /** 1782 * Updates the axis when the size changes. 1783 * 1784 * @method _handleSizeChange 1785 * @param {Object} e Event object. 1786 * @private 1787 */ 1788 _handleSizeChange: function(e) 1789 { 1790 var attrName = e.attrName, 1791 pos = this.get("position"), 1792 vert = pos === "left" || pos === "right", 1793 cb = this.get("contentBox"), 1794 hor = pos === "bottom" || pos === "top"; 1795 cb.setStyle("width", this.get("width")); 1796 cb.setStyle("height", this.get("height")); 1797 if((hor && attrName === "width") || (vert && attrName === "height")) 1798 { 1799 this._drawAxis(); 1800 } 1801 }, 1802 1803 /** 1804 * Maps key values to classes containing layout algorithms 1805 * 1806 * @property _layoutClasses 1807 * @type Object 1808 * @private 1809 */ 1810 _layoutClasses: 1811 { 1812 top : TopAxisLayout, 1813 bottom: BottomAxisLayout, 1814 left: LeftAxisLayout, 1815 right : RightAxisLayout 1816 }, 1817 1818 /** 1819 * Draws a line segment between 2 points 1820 * 1821 * @method drawLine 1822 * @param {Object} startPoint x and y coordinates for the start point of the line segment 1823 * @param {Object} endPoint x and y coordinates for the for the end point of the line segment 1824 * @param {Object} line styles (weight, color and alpha to be applied to the line segment) 1825 * @private 1826 */ 1827 drawLine: function(path, startPoint, endPoint) 1828 { 1829 path.moveTo(startPoint.x, startPoint.y); 1830 path.lineTo(endPoint.x, endPoint.y); 1831 }, 1832 1833 /** 1834 * Generates the properties necessary for rotating and positioning a text field. 1835 * 1836 * @method _getTextRotationProps 1837 * @param {Object} styles properties for the text field 1838 * @return Object 1839 * @private 1840 */ 1841 _getTextRotationProps: function(styles) 1842 { 1843 if(styles.rotation === undefined) 1844 { 1845 switch(this.get("position")) 1846 { 1847 case "left" : 1848 styles.rotation = -90; 1849 break; 1850 case "right" : 1851 styles.rotation = 90; 1852 break; 1853 default : 1854 styles.rotation = 0; 1855 break; 1856 } 1857 } 1858 var rot = Math.min(90, Math.max(-90, styles.rotation)), 1859 absRot = Math.abs(rot), 1860 radCon = Math.PI/180, 1861 sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)), 1862 cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)); 1863 return { 1864 rot: rot, 1865 absRot: absRot, 1866 radCon: radCon, 1867 sinRadians: sinRadians, 1868 cosRadians: cosRadians, 1869 textAlpha: styles.alpha 1870 }; 1871 }, 1872 1873 /** 1874 * Draws an axis. 1875 * 1876 * @method _drawAxis 1877 * @private 1878 */ 1879 _drawAxis: function () 1880 { 1881 if(this._drawing) 1882 { 1883 this._callLater = true; 1884 return; 1885 } 1886 this._drawing = true; 1887 this._callLater = false; 1888 if(this._layout) 1889 { 1890 var styles = this.get("styles"), 1891 line = styles.line, 1892 labelStyles = styles.label, 1893 majorTickStyles = styles.majorTicks, 1894 drawTicks = majorTickStyles.display !== "none", 1895 len, 1896 i = 0, 1897 layout = this._layout, 1898 layoutLength, 1899 lineStart, 1900 label, 1901 labelWidth, 1902 labelHeight, 1903 labelFunction = this.get("labelFunction"), 1904 labelFunctionScope = this.get("labelFunctionScope"), 1905 labelFormat = this.get("labelFormat"), 1906 graphic = this.get("graphic"), 1907 path = this.get("path"), 1908 tickPath, 1909 explicitlySized, 1910 position = this.get("position"), 1911 labelData, 1912 labelValues, 1913 point, 1914 points, 1915 firstPoint, 1916 lastPoint, 1917 firstLabel, 1918 lastLabel, 1919 staticCoord, 1920 dynamicCoord, 1921 edgeOffset, 1922 explicitLabels = this._labelValuesExplicitlySet ? this.get("labelValues") : null, 1923 direction = (position === "left" || position === "right") ? "vertical" : "horizontal"; 1924 this._labelWidths = []; 1925 this._labelHeights = []; 1926 graphic.set("autoDraw", false); 1927 path.clear(); 1928 path.set("stroke", { 1929 weight: line.weight, 1930 color: line.color, 1931 opacity: line.alpha 1932 }); 1933 this._labelRotationProps = this._getTextRotationProps(labelStyles); 1934 this._labelRotationProps.transformOrigin = layout._getTransformOrigin(this._labelRotationProps.rot); 1935 layout.setTickOffsets.apply(this); 1936 layoutLength = this.getLength(); 1937 1938 len = this.getTotalMajorUnits(); 1939 edgeOffset = this.getEdgeOffset(len, layoutLength); 1940 this.set("edgeOffset", edgeOffset); 1941 lineStart = layout.getLineStart.apply(this); 1942 1943 if(direction === "vertical") 1944 { 1945 staticCoord = "x"; 1946 dynamicCoord = "y"; 1947 } 1948 else 1949 { 1950 staticCoord = "y"; 1951 dynamicCoord = "x"; 1952 } 1953 1954 labelData = this._getLabelData( 1955 lineStart[staticCoord], 1956 staticCoord, 1957 dynamicCoord, 1958 this.get("minimum"), 1959 this.get("maximum"), 1960 edgeOffset, 1961 layoutLength - edgeOffset - edgeOffset, 1962 len, 1963 explicitLabels 1964 ); 1965 1966 points = labelData.points; 1967 labelValues = labelData.values; 1968 len = points.length; 1969 if(!this._labelValuesExplicitlySet) 1970 { 1971 this.set("labelValues", labelValues, {src: "internal"}); 1972 } 1973 1974 //Don't create the last label or tick. 1975 if(this.get("hideFirstMajorUnit")) 1976 { 1977 firstPoint = points.shift(); 1978 firstLabel = labelValues.shift(); 1979 len = len - 1; 1980 } 1981 1982 //Don't create the last label or tick. 1983 if(this.get("hideLastMajorUnit")) 1984 { 1985 lastPoint = points.pop(); 1986 lastLabel = labelValues.pop(); 1987 len = len - 1; 1988 } 1989 1990 if(len < 1) 1991 { 1992 this._clearLabelCache(); 1993 } 1994 else 1995 { 1996 this.drawLine(path, lineStart, this.getLineEnd(lineStart)); 1997 if(drawTicks) 1998 { 1999 tickPath = this.get("tickPath"); 2000 tickPath.clear(); 2001 tickPath.set("stroke", { 2002 weight: majorTickStyles.weight, 2003 color: majorTickStyles.color, 2004 opacity: majorTickStyles.alpha 2005 }); 2006 for(i = 0; i < len; i = i + 1) 2007 { 2008 point = points[i]; 2009 if(point) 2010 { 2011 layout.drawTick.apply(this, [tickPath, points[i], majorTickStyles]); 2012 } 2013 } 2014 } 2015 this._createLabelCache(); 2016 this._maxLabelSize = 0; 2017 this._totalTitleSize = 0; 2018 this._titleSize = 0; 2019 this._setTitle(); 2020 explicitlySized = layout.getExplicitlySized.apply(this, [styles]); 2021 for(i = 0; i < len; i = i + 1) 2022 { 2023 point = points[i]; 2024 if(point) 2025 { 2026 label = this.getLabel(labelStyles); 2027 this._labels.push(label); 2028 this.get("appendLabelFunction")(label, labelFunction.apply(labelFunctionScope, [labelValues[i], labelFormat])); 2029 labelWidth = Math.round(label.offsetWidth); 2030 labelHeight = Math.round(label.offsetHeight); 2031 if(!explicitlySized) 2032 { 2033 this._layout.updateMaxLabelSize.apply(this, [labelWidth, labelHeight]); 2034 } 2035 this._labelWidths.push(labelWidth); 2036 this._labelHeights.push(labelHeight); 2037 } 2038 } 2039 this._clearLabelCache(); 2040 if(this.get("overlapGraph")) 2041 { 2042 layout.offsetNodeForTick.apply(this, [this.get("contentBox")]); 2043 } 2044 layout.setCalculatedSize.apply(this); 2045 if(this._titleTextField) 2046 { 2047 this._layout.positionTitle.apply(this, [this._titleTextField]); 2048 } 2049 len = this._labels.length; 2050 for(i = 0; i < len; ++i) 2051 { 2052 layout.positionLabel.apply(this, [this.get("labels")[i], points[i], styles, i]); 2053 } 2054 if(firstPoint) 2055 { 2056 points.unshift(firstPoint); 2057 } 2058 if(lastPoint) 2059 { 2060 points.push(lastPoint); 2061 } 2062 if(firstLabel) 2063 { 2064 labelValues.unshift(firstLabel); 2065 } 2066 if(lastLabel) 2067 { 2068 labelValues.push(lastLabel); 2069 } 2070 this._tickPoints = points; 2071 } 2072 } 2073 this._drawing = false; 2074 if(this._callLater) 2075 { 2076 this._drawAxis(); 2077 } 2078 else 2079 { 2080 this._updatePathElement(); 2081 this.fire("axisRendered"); 2082 } 2083 }, 2084 2085 /** 2086 * Calculates and sets the total size of a title. 2087 * 2088 * @method _setTotalTitleSize 2089 * @param {Object} styles Properties for the title field. 2090 * @private 2091 */ 2092 _setTotalTitleSize: function(styles) 2093 { 2094 var title = this._titleTextField, 2095 w = title.offsetWidth, 2096 h = title.offsetHeight, 2097 rot = this._titleRotationProps.rot, 2098 bounds, 2099 size, 2100 margin = styles.margin, 2101 position = this.get("position"), 2102 matrix = new Y.Matrix(); 2103 matrix.rotate(rot); 2104 bounds = matrix.getContentRect(w, h); 2105 if(position === "left" || position === "right") 2106 { 2107 size = bounds.right - bounds.left; 2108 if(margin) 2109 { 2110 size += margin.left + margin.right; 2111 } 2112 } 2113 else 2114 { 2115 size = bounds.bottom - bounds.top; 2116 if(margin) 2117 { 2118 size += margin.top + margin.bottom; 2119 } 2120 } 2121 this._titleBounds = bounds; 2122 this._totalTitleSize = size; 2123 }, 2124 2125 /** 2126 * Updates path. 2127 * 2128 * @method _updatePathElement 2129 * @private 2130 */ 2131 _updatePathElement: function() 2132 { 2133 var path = this._path, 2134 tickPath = this._tickPath, 2135 redrawGraphic = false, 2136 graphic = this.get("graphic"); 2137 if(path) 2138 { 2139 redrawGraphic = true; 2140 path.end(); 2141 } 2142 if(tickPath) 2143 { 2144 redrawGraphic = true; 2145 tickPath.end(); 2146 } 2147 if(redrawGraphic) 2148 { 2149 graphic._redraw(); 2150 } 2151 }, 2152 2153 /** 2154 * Updates the content and style properties for a title field. 2155 * 2156 * @method _updateTitle 2157 * @private 2158 */ 2159 _setTitle: function() 2160 { 2161 var i, 2162 styles, 2163 customStyles, 2164 title = this.get("title"), 2165 titleTextField = this._titleTextField, 2166 parentNode; 2167 if(title !== null && title !== undefined) 2168 { 2169 customStyles = { 2170 rotation: "rotation", 2171 margin: "margin", 2172 alpha: "alpha" 2173 }; 2174 styles = this.get("styles").title; 2175 if(!titleTextField) 2176 { 2177 titleTextField = DOCUMENT.createElement('span'); 2178 titleTextField.style.display = "block"; 2179 titleTextField.style.whiteSpace = "nowrap"; 2180 titleTextField.setAttribute("class", "axisTitle"); 2181 this.get("contentBox").append(titleTextField); 2182 } 2183 else if(!DOCUMENT.createElementNS) 2184 { 2185 if(titleTextField.style.filter) 2186 { 2187 titleTextField.style.filter = null; 2188 } 2189 } 2190 titleTextField.style.position = "absolute"; 2191 for(i in styles) 2192 { 2193 if(styles.hasOwnProperty(i) && !customStyles.hasOwnProperty(i)) 2194 { 2195 titleTextField.style[i] = styles[i]; 2196 } 2197 } 2198 this.get("appendTitleFunction")(titleTextField, title); 2199 this._titleTextField = titleTextField; 2200 this._titleRotationProps = this._getTextRotationProps(styles); 2201 this._setTotalTitleSize(styles); 2202 } 2203 else if(titleTextField) 2204 { 2205 parentNode = titleTextField.parentNode; 2206 if(parentNode) 2207 { 2208 parentNode.removeChild(titleTextField); 2209 } 2210 this._titleTextField = null; 2211 this._totalTitleSize = 0; 2212 } 2213 }, 2214 2215 /** 2216 * Creates or updates an axis label. 2217 * 2218 * @method getLabel 2219 * @param {Object} styles styles applied to label 2220 * @return HTMLElement 2221 * @private 2222 */ 2223 getLabel: function(styles) 2224 { 2225 var i, 2226 label, 2227 labelCache = this._labelCache, 2228 customStyles = { 2229 rotation: "rotation", 2230 margin: "margin", 2231 alpha: "alpha" 2232 }; 2233 if(labelCache && labelCache.length > 0) 2234 { 2235 label = labelCache.shift(); 2236 } 2237 else 2238 { 2239 label = DOCUMENT.createElement("span"); 2240 label.className = Y.Lang.trim([label.className, "axisLabel"].join(' ')); 2241 this.get("contentBox").append(label); 2242 } 2243 if(!DOCUMENT.createElementNS) 2244 { 2245 if(label.style.filter) 2246 { 2247 label.style.filter = null; 2248 } 2249 } 2250 label.style.display = "block"; 2251 label.style.whiteSpace = "nowrap"; 2252 label.style.position = "absolute"; 2253 for(i in styles) 2254 { 2255 if(styles.hasOwnProperty(i) && !customStyles.hasOwnProperty(i)) 2256 { 2257 label.style[i] = styles[i]; 2258 } 2259 } 2260 return label; 2261 }, 2262 2263 /** 2264 * Creates a cache of labels that can be re-used when the axis redraws. 2265 * 2266 * @method _createLabelCache 2267 * @private 2268 */ 2269 _createLabelCache: function() 2270 { 2271 if(this._labels) 2272 { 2273 while(this._labels.length > 0) 2274 { 2275 this._labelCache.push(this._labels.shift()); 2276 } 2277 } 2278 else 2279 { 2280 this._clearLabelCache(); 2281 } 2282 this._labels = []; 2283 }, 2284 2285 /** 2286 * Removes axis labels from the dom and clears the label cache. 2287 * 2288 * @method _clearLabelCache 2289 * @private 2290 */ 2291 _clearLabelCache: function() 2292 { 2293 if(this._labelCache) 2294 { 2295 var len = this._labelCache.length, 2296 i = 0, 2297 label; 2298 for(; i < len; ++i) 2299 { 2300 label = this._labelCache[i]; 2301 this._removeChildren(label); 2302 Y.Event.purgeElement(label, true); 2303 label.parentNode.removeChild(label); 2304 } 2305 } 2306 this._labelCache = []; 2307 }, 2308 2309 /** 2310 * Gets the end point of an axis. 2311 * 2312 * @method getLineEnd 2313 * @return Object 2314 * @private 2315 */ 2316 getLineEnd: function(pt) 2317 { 2318 var w = this.get("width"), 2319 h = this.get("height"), 2320 pos = this.get("position"); 2321 if(pos === "top" || pos === "bottom") 2322 { 2323 return {x:w, y:pt.y}; 2324 } 2325 else 2326 { 2327 return {x:pt.x, y:h}; 2328 } 2329 }, 2330 2331 /** 2332 * Calcuates the width or height of an axis depending on its direction. 2333 * 2334 * @method getLength 2335 * @return Number 2336 * @private 2337 */ 2338 getLength: function() 2339 { 2340 var l, 2341 style = this.get("styles"), 2342 padding = style.padding, 2343 w = this.get("width"), 2344 h = this.get("height"), 2345 pos = this.get("position"); 2346 if(pos === "top" || pos === "bottom") 2347 { 2348 l = w - (padding.left + padding.right); 2349 } 2350 else 2351 { 2352 l = h - (padding.top + padding.bottom); 2353 } 2354 return l; 2355 }, 2356 2357 /** 2358 * Gets the position of the first point on an axis. 2359 * 2360 * @method getFirstPoint 2361 * @param {Object} pt Object containing x and y coordinates. 2362 * @return Object 2363 * @private 2364 */ 2365 getFirstPoint:function(pt) 2366 { 2367 var style = this.get("styles"), 2368 pos = this.get("position"), 2369 padding = style.padding, 2370 np = {x:pt.x, y:pt.y}; 2371 if(pos === "top" || pos === "bottom") 2372 { 2373 np.x += padding.left + this.get("edgeOffset"); 2374 } 2375 else 2376 { 2377 np.y += this.get("height") - (padding.top + this.get("edgeOffset")); 2378 } 2379 return np; 2380 }, 2381 2382 /** 2383 * Rotates and positions a text field. 2384 * 2385 * @method _rotate 2386 * @param {HTMLElement} label text field to rotate and position 2387 * @param {Object} props properties to be applied to the text field. 2388 * @private 2389 */ 2390 _rotate: function(label, props) 2391 { 2392 var rot = props.rot, 2393 x = props.x, 2394 y = props.y, 2395 filterString, 2396 textAlpha, 2397 matrix = new Y.Matrix(), 2398 transformOrigin = props.transformOrigin || [0, 0], 2399 offsetRect; 2400 if(DOCUMENT.createElementNS) 2401 { 2402 matrix.translate(x, y); 2403 matrix.rotate(rot); 2404 Y_DOM.setStyle(label, "transformOrigin", (transformOrigin[0] * 100) + "% " + (transformOrigin[1] * 100) + "%"); 2405 Y_DOM.setStyle(label, "transform", matrix.toCSSText()); 2406 } 2407 else 2408 { 2409 textAlpha = props.textAlpha; 2410 if(Y_Lang.isNumber(textAlpha) && textAlpha < 1 && textAlpha > -1 && !isNaN(textAlpha)) 2411 { 2412 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(textAlpha * 100) + ")"; 2413 } 2414 if(rot !== 0) 2415 { 2416 //ms filters kind of, sort of uses a transformOrigin of 0, 0. 2417 //we'll translate the difference to create a true 0, 0 origin. 2418 matrix.rotate(rot); 2419 offsetRect = matrix.getContentRect(props.labelWidth, props.labelHeight); 2420 matrix.init(); 2421 matrix.translate(offsetRect.left, offsetRect.top); 2422 matrix.translate(x, y); 2423 this._simulateRotateWithTransformOrigin(matrix, rot, transformOrigin, props.labelWidth, props.labelHeight); 2424 if(filterString) 2425 { 2426 filterString += " "; 2427 } 2428 else 2429 { 2430 filterString = ""; 2431 } 2432 filterString += matrix.toFilterText(); 2433 label.style.left = matrix.dx + "px"; 2434 label.style.top = matrix.dy + "px"; 2435 } 2436 else 2437 { 2438 label.style.left = x + "px"; 2439 label.style.top = y + "px"; 2440 } 2441 if(filterString) 2442 { 2443 label.style.filter = filterString; 2444 } 2445 } 2446 }, 2447 2448 /** 2449 * Simulates a rotation with a specified transformOrigin. 2450 * 2451 * @method _simulateTransformOrigin 2452 * @param {Matrix} matrix Reference to a `Matrix` instance. 2453 * @param {Number} rot The rotation (in degrees) that will be performed on a matrix. 2454 * @param {Array} transformOrigin An array represeniting the origin in which to perform the transform. The first 2455 * index represents the x origin and the second index represents the y origin. 2456 * @param {Number} w The width of the object that will be transformed. 2457 * @param {Number} h The height of the object that will be transformed. 2458 * @private 2459 */ 2460 _simulateRotateWithTransformOrigin: function(matrix, rot, transformOrigin, w, h) 2461 { 2462 var transformX = transformOrigin[0] * w, 2463 transformY = transformOrigin[1] * h; 2464 transformX = !isNaN(transformX) ? transformX : 0; 2465 transformY = !isNaN(transformY) ? transformY : 0; 2466 matrix.translate(transformX, transformY); 2467 matrix.rotate(rot); 2468 matrix.translate(-transformX, -transformY); 2469 }, 2470 2471 /** 2472 * Returns the coordinates (top, right, bottom, left) for the bounding box of the last label. 2473 * 2474 * @method getMaxLabelBounds 2475 * @return Object 2476 */ 2477 getMaxLabelBounds: function() 2478 { 2479 return this._getLabelBounds(this.getMaximumValue()); 2480 }, 2481 2482 /** 2483 * Returns the coordinates (top, right, bottom, left) for the bounding box of the first label. 2484 * 2485 * @method getMinLabelBounds 2486 * @return Object 2487 */ 2488 getMinLabelBounds: function() 2489 { 2490 return this._getLabelBounds(this.getMinimumValue()); 2491 }, 2492 2493 /** 2494 * Returns the coordinates (top, right, bottom, left) for the bounding box of a label. 2495 * 2496 * @method _getLabelBounds 2497 * @param {String} Value of the label 2498 * @return Object 2499 * @private 2500 */ 2501 _getLabelBounds: function(val) 2502 { 2503 var layout = this._layout, 2504 labelStyles = this.get("styles").label, 2505 matrix = new Y.Matrix(), 2506 label, 2507 props = this._getTextRotationProps(labelStyles); 2508 props.transformOrigin = layout._getTransformOrigin(props.rot); 2509 label = this.getLabel(labelStyles); 2510 this.get("appendLabelFunction")(label, this.get("labelFunction").apply(this, [val, this.get("labelFormat")])); 2511 props.labelWidth = label.offsetWidth; 2512 props.labelHeight = label.offsetHeight; 2513 this._removeChildren(label); 2514 Y.Event.purgeElement(label, true); 2515 label.parentNode.removeChild(label); 2516 props.x = 0; 2517 props.y = 0; 2518 layout._setRotationCoords(props); 2519 matrix.translate(props.x, props.y); 2520 this._simulateRotateWithTransformOrigin(matrix, props.rot, props.transformOrigin, props.labelWidth, props.labelHeight); 2521 return matrix.getContentRect(props.labelWidth, props.labelHeight); 2522 }, 2523 2524 /** 2525 * Removes all DOM elements from an HTML element. Used to clear out labels during detruction 2526 * phase. 2527 * 2528 * @method _removeChildren 2529 * @private 2530 */ 2531 _removeChildren: function(node) 2532 { 2533 if(node.hasChildNodes()) 2534 { 2535 var child; 2536 while(node.firstChild) 2537 { 2538 child = node.firstChild; 2539 this._removeChildren(child); 2540 node.removeChild(child); 2541 } 2542 } 2543 }, 2544 2545 /** 2546 * Destructor implementation Axis class. Removes all labels and the Graphic instance from the widget. 2547 * 2548 * @method destructor 2549 * @protected 2550 */ 2551 destructor: function() 2552 { 2553 var cb = this.get("contentBox").getDOMNode(), 2554 labels = this.get("labels"), 2555 graphic = this.get("graphic"), 2556 label, 2557 len = labels ? labels.length : 0; 2558 if(len > 0) 2559 { 2560 while(labels.length > 0) 2561 { 2562 label = labels.shift(); 2563 this._removeChildren(label); 2564 cb.removeChild(label); 2565 label = null; 2566 } 2567 } 2568 if(graphic) 2569 { 2570 graphic.destroy(); 2571 } 2572 }, 2573 2574 /** 2575 * Length in pixels of largest text bounding box. Used to calculate the height of the axis. 2576 * 2577 * @property maxLabelSize 2578 * @type Number 2579 * @protected 2580 */ 2581 _maxLabelSize: 0, 2582 2583 /** 2584 * Updates the content of text field. This method writes a value into a text field using 2585 * `appendChild`. If the value is a `String`, it is converted to a `TextNode` first. 2586 * 2587 * @method _setText 2588 * @param label {HTMLElement} label to be updated 2589 * @param val {String} value with which to update the label 2590 * @private 2591 */ 2592 _setText: function(textField, val) 2593 { 2594 textField.innerHTML = ""; 2595 if(Y_Lang.isNumber(val)) 2596 { 2597 val = val + ""; 2598 } 2599 else if(!val) 2600 { 2601 val = ""; 2602 } 2603 if(IS_STRING(val)) 2604 { 2605 val = DOCUMENT.createTextNode(val); 2606 } 2607 textField.appendChild(val); 2608 }, 2609 2610 /** 2611 * Returns the total number of majorUnits that will appear on an axis. 2612 * 2613 * @method getTotalMajorUnits 2614 * @return Number 2615 */ 2616 getTotalMajorUnits: function() 2617 { 2618 var units, 2619 majorUnit = this.get("styles").majorUnit, 2620 len; 2621 if(majorUnit.determinant === "count") 2622 { 2623 units = majorUnit.count; 2624 } 2625 else if(majorUnit.determinant === "distance") 2626 { 2627 len = this.getLength(); 2628 units = (len/majorUnit.distance) + 1; 2629 } 2630 return units; 2631 }, 2632 2633 /** 2634 * Returns the distance between major units on an axis. 2635 * 2636 * @method getMajorUnitDistance 2637 * @param {Number} len Number of ticks 2638 * @param {Number} uiLen Size of the axis. 2639 * @param {Object} majorUnit Hash of properties used to determine the majorUnit 2640 * @return Number 2641 */ 2642 getMajorUnitDistance: function(len, uiLen, majorUnit) 2643 { 2644 var dist; 2645 if(majorUnit.determinant === "count") 2646 { 2647 if(!this.get("calculateEdgeOffset")) 2648 { 2649 len = len - 1; 2650 } 2651 dist = uiLen/len; 2652 } 2653 else if(majorUnit.determinant === "distance") 2654 { 2655 dist = majorUnit.distance; 2656 } 2657 return dist; 2658 }, 2659 2660 /** 2661 * Checks to see if data extends beyond the range of the axis. If so, 2662 * that data will need to be hidden. This method is internal, temporary and subject 2663 * to removal in the future. 2664 * 2665 * @method _hasDataOverflow 2666 * @protected 2667 * @return Boolean 2668 */ 2669 _hasDataOverflow: function() 2670 { 2671 if(this.get("setMin") || this.get("setMax")) 2672 { 2673 return true; 2674 } 2675 return false; 2676 }, 2677 2678 /** 2679 * Returns a string corresponding to the first label on an 2680 * axis. 2681 * 2682 * @method getMinimumValue 2683 * @return String 2684 */ 2685 getMinimumValue: function() 2686 { 2687 return this.get("minimum"); 2688 }, 2689 2690 /** 2691 * Returns a string corresponding to the last label on an 2692 * axis. 2693 * 2694 * @method getMaximumValue 2695 * @return String 2696 */ 2697 getMaximumValue: function() 2698 { 2699 return this.get("maximum"); 2700 } 2701 }, { 2702 ATTRS: 2703 { 2704 /** 2705 * When set, defines the width of a vertical axis instance. By default, vertical axes automatically size based 2706 * on their contents. When the width attribute is set, the axis will not calculate its width. When the width 2707 * attribute is explicitly set, axis labels will postion themselves off of the the inner edge of the axis and the 2708 * title, if present, will position itself off of the outer edge. If a specified width is less than the sum of 2709 * the axis' contents, excess content will overflow. 2710 * 2711 * @attribute width 2712 * @type Number 2713 */ 2714 width: { 2715 lazyAdd: false, 2716 2717 getter: function() 2718 { 2719 if(this._explicitWidth) 2720 { 2721 return this._explicitWidth; 2722 } 2723 return this._calculatedWidth; 2724 }, 2725 2726 setter: function(val) 2727 { 2728 this._explicitWidth = val; 2729 return val; 2730 } 2731 }, 2732 2733 /** 2734 * When set, defines the height of a horizontal axis instance. By default, horizontal axes automatically size based 2735 * on their contents. When the height attribute is set, the axis will not calculate its height. When the height 2736 * attribute is explicitly set, axis labels will postion themselves off of the the inner edge of the axis and the 2737 * title, if present, will position itself off of the outer edge. If a specified height is less than the sum of 2738 * the axis' contents, excess content will overflow. 2739 * 2740 * @attribute height 2741 * @type Number 2742 */ 2743 height: { 2744 lazyAdd: false, 2745 2746 getter: function() 2747 { 2748 if(this._explicitHeight) 2749 { 2750 return this._explicitHeight; 2751 } 2752 return this._calculatedHeight; 2753 }, 2754 2755 setter: function(val) 2756 { 2757 this._explicitHeight = val; 2758 return val; 2759 } 2760 }, 2761 2762 /** 2763 * Calculated value of an axis' width. By default, the value is used internally for vertical axes. If the `width` 2764 * attribute is explicitly set, this value will be ignored. 2765 * 2766 * @attribute calculatedWidth 2767 * @type Number 2768 * @private 2769 */ 2770 calculatedWidth: { 2771 getter: function() 2772 { 2773 return this._calculatedWidth; 2774 }, 2775 2776 setter: function(val) 2777 { 2778 this._calculatedWidth = val; 2779 return val; 2780 } 2781 }, 2782 2783 /** 2784 * Calculated value of an axis' height. By default, the value is used internally for horizontal axes. If the `height` 2785 * attribute is explicitly set, this value will be ignored. 2786 * 2787 * @attribute calculatedHeight 2788 * @type Number 2789 * @private 2790 */ 2791 calculatedHeight: { 2792 getter: function() 2793 { 2794 return this._calculatedHeight; 2795 }, 2796 2797 setter: function(val) 2798 { 2799 this._calculatedHeight = val; 2800 return val; 2801 } 2802 }, 2803 2804 /** 2805 * Difference between the first/last tick and edge of axis. 2806 * 2807 * @attribute edgeOffset 2808 * @type Number 2809 * @protected 2810 */ 2811 edgeOffset: 2812 { 2813 value: 0 2814 }, 2815 2816 /** 2817 * The graphic in which the axis line and ticks will be rendered. 2818 * 2819 * @attribute graphic 2820 * @type Graphic 2821 */ 2822 graphic: {}, 2823 2824 /** 2825 * @attribute path 2826 * @type Shape 2827 * @readOnly 2828 * @private 2829 */ 2830 path: { 2831 readOnly: true, 2832 2833 getter: function() 2834 { 2835 if(!this._path) 2836 { 2837 var graphic = this.get("graphic"); 2838 if(graphic) 2839 { 2840 this._path = graphic.addShape({type:"path"}); 2841 } 2842 } 2843 return this._path; 2844 } 2845 }, 2846 2847 /** 2848 * @attribute tickPath 2849 * @type Shape 2850 * @readOnly 2851 * @private 2852 */ 2853 tickPath: { 2854 readOnly: true, 2855 2856 getter: function() 2857 { 2858 if(!this._tickPath) 2859 { 2860 var graphic = this.get("graphic"); 2861 if(graphic) 2862 { 2863 this._tickPath = graphic.addShape({type:"path"}); 2864 } 2865 } 2866 return this._tickPath; 2867 } 2868 }, 2869 2870 /** 2871 * Contains the contents of the axis. 2872 * 2873 * @attribute node 2874 * @type HTMLElement 2875 */ 2876 node: {}, 2877 2878 /** 2879 * Direction of the axis. 2880 * 2881 * @attribute position 2882 * @type String 2883 */ 2884 position: { 2885 lazyAdd: false, 2886 2887 setter: function(val) 2888 { 2889 var LayoutClass = this._layoutClasses[val]; 2890 if(val && val !== "none") 2891 { 2892 this._layout = new LayoutClass(); 2893 } 2894 return val; 2895 } 2896 }, 2897 2898 /** 2899 * Distance determined by the tick styles used to calculate the distance between the axis 2900 * line in relation to the top of the axis. 2901 * 2902 * @attribute topTickOffset 2903 * @type Number 2904 */ 2905 topTickOffset: { 2906 value: 0 2907 }, 2908 2909 /** 2910 * Distance determined by the tick styles used to calculate the distance between the axis 2911 * line in relation to the bottom of the axis. 2912 * 2913 * @attribute bottomTickOffset 2914 * @type Number 2915 */ 2916 bottomTickOffset: { 2917 value: 0 2918 }, 2919 2920 /** 2921 * Distance determined by the tick styles used to calculate the distance between the axis 2922 * line in relation to the left of the axis. 2923 * 2924 * @attribute leftTickOffset 2925 * @type Number 2926 */ 2927 leftTickOffset: { 2928 value: 0 2929 }, 2930 2931 /** 2932 * Distance determined by the tick styles used to calculate the distance between the axis 2933 * line in relation to the right side of the axis. 2934 * 2935 * @attribute rightTickOffset 2936 * @type Number 2937 */ 2938 rightTickOffset: { 2939 value: 0 2940 }, 2941 2942 /** 2943 * Collection of labels used to render the axis. 2944 * 2945 * @attribute labels 2946 * @type Array 2947 */ 2948 labels: { 2949 readOnly: true, 2950 getter: function() 2951 { 2952 return this._labels; 2953 } 2954 }, 2955 2956 /** 2957 * Collection of points used for placement of labels and ticks along the axis. 2958 * 2959 * @attribute tickPoints 2960 * @type Array 2961 */ 2962 tickPoints: { 2963 readOnly: true, 2964 2965 getter: function() 2966 { 2967 if(this.get("position") === "none") 2968 { 2969 return this.get("styles").majorUnit.count; 2970 } 2971 return this._tickPoints; 2972 } 2973 }, 2974 2975 /** 2976 * Indicates whether the axis overlaps the graph. If an axis is the inner most axis on a given 2977 * position and the tick position is inside or cross, the axis will need to overlap the graph. 2978 * 2979 * @attribute overlapGraph 2980 * @type Boolean 2981 */ 2982 overlapGraph: { 2983 value:true, 2984 2985 validator: function(val) 2986 { 2987 return Y_Lang.isBoolean(val); 2988 } 2989 }, 2990 2991 /** 2992 * Length in pixels of largest text bounding box. Used to calculate the height of the axis. 2993 * 2994 * @attribute maxLabelSize 2995 * @type Number 2996 * @protected 2997 */ 2998 maxLabelSize: { 2999 getter: function() 3000 { 3001 return this._maxLabelSize; 3002 }, 3003 3004 setter: function(val) 3005 { 3006 this._maxLabelSize = val; 3007 return val; 3008 } 3009 }, 3010 3011 /** 3012 * Title for the axis. When specified, the title will display. The position of the title is determined by the axis position. 3013 * <dl> 3014 * <dt>top</dt><dd>Appears above the axis and it labels. The default rotation is 0.</dd> 3015 * <dt>right</dt><dd>Appears to the right of the axis and its labels. The default rotation is 90.</dd> 3016 * <dt>bottom</dt><dd>Appears below the axis and its labels. The default rotation is 0.</dd> 3017 * <dt>left</dt><dd>Appears to the left of the axis and its labels. The default rotation is -90.</dd> 3018 * </dl> 3019 * 3020 * @attribute title 3021 * @type String 3022 */ 3023 title: { 3024 value: null 3025 }, 3026 3027 /** 3028 * Function used to append an axis value to an axis label. This function has the following signature: 3029 * <dl> 3030 * <dt>textField</dt><dd>The axis label to be appended. (`HTMLElement`)</dd> 3031 * <dt>val</dt><dd>The value to attach to the text field. This method will accept an `HTMLELement` 3032 * or a `String`. This method does not use (`HTMLElement` | `String`)</dd> 3033 * </dl> 3034 * The default method appends a value to the `HTMLElement` using the `appendChild` method. If the given 3035 * value is a `String`, the method will convert the the value to a `textNode` before appending to the 3036 * `HTMLElement`. This method will not convert an `HTMLString` to an `HTMLElement`. 3037 * 3038 * @attribute appendLabelFunction 3039 * @type Function 3040 */ 3041 appendLabelFunction: { 3042 valueFn: function() 3043 { 3044 return this._setText; 3045 } 3046 }, 3047 3048 /** 3049 * Function used to append a title value to the title object. This function has the following signature: 3050 * <dl> 3051 * <dt>textField</dt><dd>The title text field to be appended. (`HTMLElement`)</dd> 3052 * <dt>val</dt><dd>The value to attach to the text field. This method will accept an `HTMLELement` 3053 * or a `String`. This method does not use (`HTMLElement` | `String`)</dd> 3054 * </dl> 3055 * The default method appends a value to the `HTMLElement` using the `appendChild` method. If the given 3056 * value is a `String`, the method will convert the the value to a `textNode` before appending to the 3057 * `HTMLElement` element. This method will not convert an `HTMLString` to an `HTMLElement`. 3058 * 3059 * @attribute appendTitleFunction 3060 * @type Function 3061 */ 3062 appendTitleFunction: { 3063 valueFn: function() 3064 { 3065 return this._setText; 3066 } 3067 }, 3068 3069 /** 3070 * An array containing the unformatted values of the axis labels. By default, TimeAxis, NumericAxis and 3071 * StackedAxis labelValues are determined by the majorUnit style. By default, CategoryAxis labels are 3072 * determined by the values of the dataProvider. 3073 * <p>When the labelValues attribute is explicitly set, the labelValues are dictated by the set value and 3074 * the position of ticks and labels are determined by where those values would fall on the axis. </p> 3075 * 3076 * @attribute labelValues 3077 * @type Array 3078 */ 3079 labelValues: { 3080 lazyAdd: false, 3081 3082 setter: function(val) 3083 { 3084 var opts = arguments[2]; 3085 if(!val || (opts && opts.src && opts.src === "internal")) 3086 { 3087 this._labelValuesExplicitlySet = false; 3088 } 3089 else 3090 { 3091 this._labelValuesExplicitlySet = true; 3092 } 3093 return val; 3094 } 3095 }, 3096 3097 /** 3098 * Suppresses the creation of the the first visible label and tick. 3099 * 3100 * @attribute hideFirstMajorUnit 3101 * @type Boolean 3102 */ 3103 hideFirstMajorUnit: { 3104 value: false 3105 }, 3106 3107 /** 3108 * Suppresses the creation of the the last visible label and tick. 3109 * 3110 * @attribute hideLastMajorUnit 3111 * @type Boolean 3112 */ 3113 hideLastMajorUnit: { 3114 value: false 3115 } 3116 3117 /** 3118 * Style properties used for drawing an axis. This attribute is inherited from `Renderer`. Below are the default values: 3119 * <dl> 3120 * <dt>majorTicks</dt><dd>Properties used for drawing ticks. 3121 * <dl> 3122 * <dt>display</dt><dd>Position of the tick. Possible values are `inside`, `outside`, `cross` and `none`. 3123 * The default value is `inside`.</dd> 3124 * <dt>length</dt><dd>The length (in pixels) of the tick. The default value is 4.</dd> 3125 * <dt>color</dt><dd>The color of the tick. The default value is `#dad8c9`</dd> 3126 * <dt>weight</dt><dd>Number indicating the width of the tick. The default value is 1.</dd> 3127 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the tick. The default value is 1.</dd> 3128 * </dl> 3129 * </dd> 3130 * <dt>line</dt><dd>Properties used for drawing the axis line. 3131 * <dl> 3132 * <dt>weight</dt><dd>Number indicating the width of the axis line. The default value is 1.</dd> 3133 * <dt>color</dt><dd>The color of the axis line. The default value is `#dad8c9`.</dd> 3134 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the tick. The default value is 1.</dd> 3135 * </dl> 3136 * </dd> 3137 * <dt>majorUnit</dt><dd>Properties used to calculate the `majorUnit` for the axis. 3138 * <dl> 3139 * <dt>determinant</dt><dd>The algorithm used for calculating distance between ticks. The possible options are 3140 * `count` and `distance`. If the `determinant` is `count`, the axis ticks will spaced so that a specified number 3141 * of ticks appear on the axis. If the `determinant` is `distance`, the axis ticks will spaced out according to 3142 * the specified distance. The default value is `count`.</dd> 3143 * <dt>count</dt><dd>Number of ticks to appear on the axis when the `determinant` is `count`. The default value is 11.</dd> 3144 * <dt>distance</dt><dd>The distance (in pixels) between ticks when the `determinant` is `distance`. The default 3145 * value is 75.</dd> 3146 * </dl> 3147 * </dd> 3148 * <dt>label</dt><dd>Properties and styles applied to the axis labels. 3149 * <dl> 3150 * <dt>color</dt><dd>The color of the labels. The default value is `#808080`.</dd> 3151 * <dt>alpha</dt><dd>Number between 0 and 1 indicating the opacity of the labels. The default value is 1.</dd> 3152 * <dt>fontSize</dt><dd>The font-size of the labels. The default value is 85%</dd> 3153 * <dt>rotation</dt><dd>The rotation, in degrees (between -90 and 90) of the labels. The default value is 0.</dd> 3154 * <dt>offset</td><dd>A number between 0 and 1 indicating the relationship of the label to a tick. For a horizontal axis 3155 * label, a value of 0 will position the label's left side even to the the tick. A position of 1 would position the 3156 * right side of the label with the tick. A position of 0.5 would center the label horizontally with the tick. For a 3157 * vertical axis, a value of 0 would position the top of the label with the tick, a value of 1 would position the bottom 3158 * of the label with the tick and a value 0 would center the label vertically with the tick. The default value is 0.5.</dd> 3159 * <dt>margin</dt><dd>The distance between the label and the axis/tick. Depending on the position of the `Axis`, 3160 * only one of the properties used. 3161 * <dl> 3162 * <dt>top</dt><dd>Pixel value used for an axis with a `position` of `bottom`. The default value is 4.</dd> 3163 * <dt>right</dt><dd>Pixel value used for an axis with a `position` of `left`. The default value is 4.</dd> 3164 * <dt>bottom</dt><dd>Pixel value used for an axis with a `position` of `top`. The default value is 4.</dd> 3165 * <dt>left</dt><dd>Pixel value used for an axis with a `position` of `right`. The default value is 4.</dd> 3166 * </dl> 3167 * </dd> 3168 * </dl> 3169 * </dd> 3170 * </dl> 3171 * 3172 * @attribute styles 3173 * @type Object 3174 */ 3175 } 3176 }); 3177 Y.AxisType = Y.Base.create("baseAxis", Y.Axis, [], {}); 3178 3179 3180 }, '3.17.2', {"requires": ["dom", "widget", "widget-position", "widget-stack", "graphics", "axis-base"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |