1/** 2 * Title: jqPlot Charts 3 * 4 * Pure JavaScript plotting plugin for jQuery. 5 * 6 * About: Version 7 * 8 * version: 1.0.8 9 * revision: 1250 10 * 11 * About: Copyright & License 12 * 13 * Copyright (c) 2009-2013 Chris Leonello 14 * jqPlot is currently available for use in all personal or commercial projects 15 * under both the MIT and GPL version 2.0 licenses. This means that you can 16 * choose the license that best suits your project and use it accordingly. 17 * 18 * See <GPL Version 2> and <MIT License> contained within this distribution for further information. 19 * 20 * The author would appreciate an email letting him know of any substantial 21 * use of jqPlot. You can reach the author at: chris at jqplot dot com 22 * or see http://www.jqplot.com/info.php. This is, of course, not required. 23 * 24 * If you are feeling kind and generous, consider supporting the project by 25 * making a donation at: http://www.jqplot.com/donate.php. 26 * 27 * sprintf functions contained in jqplot.sprintf.js by Ash Searle: 28 * 29 * version 2007.04.27 30 * author Ash Searle 31 * http://hexmen.com/blog/2007/03/printf-sprintf/ 32 * http://hexmen.com/js/sprintf.js 33 * The author (Ash Searle) has placed this code in the public domain: 34 * "This code is unrestricted: you are free to use it however you like." 35 * 36 * 37 * About: Introduction 38 * 39 * jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.2 is included in the distribution. 40 * To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally 41 * the excanvas script for IE support in your web page: 42 * 43 * > <!--[if lt IE 9]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]--> 44 * > <script language="javascript" type="text/javascript" src="jquery-1.4.4.min.js"></script> 45 * > <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script> 46 * > <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" /> 47 * 48 * jqPlot can be customized by overriding the defaults of any of the objects which make 49 * up the plot. The general usage of jqplot is: 50 * 51 * > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject}); 52 * 53 * The options available to jqplot are detailed in <jqPlot Options> in the jqPlotOptions.txt file. 54 * 55 * An actual call to $.jqplot() may look like the 56 * examples below: 57 * 58 * > chart = $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]); 59 * 60 * or 61 * 62 * > dataArray = [34,12,43,55,77]; 63 * > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}}); 64 * 65 * For more inforrmation, see <jqPlot Usage>. 66 * 67 * About: Usage 68 * 69 * See <jqPlot Usage> 70 * 71 * About: Available Options 72 * 73 * See <jqPlot Options> for a list of options available thorugh the options object (not complete yet!) 74 * 75 * About: Options Usage 76 * 77 * See <Options Tutorial> 78 * 79 * About: Changes 80 * 81 * See <Change Log> 82 * 83 */ 84 85(function($) { 86 // make sure undefined is undefined 87 var undefined; 88 89 $.fn.emptyForce = function() { 90 for ( var i = 0, elem; (elem = $(this)[i]) != null; i++ ) { 91 // Remove element nodes and prevent memory leaks 92 if ( elem.nodeType === 1 ) { 93 $.cleanData( elem.getElementsByTagName("*") ); 94 } 95 96 // Remove any remaining nodes 97 if ($.jqplot.use_excanvas) { 98 elem.outerHTML = ""; 99 } 100 else { 101 while ( elem.firstChild ) { 102 elem.removeChild( elem.firstChild ); 103 } 104 } 105 106 elem = null; 107 } 108 109 return $(this); 110 }; 111 112 $.fn.removeChildForce = function(parent) { 113 while ( parent.firstChild ) { 114 this.removeChildForce( parent.firstChild ); 115 parent.removeChild( parent.firstChild ); 116 } 117 }; 118 119 $.fn.jqplot = function() { 120 var datas = []; 121 var options = []; 122 // see how many data arrays we have 123 for (var i=0, l=arguments.length; i<l; i++) { 124 if ($.isArray(arguments[i])) { 125 datas.push(arguments[i]); 126 } 127 else if ($.isPlainObject(arguments[i])) { 128 options.push(arguments[i]); 129 } 130 } 131 132 return this.each(function(index) { 133 var tid, 134 plot, 135 $this = $(this), 136 dl = datas.length, 137 ol = options.length, 138 data, 139 opts; 140 141 if (index < dl) { 142 data = datas[index]; 143 } 144 else { 145 data = dl ? datas[dl-1] : null; 146 } 147 148 if (index < ol) { 149 opts = options[index]; 150 } 151 else { 152 opts = ol ? options[ol-1] : null; 153 } 154 155 // does el have an id? 156 // if not assign it one. 157 tid = $this.attr('id'); 158 if (tid === undefined) { 159 tid = 'jqplot_target_' + $.jqplot.targetCounter++; 160 $this.attr('id', tid); 161 } 162 163 plot = $.jqplot(tid, data, opts); 164 165 $this.data('jqplot', plot); 166 }); 167 }; 168 169 170 /** 171 * Namespace: $.jqplot 172 * jQuery function called by the user to create a plot. 173 * 174 * Parameters: 175 * target - ID of target element to render the plot into. 176 * data - an array of data series. 177 * options - user defined options object. See the individual classes for available options. 178 * 179 * Properties: 180 * config - object to hold configuration information for jqPlot plot object. 181 * 182 * attributes: 183 * enablePlugins - False to disable plugins by default. Plugins must then be explicitly 184 * enabled in the individual plot options. Default: false. 185 * This property sets the "show" property of certain plugins to true or false. 186 * Only plugins that can be immediately active upon loading are affected. This includes 187 * non-renderer plugins like cursor, dragable, highlighter, and trendline. 188 * defaultHeight - Default height for plots where no css height specification exists. This 189 * is a jqplot wide default. 190 * defaultWidth - Default height for plots where no css height specification exists. This 191 * is a jqplot wide default. 192 */ 193 194 $.jqplot = function(target, data, options) { 195 var _data = null, _options = null; 196 197 if (arguments.length === 3) { 198 _data = data; 199 _options = options; 200 } 201 202 else if (arguments.length === 2) { 203 if ($.isArray(data)) { 204 _data = data; 205 } 206 207 else if ($.isPlainObject(data)) { 208 _options = data; 209 } 210 } 211 212 if (_data === null && _options !== null && _options.data) { 213 _data = _options.data; 214 } 215 216 var plot = new jqPlot(); 217 // remove any error class that may be stuck on target. 218 $('#'+target).removeClass('jqplot-error'); 219 220 if ($.jqplot.config.catchErrors) { 221 try { 222 plot.init(target, _data, _options); 223 plot.draw(); 224 plot.themeEngine.init.call(plot); 225 return plot; 226 } 227 catch(e) { 228 var msg = $.jqplot.config.errorMessage || e.message; 229 $('#'+target).append('<div class="jqplot-error-message">'+msg+'</div>'); 230 $('#'+target).addClass('jqplot-error'); 231 document.getElementById(target).style.background = $.jqplot.config.errorBackgroun
231d; 232 document.getElementById(target).style.border = $.jqplot.config.errorBorder; 233 document.getElementById(target).style.fontFamily = $.jqplot.config.errorFontFamily; 234 document.getElementById(target).style.fontSize = $.jqplot.config.errorFontSize; 235 document.getElementById(target).style.fontStyle = $.jqplot.config.errorFontStyle; 236 document.getElementById(target).style.fontWeight = $.jqplot.config.errorFontWeight; 237 } 238 } 239 else { 240 plot.init(target, _data, _options); 241 plot.draw(); 242 plot.themeEngine.init.call(plot); 243 return plot; 244 } 245 }; 246 247 $.jqplot.version = "1.0.8"; 248 $.jqplot.revision = "1250"; 249 250 $.jqplot.targetCounter = 1; 251 252 // canvas manager to reuse canvases on the plot. 253 // Should help solve problem of canvases not being freed and 254 // problem of waiting forever for firefox to decide to free memory. 255 $.jqplot.CanvasManager = function() { 256 // canvases are managed globally so that they can be reused 257 // across plots after they have been freed 258 if (typeof $.jqplot.CanvasManager.canvases == 'undefined') { 259 $.jqplot.CanvasManager.canvases = []; 260 $.jqplot.CanvasManager.free = []; 261 } 262 263 var myCanvases = []; 264 265 this.getCanvas = function() { 266 var canvas; 267 var makeNew = true; 268 269 if (!$.jqplot.use_excanvas) { 270 for (var i = 0, l = $.jqplot.CanvasManager.canvases.length; i < l; i++) { 271 if ($.jqplot.CanvasManager.free[i] === true) { 272 makeNew = false; 273 canvas = $.jqplot.CanvasManager.canvases[i]; 274 // $(canvas).removeClass('jqplot-canvasManager-free').addClass('jqplot-canvasManager-inuse'); 275 $.jqplot.CanvasManager.free[i] = false; 276 myCanvases.push(i); 277 break; 278 } 279 } 280 } 281 282 if (makeNew) { 283 canvas = document.createElement('canvas'); 284 myCanvases.push($.jqplot.CanvasManager.canvases.length); 285 $.jqplot.CanvasManager.canvases.push(canvas); 286 $.jqplot.CanvasManager.free.push(false); 287 } 288 289 return canvas; 290 }; 291 292 // this method has to be used after settings the dimesions 293 // on the element returned by getCanvas() 294 this.initCanvas = function(canvas) { 295 if ($.jqplot.use_excanvas) { 296 return window.G_vmlCanvasManager.initElement(canvas); 297 } 298 return canvas; 299 }; 300 301 this.freeAllCanvases = function() { 302 for (var i = 0, l=myCanvases.length; i < l; i++) { 303 this.freeCanvas(myCanvases[i]); 304 } 305 myCanvases = []; 306 }; 307 308 this.freeCanvas = function(idx) { 309 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 310 // excanvas can't be reused, but properly unset 311 window.G_vmlCanvasManager.uninitElement($.jqplot.CanvasManager.canvases[idx]); 312 $.jqplot.CanvasManager.canvases[idx] = null; 313 } 314 else { 315 var canvas = $.jqplot.CanvasManager.canvases[idx]; 316 canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); 317 $(canvas).unbind().removeAttr('class').removeAttr('style'); 318 // Style attributes seemed to be still hanging around. wierd. Some ticks 319 // still retained a left: 0px attribute after reusing a canvas. 320 $(canvas).css({left: '', top: '', position: ''}); 321 // setting size to 0 may save memory of unused canvases? 322 canvas.width = 0; 323 canvas.height = 0; 324 $.jqplot.CanvasManager.free[idx] = true; 325 } 326 }; 327 328 }; 329 330 331 // Convienence function that won't hang IE or FF without FireBug. 332 $.jqplot.log = function() { 333 if (window.console) { 334 window.console.log.apply(window.console, arguments); 335 } 336 }; 337 338 $.jqplot.config = { 339 addDomReference: false, 340 enablePlugins:false, 341 defaultHeight:300, 342 defaultWidth:400, 343 UTCAdjust:false, 344 timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000), 345 errorMessage: '',
346 errorBackground: '', 347 errorBorder: '', 348 errorFontFamily: '', 349 errorFontSize: '', 350 errorFontStyle: '', 351 errorFontWeight: '', 352 catchErrors: false, 353 defaultTickFormatString: "%.1f", 354 defaultColors: [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], 355 defaultNegativeColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"], 356 dashLength: 4, 357 gapLength: 4, 358 dotGapLength: 2.5, 359 srcLocation: 'jqplot/src/', 360 pluginLocation: 'jqplot/src/plugins/' 361 }; 362 363 364 $.jqplot.arrayMax = function( array ){ 365 return Math.max.apply( Math, array ); 366 }; 367 368 $.jqplot.arrayMin = function( array ){ 369 return Math.min.apply( Math, array ); 370 }; 371 372 $.jqplot.enablePlugins = $.jqplot.config.enablePlugins; 373 374 // canvas related tests taken from modernizer: 375 // Copyright (c) 2009 - 2010 Faruk Ates. 376 // http://www.modernizr.com 377 378 $.jqplot.support_canvas = function() { 379 if (typeof $.jqplot.support_canvas.result == 'undefined') { 380 $.jqplot.support_canvas.result = !!document.createElement('canvas').getContext; 381 } 382 return $.jqplot.support_canvas.result; 383 }; 384 385 $.jqplot.support_canvas_text = function() { 386 if (typeof $.jqplot.support_canvas_text.result == 'undefined') { 387 if (window.G_vmlCanvasManager !== undefined && window.G_vmlCanvasManager._version > 887) { 388 $.jqplot.support_canvas_text.result = true; 389 } 390 else { 391 $.jqplot.support_canvas_text.result = !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 392 } 393 394 } 395 return $.jqplot.support_canvas_text.result; 396 }; 397 398 $.jqplot.use_excanvas = ((!$.support.boxModel || !$.support.objectAll || !$support.leadingWhitespace) && !$.jqplot.support_canvas()) ? true : false; 399 400 /** 401 * 402 * Hooks: jqPlot Pugin Hooks 403 * 404 * $.jqplot.preInitHooks - called before initialization. 405 * $.jqplot.postInitHooks - called after initialization. 406 * $.jqplot.preParseOptionsHooks - called before user options are parsed. 407 * $.jqplot.postParseOptionsHooks - called after user options are parsed. 408 * $.jqplot.preDrawHooks - called before plot draw. 409 * $.jqplot.postDrawHooks - called after plot draw. 410 * $.jqplot.preDrawSeriesHooks - called before each series is drawn. 411 * $.jqplot.postDrawSeriesHooks - called after each series is drawn. 412 * $.jqplot.preDrawLegendHooks - called before the legend is drawn. 413 * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins 414 * can add rows to the legend table. 415 * $.jqplot.preSeriesInitHooks - called before series is initialized. 416 * $.jqplot.postSeriesInitHooks - called after series is initialized. 417 * $.jqplot.preParseSeriesOptionsHooks - called before series related options 418 * are parsed. 419 * $.jqplot.postParseSeriesOptionsHooks - called after series related options 420 * are parsed. 421 * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds 422 * listeners to the event canvas which lays on top of the grid area. 423 * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn. 424 * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn. 425 * 426 */ 427 428 $.jqplot.preInitHooks = []; 429 $.jqplot.postInitHooks = []; 430 $.jqplot.preParseOptionsHooks = []; 431 $.jqplot.postParseOptionsHooks = []; 432 $.jqplot.preDrawHooks = []; 433 $.jqplot.postDrawHooks = []; 434 $.jqplot.preDrawSeriesHooks = []; 435 $.jqplot.postDrawSeriesHooks = []; 436 $.jqplot.preDrawLegendHooks = []; 437 $.jqplot.addLegendRowHooks = []; 438 $.jqplot.preSeriesInitHooks = []; 439 $.jqplot.postSeriesInitHooks = []; 440 $.jqplot.preParseSeriesOptionsHooks = []; 441 $.jqplot.postParseSeriesOptionsHooks = [];
442 $.jqplot.eventListenerHooks = []; 443 $.jqplot.preDrawSeriesShadowHooks = []; 444 $.jqplot.postDrawSeriesShadowHooks = []; 445 446 // A superclass holding some common properties and methods. 447 $.jqplot.ElemContainer = function() { 448 this._elem; 449 this._plotWidth; 450 this._plotHeight; 451 this._plotDimensions = {height:null, width:null}; 452 }; 453 454 $.jqplot.ElemContainer.prototype.createElement = function(el, offsets, clss, cssopts, attrib) { 455 this._offsets = offsets; 456 var klass = clss || 'jqplot'; 457 var elem = document.createElement(el); 458 this._elem = $(elem); 459 this._elem.addClass(klass); 460 this._elem.css(cssopts); 461 this._elem.attr(attrib); 462 // avoid memory leak; 463 elem = null; 464 return this._elem; 465 }; 466 467 $.jqplot.ElemContainer.prototype.getWidth = function() { 468 if (this._elem) { 469 return this._elem.outerWidth(true); 470 } 471 else { 472 return null; 473 } 474 }; 475 476 $.jqplot.ElemContainer.prototype.getHeight = function() { 477 if (this._elem) { 478 return this._elem.outerHeight(true); 479 } 480 else { 481 return null; 482 } 483 }; 484 485 $.jqplot.ElemContainer.prototype.getPosition = function() { 486 if (this._elem) { 487 return this._elem.position(); 488 } 489 else { 490 return {top:null, left:null, bottom:null, right:null}; 491 } 492 }; 493 494 $.jqplot.ElemContainer.prototype.getTop = function() { 495 return this.getPosition().top; 496 }; 497 498 $.jqplot.ElemContainer.prototype.getLeft = function() { 499 return this.getPosition().left; 500 }; 501 502 $.jqplot.ElemContainer.prototype.getBottom = function() { 503 return this._elem.css('bottom'); 504 }; 505 506 $.jqplot.ElemContainer.prototype.getRight = function() { 507 return this._elem.css('right'); 508 }; 509 510 511 /** 512 * Class: Axis 513 * An individual axis object. Cannot be instantiated directly, but created 514 * by the Plot object. Axis properties can be set or overridden by the 515 * options passed in from the user. 516 * 517 */ 518 function Axis(name) { 519 $.jqplot.ElemContainer.call(this); 520 // Group: Properties 521 // 522 // Axes options are specified within an axes object at the top level of the 523 // plot options like so: 524 // > { 525 // > axes: { 526 // > xaxis: {min: 5}, 527 // > yaxis: {min: 2, max: 8, numberTicks:4}, 528 // > x2axis: {pad: 1.5}, 529 // > y2axis: {ticks:[22, 44, 66, 88]} 530 // > } 531 // > } 532 // There are 2 x axes, 'xaxis' and 'x2axis', and 533 // 9 yaxes, 'yaxis', 'y2axis'. 'y3axis', ... Any or all of which may be specified. 534 this.name = name; 535 this._series = []; 536 // prop: show 537 // Wether to display the axis on the graph. 538 this.show = false; 539 // prop: tickRenderer 540 // A class of a rendering engine for creating the ticks labels displayed on the plot, 541 // See <$.jqplot.AxisTickRenderer>. 542 this.tickRenderer = $.jqplot.AxisTickRenderer; 543 // prop: tickOptions 544 // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options. 545 this.tickOptions = {}; 546 // prop: labelRenderer 547 // A class of a rendering engine for creating an axis label. 548 this.labelRenderer = $.jqplot.AxisLabelRenderer; 549 // prop: labelOptions 550 // Options passed to the label renderer. 551 this.labelOptions = {}; 552 // prop: label 553 // Label for the axis 554 this.label = null; 555 // prop: showLabel 556 // true to show the axis label. 557 this.showLabel = true; 558 // prop: min 559 // minimum value of the axis (in data units, not pixels). 560 this.min = null; 561 // prop: max 562 // maximum value of the axis (in data units, not pixels). 563 this.max = null; 564 // prop: autoscale 565 // DEPRECATED 566 // the default scaling algorithm produces superior results. 567 this.autoscale = false;
568 // prop: pad 569 // Padding to extend the range above and below the data bounds. 570 // The data range is multiplied by this factor to determine minimum and maximum axis bounds. 571 // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0. 572 this.pad = 1.2; 573 // prop: padMax 574 // Padding to extend the range above data bounds. 575 // The top of the data range is multiplied by this factor to determine maximum axis bounds. 576 // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0. 577 this.padMax = null; 578 // prop: padMin 579 // Padding to extend the range below data bounds. 580 // The bottom of the data range is multiplied by this factor to determine minimum axis bounds. 581 // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0. 582 this.padMin = null; 583 // prop: ticks 584 // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis. 585 // If no label is specified, the value is formatted into an appropriate label. 586 this.ticks = []; 587 // prop: numberTicks 588 // Desired number of ticks. Default is to compute automatically. 589 this.numberTicks; 590 // prop: tickInterval 591 // number of units between ticks. Mutually exclusive with numberTicks. 592 this.tickInterval; 593 // prop: renderer 594 // A class of a rendering engine that handles tick generation, 595 // scaling input data to pixel grid units and drawing the axis element. 596 this.renderer = $.jqplot.LinearAxisRenderer; 597 // prop: rendererOptions 598 // renderer specific options. See <$.jqplot.LinearAxisRenderer> for options. 599 this.rendererOptions = {}; 600 // prop: showTicks 601 // Wether to show the ticks (both marks and labels) or not. 602 // Will not override showMark and showLabel options if specified on the ticks themselves. 603 this.showTicks = true; 604 // prop: showTickMarks 605 // Wether to show the tick marks (line crossing grid) or not. 606 // Overridden by showTicks and showMark option of tick itself. 607 this.showTickMarks = true; 608 // prop: showMinorTicks 609 // Wether or not to show minor ticks. This is renderer dependent. 610 this.showMinorTicks = true; 611 // prop: drawMajorGridlines 612 // True to draw gridlines for major axis ticks. 613 this.drawMajorGridlines = true; 614 // prop: drawMinorGridlines 615 // True to draw gridlines for minor ticks. 616 this.drawMinorGridlines = false; 617 // prop: drawMajorTickMarks 618 // True to draw tick marks for major axis ticks. 619 this.drawMajorTickMarks = true; 620 // prop: drawMinorTickMarks 621 // True to draw tick marks for minor ticks. This is renderer dependent. 622 this.drawMinorTickMarks = true; 623 // prop: useSeriesColor 624 // Use the color of the first series associated with this axis for the 625 // tick marks and line bordering this axis. 626 this.useSeriesColor = false; 627 // prop: borderWidth 628 // width of line stroked at the border of the axis. Defaults 629 // to the width of the grid boarder. 630 this.borderWidth = null; 631 // prop: borderColor 632 // color of the border adjacent to the axis. Defaults to grid border color. 633 this.borderColor = null; 634 // prop: scaleToHiddenSeries 635 // True to include hidden series when computing axes bounds and scaling. 636 this.scaleToHiddenSeries = false; 637 // minimum and maximum values on the axis. 638 this._dataBounds = {min:null, max:null}; 639 // statistics (min, max, mean) as well as actual data intervals for each series attached to axis. 640 // holds collection of {intervals:[], min:, max:, mean: } objects for each series on axis. 641 this._intervalStats = []; 642 // pixel position from the top left of the min value and max value on the axis. 643 this._offsets = {min:null, max:null}; 644 this._ticks=[]; 645 this._label = null; 646 // prop: syncTicks 647 // true to try and synchronize tick spacing across multiple axes so that ticks and 648 // grid lines line up. This has an impact on autoscaling algorithm, however. 649 // In general, autoscaling an individual axis will work better if it does not 650 // have to sync ticks. 651 this.syncTicks = null; 652 // prop: tickSpacing 653 // Approximate pixel spacing between ticks on graph. Used during autoscaling. 654 // This number will be an upper bound, actual spacing will be less. 655 this.tickSpacing = 75; 656 // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks 657 // so they can be restored if altered by plugins. 658 this._min = null; 659 this._max = null; 660 this._tickInterval = null; 661 this._numberTicks = null; 662 this.__ticks = null; 663 // hold original user options. 664 this._options = {}; 665 } 666 667 Axis.prototype = new $.jqplot.ElemContainer(); 668 Axis.prototype.constructor = Axis; 669 670 Axis.prototype.init = function() { 671 if ($.isFunction(this.renderer)) { 672 this.renderer = new this.renderer(); 673 } 674 // set the axis name 675 this.tickOptions.axis = this.name; 676 // if showMark or showLabel tick options not specified, use value of axis option. 677 // showTicks overrides showTickMarks. 678 if (this.tickOptions.showMark == null) { 679 this.tickOptions.showMark = this.showTicks; 680 } 681 if (this.tickOptions.showMark == null) { 682 this.tickOptions.showMark = this.showTickMarks; 683 } 684 if (this.tickOptions.showLabel == null) { 685 this.tickOptions.showLabel = this.showTicks; 686 } 687 688 if (this.label == null || this.label == '') { 689 this.showLabel = false;
690 } 691 else { 692 this.labelOptions.label = this.label; 693 } 694 if (this.showLabel == false) { 695 this.labelOptions.show = false; 696 } 697 // set the default padMax, padMin if not specified 698 // special check, if no padding desired, padding 699 // should be set to 1.0 700 if (this.pad == 0) { 701 this.pad = 1.0; 702 } 703 if (this.padMax == 0) { 704 this.padMax = 1.0; 705 } 706 if (this.padMin == 0) { 707 this.padMin = 1.0; 708 } 709 if (this.padMax == null) { 710 this.padMax = (this.pad-1)/2 + 1; 711 } 712 if (this.padMin == null) { 713 this.padMin = (this.pad-1)/2 + 1; 714 } 715 // now that padMin and padMax are correctly set, reset pad in case user has supplied 716 // padMin and/or padMax 717 this.pad = this.padMax + this.padMin - 1; 718 if (this.min != null || this.max != null) { 719 this.autoscale = false; 720 } 721 // if not set, sync ticks for y axes but not x by default. 722 if (this.syncTicks == null && this.name.indexOf('y') > -1) { 723 this.syncTicks = true; 724 } 725 else if (this.syncTicks == null){ 726 this.syncTicks = false; 727 } 728 this.renderer.init.call(this, this.rendererOptions); 729 730 }; 731 732 Axis.prototype.draw = function(ctx, plot) { 733 // Memory Leaks patch 734 if (this.__ticks) { 735 this.__ticks = null; 736 } 737 738 return this.renderer.draw.call(this, ctx, plot); 739 740 }; 741 742 Axis.prototype.set = function() { 743 this.renderer.set.call(this); 744 }; 745 746 Axis.prototype.pack = function(pos, offsets) { 747 if (this.show) { 748 this.renderer.pack.call(this, pos, offsets); 749 } 750 // these properties should all be available now. 751 if (this._min == null) { 752 this._min = this.min; 753 this._max = this.max; 754 this._tickInterval = this.tickInterval; 755 this._numberTicks = this.numberTicks; 756 this.__ticks = this._ticks; 757 } 758 }; 759 760 // reset the axis back to original values if it has been scaled, zoomed, etc. 761 Axis.prototype.reset = function() { 762 this.renderer.reset.call(this); 763 }; 764 765 Axis.prototype.resetScale = function(opts) { 766 $.extend(true, this, {min: null, max: null, numberTicks: null, tickInterval: null, _ticks: [], ticks: []}, opts); 767 this.resetDataBounds(); 768 }; 769 770 Axis.prototype.resetDataBounds = function() { 771 // Go through all the series attached to this axis and find 772 // the min/max bounds for this axis. 773 var db = this._dataBounds; 774 db.min = null; 775 db.max = null; 776 var l, s, d; 777 // check for when to force min 0 on bar series plots. 778 var doforce = (this.show) ? true : false; 779 for (var i=0; i<this._series.length; i++) { 780 s = this._series[i]; 781 if (s.show || this.scaleToHiddenSeries) { 782 d = s._plotData; 783 if (s._type === 'line' && s.renderer.bands.show && this.name.charAt(0) !== 'x') { 784 d = [[0, s.renderer.bands._min], [1, s.renderer.bands._max]]; 785 } 786 787 var minyidx = 1, maxyidx = 1; 788 789 if (s._type != null && s._type == 'ohlc') { 790 minyidx = 3; 791 maxyidx = 2; 792 } 793 794 for (var j=0, l=d.length; j<l; j++) { 795 if (this.name == 'xaxis' || this.name == 'x2axis') { 796 if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) { 797 db.min = d[j][0]; 798 } 799 if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) { 800 db.max = d[j][0]; 801 } 802 } 803 else { 804 if ((d[j][minyidx] != null && d[j][minyidx] < db.min) || db.min == null) {
805 db.min = d[j][minyidx]; 806 } 807 if ((d[j][maxyidx] != null && d[j][maxyidx] > db.max) || db.max == null) { 808 db.max = d[j][maxyidx]; 809 } 810 } 811 } 812 813 // Hack to not pad out bottom of bar plots unless user has specified a padding. 814 // every series will have a chance to set doforce to false. once it is set to 815 // false, it cannot be reset to true. 816 // If any series attached to axis is not a bar, wont force 0. 817 if (doforce && s.renderer.constructor !== $.jqplot.BarRenderer) { 818 doforce = false; 819 } 820 821 else if (doforce && this._options.hasOwnProperty('forceTickAt0') && this._options.forceTickAt0 == false) { 822 doforce = false; 823 } 824 825 else if (doforce && s.renderer.constructor === $.jqplot.BarRenderer) { 826 if (s.barDirection == 'vertical' && this.name != 'xaxis' && this.name != 'x2axis') { 827 if (this._options.pad != null || this._options.padMin != null) { 828 doforce = false; 829 } 830 } 831 832 else if (s.barDirection == 'horizontal' && (this.name == 'xaxis' || this.name == 'x2axis')) { 833 if (this._options.pad != null || this._options.padMin != null) { 834 doforce = false; 835 } 836 } 837 838 } 839 } 840 } 841 842 if (doforce && this.renderer.constructor === $.jqplot.LinearAxisRenderer && db.min >= 0) { 843 this.padMin = 1.0; 844 this.forceTickAt0 = true; 845 } 846 }; 847 848 /** 849 * Class: Legend 850 * Legend object. Cannot be instantiated directly, but created 851 * by the Plot object. Legend properties can be set or overridden by the 852 * options passed in from the user. 853 */ 854 function Legend(options) { 855 $.jqplot.ElemContainer.call(this); 856 // Group: Properties 857 858 // prop: show 859 // Wether to display the legend on the graph. 860 this.show = false; 861 // prop: location 862 // Placement of the legend. one of the compass directions: nw, n, ne, e, se, s, sw, w 863 this.location = 'ne'; 864 // prop: labels 865 // Array of labels to use. By default the renderer will look for labels on the series. 866 // Labels specified in this array will override labels specified on the series. 867 this.labels = []; 868 // prop: showLabels 869 // true to show the label text on the legend. 870 this.showLabels = true; 871 // prop: showSwatch 872 // true to show the color swatches on the legend. 873 this.showSwatches = true; 874 // prop: placement 875 // "insideGrid" places legend inside the grid area of the plot. 876 // "outsideGrid" places the legend outside the grid but inside the plot container, 877 // shrinking the grid to accomodate the legend. 878 // "inside" synonym for "insideGrid", 879 // "outside" places the legend ouside the grid area, but does not shrink the grid which 880 // can cause the legend to overflow the plot container. 881 this.placement = "insideGrid"; 882 // prop: xoffset 883 // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc. 884 // properties or via CSS margin styling of the .jqplot-table-legend class. 885 this.xoffset = 0; 886 // prop: yoffset 887 // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc. 888 // properties or via CSS margin styling of the .jqplot-table-legend class. 889 this.yoffset = 0; 890 // prop: border 891 // css spec for the border around the legend box. 892 this.border; 893 // prop: background 894 // css spec for the background of the legend box. 895 this.background; 896 // prop: textColor 897 // css color spec for the legend text. 898 this.textColor; 899 // prop: fontFamily 900 // css font-family spec for the legend text. 901 this.fontFamily; 902 // prop: fontSize 903 // css font-size spec for the legend text. 904 this.fontSize ; 905 // prop: rowSpacing 906 // css padding-top spec for the rows in the legend. 907 this.rowSpacing = '0.5em'; 908 // renderer 909 // A class that will create a DOM object for the legend, 910 // see <$.jqplot.TableLegendRenderer>. 911 this.renderer = $.jqplot.TableLegendRenderer; 912 // prop: rendererOptions 913 // renderer specific options passed to the renderer. 914 this.rendererOptions = {}; 915 // prop: predraw 916 // Wether to draw the legend before the series or not. 917 // Used with series specific legend renderers for pie, donut, mekko charts, etc. 918 this.preDraw = false;
919 // prop: marginTop 920 // CSS margin for the legend DOM element. This will set an element 921 // CSS style for the margin which will override any style sheet setting. 922 // The default will be taken from the stylesheet. 923 this.marginTop = null; 924 // prop: marginRight 925 // CSS margin for the legend DOM element. This will set an element 926 // CSS style for the margin which will override any style sheet setting. 927 // The default will be taken from the stylesheet. 928 this.marginRight = null; 929 // prop: marginBottom 930 // CSS margin for the legend DOM element. This will set an element 931 // CSS style for the margin which will override any style sheet setting. 932 // The default will be taken from the stylesheet. 933 this.marginBottom = null; 934 // prop: marginLeft 935 // CSS margin for the legend DOM element. This will set an element 936 // CSS style for the margin which will override any style sheet setting. 937 // The default will be taken from the stylesheet. 938 this.marginLeft = null; 939 // prop: escapeHtml 940 // True to escape special characters with their html entity equivalents 941 // in legend text. "<" becomes < and so on, so html tags are not rendered. 942 this.escapeHtml = false; 943 this._series = []; 944 945 $.extend(true, this, options); 946 } 947 948 Legend.prototype = new $.jqplot.ElemContainer(); 949 Legend.prototype.constructor = Legend; 950 951 Legend.prototype.setOptions = function(options) { 952 $.extend(true, this, options); 953 954 // Try to emulate deprecated behaviour 955 // if user has specified xoffset or yoffset, copy these to 956 // the margin properties. 957 958 if (this.placement == 'inside') { 959 this.placement = 'insideGrid'; 960 } 961 962 if (this.xoffset >0) { 963 if (this.placement == 'insideGrid') { 964 switch (this.location) { 965 case 'nw': 966 case 'w': 967 case 'sw': 968 if (this.marginLeft == null) { 969 this.marginLeft = this.xoffset + 'px'; 970 } 971 this.marginRight = '0px'; 972 break; 973 case 'ne': 974 case 'e': 975 case 'se': 976 default: 977 if (this.marginRight == null) { 978 this.marginRight = this.xoffset + 'px'; 979 } 980 this.marginLeft = '0px'; 981 break; 982 } 983 } 984 else if (this.placement == 'outside') { 985 switch (this.location) { 986 case 'nw': 987 case 'w': 988 case 'sw': 989 if (this.marginRight == null) { 990 this.marginRight = this.xoffset + 'px'; 991 } 992 this.marginLeft = '0px'; 993 break; 994 case 'ne': 995 case 'e': 996 case 'se': 997 default: 998 if (this.marginLeft == null) { 999 this.marginLeft = this.xoffset + 'px'; 1000 } 1001 this.marginRight = '0px'; 1002 break; 1003 } 1004 } 1005 this.xoffset = 0; 1006 } 1007 1008 if (this.yoffset >0) { 1009 if (this.placement == 'outside') { 1010 switch (this.location) { 1011 case 'sw': 1012 case 's': 1013 case 'se': 1014 if (this.marginTop == null) { 1015 this.marginTop = this.yoffset + 'px'; 1016 } 1017 this.marginBottom = '0px'; 1018 break; 1019 case 'ne': 1020 case 'n': 1021 case 'nw': 1022 default: 1023 if (this.marginBottom == null) { 1024 this.marginBottom = this.yoffset + 'px'; 1025 } 1026 this.marginTop = '0px'; 1027 break; 1028 } 1029 } 1030 else if (this.placement == 'insideGrid') { 1031 switch (this.location) { 1032 case 'sw': 1033 case 's': 1034 case 'se': 1035 if (this.marginBottom == null) { 1036 this.marginBottom = this.yoffset + 'px'; 1037 } 1038 this.marginTop = '0px'; 1039 break;
1040 case 'ne': 1041 case 'n': 1042 case 'nw': 1043 default: 1044 if (this.marginTop == null) { 1045 this.marginTop = this.yoffset + 'px'; 1046 } 1047 this.marginBottom = '0px'; 1048 break; 1049 } 1050 } 1051 this.yoffset = 0; 1052 } 1053 1054 // TO-DO: 1055 // Handle case where offsets are < 0. 1056 // 1057 }; 1058 1059 Legend.prototype.init = function() { 1060 if ($.isFunction(this.renderer)) { 1061 this.renderer = new this.renderer(); 1062 } 1063 this.renderer.init.call(this, this.rendererOptions); 1064 }; 1065 1066 Legend.prototype.draw = function(offsets, plot) { 1067 for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){ 1068 $.jqplot.preDrawLegendHooks[i].call(this, offsets); 1069 } 1070 return this.renderer.draw.call(this, offsets, plot); 1071 }; 1072 1073 Legend.prototype.pack = function(offsets) { 1074 this.renderer.pack.call(this, offsets); 1075 }; 1076 1077 /** 1078 * Class: Title 1079 * Plot Title object. Cannot be instantiated directly, but created 1080 * by the Plot object. Title properties can be set or overridden by the 1081 * options passed in from the user. 1082 * 1083 * Parameters: 1084 * text - text of the title. 1085 */ 1086 function Title(text) { 1087 $.jqplot.ElemContainer.call(this); 1088 // Group: Properties 1089 1090 // prop: text 1091 // text of the title; 1092 this.text = text; 1093 // prop: show 1094 // whether or not to show the title 1095 this.show = true; 1096 // prop: fontFamily 1097 // css font-family spec for the text. 1098 this.fontFamily; 1099 // prop: fontSize 1100 // css font-size spec for the text. 1101 this.fontSize ; 1102 // prop: textAlign 1103 // css text-align spec for the text. 1104 this.textAlign; 1105 // prop: textColor 1106 // css color spec for the text. 1107 this.textColor; 1108 // prop: renderer 1109 // A class for creating a DOM element for the title, 1110 // see <$.jqplot.DivTitleRenderer>. 1111 this.renderer = $.jqplot.DivTitleRenderer; 1112 // prop: rendererOptions 1113 // renderer specific options passed to the renderer. 1114 this.rendererOptions = {}; 1115 // prop: escapeHtml 1116 // True to escape special characters with their html entity equivalents 1117 // in title text. "<" becomes < and so on, so html tags are not rendered. 1118 this.escapeHtml = false; 1119 } 1120 1121 Title.prototype = new $.jqplot.ElemContainer(); 1122 Title.prototype.constructor = Title; 1123 1124 Title.prototype.init = function() { 1125 if ($.isFunction(this.renderer)) { 1126 this.renderer = new this.renderer(); 1127 } 1128 this.renderer.init.call(this, this.rendererOptions); 1129 }; 1130 1131 Title.prototype.draw = function(width) { 1132 return this.renderer.draw.call(this, width); 1133 }; 1134 1135 Title.prototype.pack = function() { 1136 this.renderer.pack.call(this); 1137 }; 1138 1139 1140 /** 1141 * Class: Series 1142 * An individual data series object. Cannot be instantiated directly, but created 1143 * by the Plot object. Series properties can be set or overridden by the 1144 * options passed in from the user. 1145 */ 1146 function Series(options) { 1147 options = options || {}; 1148 $.jqplot.ElemContainer.call(this); 1149 // Group: Properties 1150 // Properties will be assigned from a series array at the top level of the 1151 // options. If you had two series and wanted to change the color and line 1152 // width of the first and set the second to use the secondary y axis with 1153 // no shadow and supply custom labels for each: 1154 // > { 1155 // > series:[ 1156 // > {color: '#ff4466', lineWidth: 5, label:'good line'}, 1157 // > {yaxis: 'y2axis', shadow: false, label:'bad line'} 1158 // > ] 1159 // > } 1160 1161 // prop: show 1162 // whether or not to draw the series. 1163 this.show = true; 1164 // prop: xaxis 1165 // which x axis to use with this series, either 'xaxis' or 'x2axis'. 1166 this.xaxis = 'xaxis'; 1167 this._xaxis; 1168 // prop: yaxis 1169 // which y axis to use with this series, either 'yaxis' or 'y2axis'. 1170 this.yaxis = 'yaxis'; 1171 this._yaxis; 1172 this.gridBorderWidth = 2.0; 1173 // prop: renderer 1174 // A class of a renderer which will draw the series, 1175 // see <$.jqplot.LineRenderer>. 1176 this.renderer = $.jqplot.LineRenderer; 1177 // prop: rendererOptions 1178 // Options to pass on to the renderer. 1179 this.rendererOptions = {}; 1180 this.data = []; 1181 this.gridData = []; 1182 // prop: label 1183 // Line label to use in the legend. 1184 this.label = ''; 1185 // prop: showLabel 1186 // true to show label for this series in the legend. 1187 this.showLabel = true; 1188 // prop: color 1189 // css color spec for the series 1190 this.color; 1191 // prop: negativeColor 1192 // css color spec used for filled (area) plots that are filled to zero and 1193 // the "useNegativeColors" option is true. 1194 this.negativeColor; 1195 // prop: lineWidth 1196 // width of the line in pixels. May have different meanings depending on renderer. 1197 this.lineWidth = 2.5; 1198 // prop: lineJoin 1199 // Canvas lineJoin style between segments of series. 1200 this.lineJoin = 'round'; 1201 // prop: lineCap 1202 // Canvas lineCap style at ends of line. 1203 this.lineCap = 'round'; 1204 // prop: linePattern 1205 // line pattern 'dashed', 'dotted', 'solid', some combination 1206 // of '-' and '.' characters such as '.-.' or a numerical array like 1207 // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, 1208 // [1, 10, 20, 10] to draw a dot-dash line, and so on. 1209 this.linePattern = 'solid'; 1210 this.shadow = true; 1211 // prop: shadowAngle 1212 // Shadow angle in degrees 1213 this.shadowAngle = 45; 1214 // prop: shadowOffset 1215 // Shadow offset from line in pixels 1216 this.shadowOffset = 1.25; 1217 // prop: shadowDepth 1218 // Number of times shadow is stroked, each stroke offset shadowOffset from the last. 1219 this.shadowDepth = 3; 1220 // prop: shadowAlpha 1221 // Alpha channel transparency of shadow. 0 = transparent. 1222 this.shadowAlpha = '0.1'; 1223 // prop: breakOnNull 1224 // Wether line segments should be be broken at null value. 1225 // False will join point on either side of line. 1226 this.breakOnNull = false;
1227 // prop: markerRenderer 1228 // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points, 1229 // see <$.jqplot.MarkerRenderer>. 1230 this.markerRenderer = $.jqplot.MarkerRenderer; 1231 // prop: markerOptions 1232 // renderer specific options to pass to the markerRenderer, 1233 // see <$.jqplot.MarkerRenderer>. 1234 this.markerOptions = {}; 1235 // prop: showLine 1236 // whether to actually draw the line or not. Series will still be renderered, even if no line is drawn. 1237 this.showLine = true; 1238 // prop: showMarker 1239 // whether or not to show the markers at the data points. 1240 this.showMarker = true; 1241 // prop: index 1242 // 0 based index of this series in the plot series array. 1243 this.index; 1244 // prop: fill 1245 // true or false, whether to fill under lines or in bars. 1246 // May not be implemented in all renderers. 1247 this.fill = false; 1248 // prop: fillColor 1249 // CSS color spec to use for fill under line. Defaults to line color. 1250 this.fillColor; 1251 // prop: fillAlpha 1252 // Alpha transparency to apply to the fill under the line. 1253 // Use this to adjust alpha separate from fill color. 1254 this.fillAlpha; 1255 // prop: fillAndStroke 1256 // If true will stroke the line (with color this.color) as well as fill under it. 1257 // Applies only when fill is true. 1258 this.fillAndStroke = false; 1259 // prop: disableStack 1260 // true to not stack this series with other series in the plot. 1261 // To render properly, non-stacked series must come after any stacked series 1262 // in the plot's data series array. So, the plot's data series array would look like: 1263 // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...] 1264 // disableStack will put a gap in the stacking order of series, and subsequent 1265 // stacked series will not fill down through the non-stacked series and will 1266 // most likely not stack properly on top of the non-stacked series. 1267 this.disableStack = false; 1268 // _stack is set by the Plot if the plot is a stacked chart. 1269 // will stack lines or bars on top of one another to build a "mountain" style chart. 1270 // May not be implemented in all renderers. 1271 this._stack = false; 1272 // prop: neighborThreshold 1273 // how close or far (in pixels) the cursor must be from a point marker to detect the point. 1274 this.neighborThreshold = 4; 1275 // prop: fillToZero 1276 // true will force bar and filled series to fill toward zero on the fill Axis. 1277 this.fillToZero = false; 1278 // prop: fillToValue 1279 // fill a filled series to this value on the fill axis. 1280 // Works in conjunction with fillToZero, so that must be true. 1281 this.fillToValue = 0; 1282 // prop: fillAxis 1283 // Either 'x' or 'y'. Which axis to fill the line toward if fillToZero is true. 1284 // 'y' means fill up/down to 0 on the y axis for this series. 1285 this.fillAxis = 'y'; 1286 // prop: useNegativeColors 1287 // true to color negative values differently in filled and bar charts. 1288 this.useNegativeColors = true; 1289 this._stackData = []; 1290 // _plotData accounts for stacking. If plots not stacked, _plotData and data are same. If 1291 // stacked, _plotData is accumulation of stacking data. 1292 this._plotData = []; 1293 // _plotValues hold the individual x and y values that will be plotted for this series. 1294 this._plotValues = {x:[], y:[]}; 1295 // statistics about the intervals between data points. Used for auto scaling. 1296 this._intervals = {x:{}, y:{}}; 1297 // data from the previous series, for stacked charts. 1298 this._prevPlotData = []; 1299 this._prevGridData = []; 1300 this._stackAxis = 'y'; 1301 this._primaryAxis = '_xaxis'; 1302 // give each series a canvas to draw on. This should allow for redrawing speedups. 1303 this.canvas = new $.jqplot.GenericCanvas(); 1304 this.shadowCanvas = new $.jqplot.GenericCanvas(); 1305 this.plugins = {}; 1306 // sum of y values in this series. 1307 this._sumy = 0; 1308 this._sumx = 0; 1309 this._type = ''; 1310 } 1311 1312 Series.prototype = new $.jqplot.ElemContainer(); 1313 Series.prototype.constructor = Series; 1314 1315 Series.prototype.init = function(index, gridbw, plot) { 1316 // weed out any null values in the data. 1317 this.index = index; 1318 this.gridBorderWidth = gridbw; 1319 var d = this.data; 1320 var temp = [], i, l; 1321 for (i=0, l=d.length; i<l; i++) { 1322 if (! this.breakOnNull) { 1323 if (d[i] == null || d[i][0] == null || d[i][1] == null) { 1324 continue; 1325 } 1326 else { 1327 temp.push(d[i]); 1328 } 1329 } 1330 else {
1331 // TODO: figure out what to do with null values 1332 // probably involve keeping nulls in data array 1333 // and then updating renderers to break line 1334 // when it hits null value. 1335 // For now, just keep value. 1336 temp.push(d[i]); 1337 } 1338 } 1339 this.data = temp; 1340 1341 // parse the renderer options and apply default colors if not provided 1342 // Set color even if not shown, so series don't change colors when other 1343 // series on plot shown/hidden. 1344 if (!this.color) { 1345 this.color = plot.colorGenerator.get(this.index); 1346 } 1347 if (!this.negativeColor) { 1348 this.negativeColor = plot.negativeColorGenerator.get(this.index); 1349 } 1350 1351 1352 if (!this.fillColor) { 1353 this.fillColor = this.color; 1354 } 1355 if (this.fillAlpha) { 1356 var comp = $.jqplot.normalize2rgb(this.fillColor); 1357 var comp = $.jqplot.getColorComponents(comp); 1358 this.fillColor = 'rgba('+comp[0]+','+comp[1]+','+comp[2]+','+this.fillAlpha+')'; 1359 } 1360 if ($.isFunction(this.renderer)) { 1361 this.renderer = new this.renderer(); 1362 } 1363 this.renderer.init.call(this, this.rendererOptions, plot); 1364 this.markerRenderer = new this.markerRenderer(); 1365 if (!this.markerOptions.color) { 1366 this.markerOptions.color = this.color; 1367 } 1368 if (this.markerOptions.show == null) { 1369 this.markerOptions.show = this.showMarker; 1370 } 1371 this.showMarker = this.markerOptions.show; 1372 // the markerRenderer is called within its own scope, don't want to overwrite series options!! 1373 this.markerRenderer.init(this.markerOptions); 1374 }; 1375 1376 // data - optional data point array to draw using this series renderer 1377 // gridData - optional grid data point array to draw using this series renderer 1378 // stackData - array of cumulative data for stacked plots. 1379 Series.prototype.draw = function(sctx, opts, plot) { 1380 var options = (opts == undefined) ? {} : opts; 1381 sctx = (sctx == undefined) ? this.canvas._ctx : sctx; 1382 1383 var j, data, gridData; 1384 1385 // hooks get called even if series not shown 1386 // we don't clear canvas here, it would wipe out all other series as well. 1387 for (j=0; j<$.jqplot.preDrawSeriesHooks.length; j++) { 1388 $.jqplot.preDrawSeriesHooks[j].call(this, sctx, options); 1389 } 1390 if (this.show) { 1391 this.renderer.setGridData.call(this, plot); 1392 if (!options.preventJqPlotSeriesDrawTrigger) { 1393 $(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]); 1394 } 1395 data = []; 1396 if (options.data) { 1397 data = options.data; 1398 } 1399 else if (!this._stack) { 1400 data = this.data; 1401 } 1402 else { 1403 data = this._plotData; 1404 } 1405 gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot); 1406 1407 if (this._type === 'line' && this.renderer.smooth && this.renderer._smoothedData.length) { 1408 gridData = this.renderer._smoothedData; 1409 } 1410 1411 this.renderer.draw.call(this, sctx, gridData, options, plot); 1412 } 1413 1414 for (j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) { 1415 $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options, plot); 1416 } 1417 1418 sctx = opts = plot = j = data = gridData = null; 1419 }; 1420 1421 Series.prototype.drawShadow = function(sctx, opts, plot) { 1422 var options = (opts == undefined) ? {} : opts; 1423 sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx; 1424 1425 var j, data, gridData; 1426 1427 // hooks get called even if series not shown 1428 // we don't clear canvas here, it would wipe out all other series as well. 1429 for (j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) { 1430 $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options); 1431 } 1432 if (this.shadow) { 1433 this.renderer.setGridData.call(this, plot); 1434 1435 data = []; 1436 if (options.data) { 1437 data = options.data; 1438 } 1439 else if (!this._stack) { 1440 data = this.data; 1441 } 1442 else { 1443 data = this._plotData; 1444 } 1445 gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot); 1446 1447 this.renderer.drawShadow.call(this, sctx, gridData, options, plot); 1448 } 1449 1450 for (j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) { 1451 $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options); 1452 } 1453 1454 sctx = opts = plot = j = data = gridData = null; 1455 1456 }; 1457 1458 // toggles series display on plot, e.g. show/hide series 1459 Series.prototype.toggleDisplay = function(ev, callback) { 1460 var s, speed; 1461 if (ev.data.series) { 1462 s = ev.data.series; 1463 } 1464 else { 1465 s = this; 1466 } 1467 1468 if (ev.data.speed) { 1469 speed = ev.data.speed; 1470 } 1471 if (speed) { 1472 // this can be tricky because series may not have a canvas element if replotting. 1473 if (s.canvas._elem.is(':hidden') || !s.show) { 1474 s.show = true; 1475 1476 s.canvas._elem.removeClass('jqplot-series-hidden'); 1477 if (s.shadowCanvas._elem) { 1478 s.shadowCanvas._elem.fadeIn(speed); 1479 } 1480 s.canvas._elem.fadeIn(speed, callback); 1481 s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeIn(speed); 1482 } 1483 else { 1484 s.show = false;
1485 1486 s.canvas._elem.addClass('jqplot-series-hidden'); 1487 if (s.shadowCanvas._elem) { 1488 s.shadowCanvas._elem.fadeOut(speed); 1489 } 1490 s.canvas._elem.fadeOut(speed, callback); 1491 s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeOut(speed); 1492 } 1493 } 1494 else { 1495 // this can be tricky because series may not have a canvas element if replotting. 1496 if (s.canvas._elem.is(':hidden') || !s.show) { 1497 s.show = true; 1498 1499 s.canvas._elem.removeClass('jqplot-series-hidden'); 1500 if (s.shadowCanvas._elem) { 1501 s.shadowCanvas._elem.show(); 1502 } 1503 s.canvas._elem.show(0, callback); 1504 s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).show(); 1505 } 1506 else { 1507 s.show = false; 1508 1509 s.canvas._elem.addClass('jqplot-series-hidden'); 1510 if (s.shadowCanvas._elem) { 1511 s.shadowCanvas._elem.hide(); 1512 } 1513 s.canvas._elem.hide(0, callback); 1514 s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide(); 1515 } 1516 } 1517 }; 1518 1519 1520 1521 /** 1522 * Class: Grid 1523 * 1524 * Object representing the grid on which the plot is drawn. The grid in this 1525 * context is the area bounded by the axes, the area which will contain the series. 1526 * Note, the series are drawn on their own canvas. 1527 * The Grid object cannot be instantiated directly, but is created by the Plot object. 1528 * Grid properties can be set or overridden by the options passed in from the user. 1529 */ 1530 function Grid() { 1531 $.jqplot.ElemContainer.call(this); 1532 // Group: Properties 1533 1534 // prop: drawGridlines 1535 // whether to draw the gridlines on the plot. 1536 this.drawGridlines = true; 1537 // prop: gridLineColor 1538 // color of the grid lines. 1539 this.gridLineColor = '#cccccc'; 1540 // prop: gridLineWidth 1541 // width of the grid lines. 1542 this.gridLineWidth = 1.0; 1543 // prop: background 1544 // css spec for the background color. 1545 this.background = '#fffdf6'; 1546 // prop: borderColor 1547 // css spec for the color of the grid border. 1548 this.borderColor = '#999999'; 1549 // prop: borderWidth 1550 // width of the border in pixels. 1551 this.borderWidth = 2.0; 1552 // prop: drawBorder 1553 // True to draw border around grid. 1554 this.drawBorder = true; 1555 // prop: shadow 1556 // whether to show a shadow behind the grid. 1557 this.shadow = true; 1558 // prop: shadowAngle 1559 // shadow angle in degrees 1560 this.shadowAngle = 45; 1561 // prop: shadowOffset 1562 // Offset of each shadow stroke from the border in pixels 1563 this.shadowOffset = 1.5; 1564 // prop: shadowWidth 1565 // width of the stoke for the shadow 1566 this.shadowWidth = 3; 1567 // prop: shadowDepth 1568 // Number of times shadow is stroked, each stroke offset shadowOffset from the last. 1569 this.shadowDepth = 3; 1570 // prop: shadowColor 1571 // an optional css color spec for the shadow in 'rgba(n, n, n, n)' form 1572 this.shadowColor = null; 1573 // prop: shadowAlpha 1574 // Alpha channel transparency of shadow. 0 = transparent. 1575 this.shadowAlpha = '0.07'; 1576 this._left; 1577 this._top; 1578 this._right; 1579 this._bottom; 1580 this._width; 1581 this._height; 1582 this._axes = []; 1583 // prop: renderer 1584 // Instance of a renderer which will actually render the grid, 1585 // see <$.jqplot.CanvasGridRenderer>. 1586 this.renderer = $.jqplot.CanvasGridRenderer; 1587 // prop: rendererOptions 1588 // Options to pass on to the renderer, 1589 // see <$.jqplot.CanvasGridRenderer>. 1590 this.rendererOptions = {}; 1591 this._offsets = {top:null, bottom:null, left:null, right:null}; 1592 } 1593 1594 Grid.prototype = new $.jqplot.ElemContainer(); 1595 Grid.prototype.constructor = Grid; 1596 1597 Grid.prototype.init = function() { 1598 if ($.isFunction(this.renderer)) { 1599 this.renderer = new this.renderer(); 1600 } 1601 this.renderer.init.call(this, this.rendererOptions); 1602 }; 1603 1604 Grid.prototype.createElement = function(offsets,plot) { 1605 this._offsets = offsets; 1606 return this.renderer.createElement.call(this, plot); 1607 }; 1608 1609 Grid.prototype.draw = function() { 1610 this.renderer.draw.call(this); 1611 }; 1612 1613 $.jqplot.GenericCanvas = function() { 1614 $.jqplot.ElemContainer.call(this); 1615 this._ctx; 1616 }; 1617 1618 $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer(); 1619 $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas; 1620 1621 $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions, plot) { 1622 this._offsets = offsets; 1623 var klass = 'jqplot'; 1624 if (clss != undefined) { 1625 klass = clss; 1626 } 1627 var elem; 1628 1629 elem = plot.canvasManager.getCanvas(); 1630 1631 // if new plotDimensions supplied, use them. 1632 if (plotDimensions != null) { 1633 this._plotDimensions = plotDimensions; 1634 } 1635 1636 elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right; 1637 elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom; 1638 this._elem = $(elem); 1639 this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top }); 1640 1641 this._elem.addClass(klass); 1642 1643 elem = plot.canvasManager.initCanvas(elem); 1644 1645 elem = null; 1646 return this._elem; 1647 }; 1648 1649 $.jqplot.GenericCanvas.prototype.setContext = function() { 1650 this._ctx = this._elem.get(0).getContext("2d"); 1651 return this._ctx; 1652 }; 1653 1654 // Memory Leaks patch 1655 $.jqplot.GenericCanvas.prototype.resetCanvas = function() { 1656 if (this._elem) { 1657 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 1658 window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); 1659 } 1660 1661 //this._elem.remove(); 1662 this._elem.emptyForce(); 1663 } 1664 1665 this._ctx = null; 1666 }; 1667 1668 $.jqplot.HooksManager = function () { 1669 this.hooks =[]; 1670 this.args = []; 1671 }; 1672 1673 $.jqplot.HooksManager.prototype.addOnce = function(fn, args) { 1674 args = args || []; 1675 var havehook = false;
1676 for (var i=0, l=this.hooks.length; i<l; i++) { 1677 if (this.hooks[i] == fn) { 1678 havehook = true; 1679 } 1680 } 1681 if (!havehook) { 1682 this.hooks.push(fn); 1683 this.args.push(args); 1684 } 1685 }; 1686 1687 $.jqplot.HooksManager.prototype.add = function(fn, args) { 1688 args = args || []; 1689 this.hooks.push(fn); 1690 this.args.push(args); 1691 }; 1692 1693 $.jqplot.EventListenerManager = function () { 1694 this.hooks =[]; 1695 }; 1696 1697 $.jqplot.EventListenerManager.prototype.addOnce = function(ev, fn) { 1698 var havehook = false, h, i; 1699 for (var i=0, l=this.hooks.length; i<l; i++) { 1700 h = this.hooks[i]; 1701 if (h[0] == ev && h[1] == fn) { 1702 havehook = true; 1703 } 1704 } 1705 if (!havehook) { 1706 this.hooks.push([ev, fn]); 1707 } 1708 }; 1709 1710 $.jqplot.EventListenerManager.prototype.add = function(ev, fn) { 1711 this.hooks.push([ev, fn]); 1712 }; 1713 1714 1715 var _axisNames = ['yMidAxis', 'xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis']; 1716 1717 /** 1718 * Class: jqPlot 1719 * Plot object returned by call to $.jqplot. Handles parsing user options, 1720 * creating sub objects (Axes, legend, title, series) and rendering the plot. 1721 */ 1722 function jqPlot() { 1723 // Group: Properties 1724 // These properties are specified at the top of the options object 1725 // like so: 1726 // > { 1727 // > axesDefaults:{min:0}, 1728 // > series:[{color:'#6633dd'}], 1729 // > title: 'A Plot' 1730 // > } 1731 // 1732 1733 // prop: animate 1734 // True to animate the series on initial plot draw (renderer dependent). 1735 // Actual animation functionality must be supported in the renderer. 1736 this.animate = false; 1737 // prop: animateReplot 1738 // True to animate series after a call to the replot() method. 1739 // Use with caution! Replots can happen very frequently under 1740 // certain circumstances (e.g. resizing, dragging points) and 1741 // animation in these situations can cause problems. 1742 this.animateReplot = false; 1743 // prop: axes 1744 // up to 4 axes are supported, each with its own options, 1745 // See <Axis> for axis specific options. 1746 this.axes = {xaxis: new Axis('xaxis'), yaxis: new Axis('yaxis'), x2axis: new Axis('x2axis'), y2axis: new Axis('y2axis'), y3axis: new Axis('y3axis'), y4axis: new Axis('y4axis'), y5axis: new Axis('y5axis'), y6axis: new Axis('y6axis'), y7axis: new Axis('y7axis'), y8axis: new Axis('y8axis'), y9axis: new Axis('y9axis'), yMidAxis: new Axis('yMidAxis')}; 1747 this.baseCanvas = new $.jqplot.GenericCanvas(); 1748 // true to intercept right click events and fire a 'jqplotRightClick' event. 1749 // this will also block the context menu. 1750 this.captureRightClick = false; 1751 // prop: data 1752 // user's data. Data should *NOT* be specified in the options object, 1753 // but be passed in as the second argument to the $.jqplot() function. 1754 // The data property is described here soley for reference. 1755 // The data should be in the form of an array of 2D or 1D arrays like 1756 // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ]. 1757 this.data = []; 1758 // prop: dataRenderer 1759 // A callable which can be used to preprocess data passed into the plot. 1760 // Will be called with 3 arguments: the plot data, a reference to the plot, 1761 // and the value of dataRendererOptions. 1762 this.dataRenderer; 1763 // prop: dataRendererOptions 1764 // Options that will be passed to the dataRenderer. 1765 // Can be of any type. 1766 this.dataRendererOptions; 1767 this.defaults = { 1768 // prop: axesDefaults 1769 // default options that will be applied to all axes. 1770 // see <Axis> for axes options. 1771 axesDefaults: {}, 1772 axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}, yMidAxis:{}}, 1773 // prop: seriesDefaults 1774 // default options that will be applied to all series. 1775 // see <Series> for series options. 1776 seriesDefaults: {}, 1777 series:[] 1778 }; 1779 // prop: defaultAxisStart 1780 // 1-D data series are internally converted into 2-D [x,y] data point arrays 1781 // by jqPlot. This is the default starting value for the missing x or y value. 1782 // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...]) 1783 // starting at this value. 1784 this.defaultAxisStart = 1; 1785 // this.doCustomEventBinding = true; 1786 // prop: drawIfHidden 1787 // True to execute the draw method even if the plot target is hidden. 1788 // Generally, this should be false. Most plot elements will not be sized/ 1789 // positioned correclty if renderered into a hidden container. To render into 1790 // a hidden container, call the replot method when the container is shown.
1791 this.drawIfHidden = false; 1792 this.eventCanvas = new $.jqplot.GenericCanvas(); 1793 // prop: fillBetween 1794 // Fill between 2 line series in a plot. 1795 // Options object: 1796 // { 1797 // series1: first index (0 based) of series in fill 1798 // series2: second index (0 based) of series in fill 1799 // color: color of fill [default fillColor of series1] 1800 // baseSeries: fill will be drawn below this series (0 based index) 1801 // fill: false to turn off fill [default true]. 1802 // } 1803 this.fillBetween = { 1804 series1: null, 1805 series2: null, 1806 color: null, 1807 baseSeries: 0, 1808 fill: true 1809 }; 1810 // prop; fontFamily 1811 // css spec for the font-family attribute. Default for the entire plot. 1812 this.fontFamily; 1813 // prop: fontSize 1814 // css spec for the font-size attribute. Default for the entire plot. 1815 this.fontSize; 1816 // prop: grid 1817 // See <Grid> for grid specific options. 1818 this.grid = new Grid(); 1819 // prop: legend 1820 // see <$.jqplot.TableLegendRenderer> 1821 this.legend = new Legend(); 1822 // prop: noDataIndicator 1823 // Options to set up a mock plot with a data loading indicator if no data is specified. 1824 this.noDataIndicator = { 1825 show: false, 1826 indicator: 'Loading Data...', 1827 axes: { 1828 xaxis: { 1829 min: 0, 1830 max: 10, 1831 tickInterval: 2, 1832 show: true 1833 }, 1834 yaxis: { 1835 min: 0, 1836 max: 12, 1837 tickInterval: 3, 1838 show: true 1839 } 1840 } 1841 }; 1842 // prop: negativeSeriesColors 1843 // colors to use for portions of the line below zero. 1844 this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors; 1845 // container to hold all of the merged options. Convienence for plugins. 1846 this.options = {}; 1847 this.previousSeriesStack = []; 1848 // Namespace to hold plugins. Generally non-renderer plugins add themselves to here. 1849 this.plugins = {}; 1850 // prop: series 1851 // Array of series object options. 1852 // see <Series> for series specific options. 1853 this.series = []; 1854 // array of series indices. Keep track of order 1855 // which series canvases are displayed, lowest 1856 // to highest, back to front. 1857 this.seriesStack = []; 1858 // prop: seriesColors 1859 // Ann array of CSS color specifications that will be applied, in order, 1860 // to the series in the plot. Colors will wrap around so, if their 1861 // are more series than colors, colors will be reused starting at the 1862 // beginning. For pie charts, this specifies the colors of the slices. 1863 this.seriesColors = $.jqplot.config.defaultColors; 1864 // prop: sortData 1865 // false to not sort the data passed in by the user. 1866 // Many bar, stacked and other graphs as well as many plugins depend on 1867 // having sorted data. 1868 this.sortData = true; 1869 // prop: stackSeries 1870 // true or false, creates a stack or "mountain" plot. 1871 // Not all series renderers may implement this option. 1872 this.stackSeries = false; 1873 // a shortcut for axis syncTicks options. Not implemented yet. 1874 this.syncXTicks = true; 1875 // a shortcut for axis syncTicks options. Not implemented yet. 1876 this.syncYTicks = true; 1877 // the jquery object for the dom target. 1878 this.target = null; 1879 // The id of the dom element to render the plot into 1880 this.targetId = null; 1881 // prop textColor 1882 // css spec for the css color attribute. Default for the entire plot. 1883 this.textColor; 1884 // prop: title 1885 // Title object. See <Title> for specific options. As a shortcut, you 1886 // can specify the title option as just a string like: title: 'My Plot' 1887 // and this will create a new title object with the specified text. 1888 this.title = new Title(); 1889 // Count how many times the draw method has been called while the plot is visible. 1890 // Mostly used to test if plot has never been dran (=0), has been successfully drawn 1891 // into a visible container once (=1) or draw more than once into a visible container. 1892 // Can use this in tests to see if plot has been visibly drawn at least one time. 1893 // After plot has been visibly drawn once, it generally doesn't need redrawing if its 1894 // container is hidden and shown. 1895 this._drawCount = 0; 1896 // sum of y values for all series in plot. 1897 // used in mekko chart. 1898 this._sumy = 0; 1899 this._sumx = 0; 1900 // array to hold the cumulative stacked series data. 1901 // used to ajust the individual series data, which won't have access to other 1902 // series data. 1903 this._stackData = []; 1904 // array that holds the data to be plotted. This will be the series data 1905 // merged with the the appropriate data from _stackData according to the stackAxis. 1906 this._plotData = []; 1907 this._width = null; 1908 this._height = null; 1909 this._plotDimensions = {height:null, width:null}; 1910 this._gridPadding = {top:null, right:null, bottom:null, left:null}; 1911 this._defaultGridPadding = {top:10, right:10, bottom:23, left:10}; 1912 1913 this._addDomReference = $.jqplot.config.addDomReference; 1914 1915 this.preInitHooks = new $.jqplot.HooksManager(); 1916 this.postInitHooks = new $.jqplot.HooksManager(); 1917 this.preParseOptionsHooks = new $.jqplot.HooksManager(); 1918 this.postParseOptionsHooks = new $.jqplot.HooksManager(); 1919 this.preDrawHooks = new $.jqplot.HooksManager(); 1920 this.postDrawHooks = new $.jqplot.HooksManager(); 1921 this.preDrawSeriesHooks = new $.jqplot.HooksManager(); 1922 this.postDrawSeriesHooks = new $.jqplot.HooksManager(); 1923 this.preDrawLegendHooks = new $.jqplot.HooksManager(); 1924 this.addLegendRowHooks = new $.jqplot.HooksManager(); 1925 this.preSeriesInitHooks = new $.jqplot.HooksManager(); 1926 this.postSeriesInitHooks = new $.jqplot.HooksManager(); 1927 this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager(); 1928 this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager();
1929 this.eventListenerHooks = new $.jqplot.EventListenerManager(); 1930 this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager(); 1931 this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager(); 1932 1933 this.colorGenerator = new $.jqplot.ColorGenerator(); 1934 this.negativeColorGenerator = new $.jqplot.ColorGenerator(); 1935 1936 this.canvasManager = new $.jqplot.CanvasManager(); 1937 1938 this.themeEngine = new $.jqplot.ThemeEngine(); 1939 1940 var seriesColorsIndex = 0; 1941 1942 // Group: methods 1943 // 1944 // method: init 1945 // sets the plot target, checks data and applies user 1946 // options to plot. 1947 this.init = function(target, data, options) { 1948 options = options || {}; 1949 for (var i=0; i<$.jqplot.preInitHooks.length; i++) { 1950 $.jqplot.preInitHooks[i].call(this, target, data, options); 1951 } 1952 1953 for (var i=0; i<this.preInitHooks.hooks.length; i++) { 1954 this.preInitHooks.hooks[i].call(this, target, data, options); 1955 } 1956 1957 this.targetId = '#'+target; 1958 this.target = $('#'+target); 1959 1960 ////// 1961 // Add a reference to plot 1962 ////// 1963 if (this._addDomReference) { 1964 this.target.data('jqplot', this); 1965 } 1966 // remove any error class that may be stuck on target. 1967 this.target.removeClass('jqplot-error'); 1968 if (!this.target.get(0)) { 1969 throw new Error("No plot target specified"); 1970 } 1971 1972 // make sure the target is positioned by some means and set css 1973 if (this.target.css('position') == 'static') { 1974 this.target.css('position', 'relative'); 1975 } 1976 if (!this.target.hasClass('jqplot-target')) { 1977 this.target.addClass('jqplot-target'); 1978 } 1979 1980 // if no height or width specified, use a default. 1981 if (!this.target.height()) { 1982 var h; 1983 if (options && options.height) { 1984 h = parseInt(options.height, 10); 1985 } 1986 else if (this.target.attr('data-height')) { 1987 h = parseInt(this.target.attr('data-height'), 10); 1988 } 1989 else { 1990 h = parseInt($.jqplot.config.defaultHeight, 10); 1991 } 1992 this._height = h; 1993 this.target.css('height', h+'px'); 1994 } 1995 else { 1996 this._height = h = this.target.height(); 1997 } 1998 if (!this.target.width()) { 1999 var w; 2000 if (options && options.width) { 2001 w = parseInt(options.width, 10); 2002 } 2003 else if (this.target.attr('data-width')) { 2004 w = parseInt(this.target.attr('data-width'), 10); 2005 } 2006 else { 2007 w = parseInt($.jqplot.config.defaultWidth, 10); 2008 } 2009 this._width = w; 2010 this.target.css('width', w+'px'); 2011 } 2012 else { 2013 this._width = w = this.target.width(); 2014 } 2015 2016 for (var i=0, l=_axisNames.length; i<l; i++) { 2017 this.axes[_axisNames[i]] = new Axis(_axisNames[i]); 2018 } 2019 2020 this._plotDimensions.height = this._height; 2021 this._plotDimensions.width = this._width; 2022 this.grid._plotDimensions = this._plotDimensions; 2023 this.title._plotDimensions = this._plotDimensions; 2024 this.baseCanvas._plotDimensions = this._plotDimensions; 2025 this.eventCanvas._plotDimensions = this._plotDimensions; 2026 this.legend._plotDimensions = this._plotDimensions; 2027 if (this._height <=0 || this._width <=0 || !this._height || !this._width) { 2028 throw new Error("Canvas dimension not set"); 2029 } 2030 2031 if (options.dataRenderer && $.isFunction(options.dataRenderer)) { 2032 if (options.dataRendererOptions) { 2033 this.dataRendererOptions = options.dataRendererOptions; 2034 } 2035 this.dataRenderer = options.dataRenderer; 2036 data = this.dataRenderer(data, this, this.dataRendererOptions); 2037 } 2038 2039 if (options.noDataIndicator && $.isPlainObject(options.noDataIndicator)) { 2040 $.extend(true, this.noDataIndicator, options.noDataIndicator); 2041 } 2042 2043 if (data == null || $.isArray(data) == false || data.length == 0 || $.isArray(data[0]) == false || data[0].length == 0) { 2044 2045 if (this.noDataIndicator.show == false) { 2046 throw new Error("No data specified"); 2047 } 2048 2049 else { 2050 // have to be descructive here in order for plot to not try and render series. 2051 // This means that $.jqplot() will have to be called again when there is data. 2052 //delete options.series; 2053 2054 for (var ax in this.noDataIndicator.axes) { 2055 for (var prop in this.noDataIndicator.axes[ax]) { 2056 this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop]; 2057 } 2058 } 2059 2060 this.postDrawHooks.add(function() { 2061 var eh = this.eventCanvas.getHeight(); 2062 var ew = this.eventCanvas.getWidth(); 2063 var temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>'); 2064 this.target.append(temp); 2065 temp.height(eh); 2066 temp.width(ew); 2067 temp.css('top', this.eventCanvas._offsets.top); 2068 temp.css('left', this.eventCanvas._offsets.left); 2069 2070 var temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>'); 2071 temp.append(temp2); 2072 temp2.html(this.noDataIndicator.indicator); 2073 var th = temp2.height(); 2074 var tw = temp2.width(); 2075 temp2.height(th); 2076 temp2.width(tw); 2077 temp2.css('top', (eh - th)/2 + 'px'); 2078 }); 2079 2080 } 2081 } 2082 2083 // make a copy of the data 2084 this.data = $.extend(true, [], data); 2085 2086 this.parseOptions(options); 2087 2088 if (this.textColor) { 2089 this.target.css('color', this.textColor); 2090 } 2091 if (this.fontFamily) { 2092 this.target.css('font-family', this.fontFamily); 2093 } 2094 if (this.fontSize) { 2095 this.target.css('font-size', this.fontSize); 2096 } 2097
2098 this.title.init(); 2099 this.legend.init(); 2100 this._sumy = 0; 2101 this._sumx = 0; 2102 this.computePlotData(); 2103 for (var i=0; i<this.series.length; i++) { 2104 // set default stacking order for series canvases 2105 this.seriesStack.push(i); 2106 this.previousSeriesStack.push(i); 2107 this.series[i].shadowCanvas._plotDimensions = this._plotDimensions; 2108 this.series[i].canvas._plotDimensions = this._plotDimensions; 2109 for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) { 2110 $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2111 } 2112 for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) { 2113 this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2114 } 2115 // this.populatePlotData(this.series[i], i); 2116 this.series[i]._plotDimensions = this._plotDimensions; 2117 this.series[i].init(i, this.grid.borderWidth, this); 2118 for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) { 2119 $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2120 } 2121 for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) { 2122 this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2123 } 2124 this._sumy += this.series[i]._sumy; 2125 this._sumx += this.series[i]._sumx; 2126 } 2127 2128 var name, 2129 axis; 2130 for (var i=0, l=_axisNames.length; i<l; i++) { 2131 name = _axisNames[i]; 2132 axis = this.axes[name]; 2133 axis._plotDimensions = this._plotDimensions; 2134 axis.init(); 2135 if (this.axes[name].borderColor == null) { 2136 if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) { 2137 axis.borderColor = axis._series[0].color; 2138 } 2139 else { 2140 axis.borderColor = this.grid.borderColor; 2141 } 2142 } 2143 } 2144 2145 if (this.sortData) { 2146 sortData(this.series); 2147 } 2148 this.grid.init(); 2149 this.grid._axes = this.axes; 2150 2151 this.legend._series = this.series; 2152 2153 for (var i=0; i<$.jqplot.postInitHooks.length; i++) { 2154 $.jqplot.postInitHooks[i].call(this, target, this.data, options); 2155 } 2156 2157 for (var i=0; i<this.postInitHooks.hooks.length; i++) { 2158 this.postInitHooks.hooks[i].call(this, target, this.data, options); 2159 } 2160 }; 2161 2162 // method: resetAxesScale 2163 // Reset the specified axes min, max, numberTicks and tickInterval properties to null 2164 // or reset these properties on all axes if no list of axes is provided. 2165 // 2166 // Parameters: 2167 // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset. 2168 this.resetAxesScale = function(axes, options) { 2169 var opts = options || {}; 2170 var ax = axes || this.axes; 2171 if (ax === true) { 2172 ax = this.axes; 2173 } 2174 if ($.isArray(ax)) { 2175 for (var i = 0; i < ax.length; i++) { 2176 this.axes[ax[i]].resetScale(opts[ax[i]]); 2177 } 2178 } 2179 else if (typeof(ax) === 'object') { 2180 for (var name in ax) { 2181 this.axes[name].resetScale(opts[name]); 2182 } 2183 } 2184 }; 2185 // method: reInitialize 2186 // reinitialize plot for replotting. 2187 // not called directly. 2188 this.reInitialize = function (data, opts) { 2189 // Plot should be visible and have a height and width. 2190 // If plot doesn't have height and width for some 2191 // reason, set it by other means. Plot must not have 2192 // a display:none attribute, however. 2193 2194 var options = $.extend(true, {}, this.options, opts); 2195 2196 var target = this.targetId.substr(1); 2197 var tdata = (data == null) ? this.data : data; 2198 2199 for (var i=0; i<$.jqplot.preInitHooks.length; i++) { 2200 $.jqplot.preInitHooks[i].call(this, target, tdata, options); 2201 } 2202 2203 for (var i=0; i<this.preInitHooks.hooks.length; i++) { 2204 this.preInitHooks.hooks[i].call(this, target, tdata, options); 2205 } 2206 2207 this._height = this.target.height(); 2208 this._width = this.target.width(); 2209 2210 if (this._height <=0 || this._width <=0 || !this._height || !this._width) { 2211 throw new Error("Target dimension not set"); 2212 } 2213 2214 this._plotDimensions.height = this._height; 2215 this._plotDimensions.width = this._width; 2216 this.grid._plotDimensions = this._plotDimensions; 2217 this.title._plotDimensions = this._plotDimensions; 2218 this.baseCanvas._plotDimensions = this._plotDimensions; 2219 this.eventCanvas._plotDimensions = this._plotDimensions; 2220 this.legend._plotDimensions = this._plotDimensions; 2221 2222 var name, 2223 t, 2224 j, 2225 axis; 2226 2227 for (var i=0, l=_axisNames.length; i<l; i++) { 2228 name = _axisNames[i]; 2229 axis = this.axes[name]; 2230 2231 // Memory Leaks patch : clear ticks elements 2232 t = axis._ticks; 2233 for (var j = 0, tlen = t.length; j < tlen; j++) { 2234 var el = t[j]._elem; 2235 if (el) { 2236 // if canvas renderer 2237 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 2238 window.G_vmlCanvasManager.uninitElement(el.get(0)); 2239 } 2240 el.emptyForce(); 2241 el = null; 2242 t._elem = null; 2243 } 2244 } 2245 t = null; 2246 2247 delete axis.ticks; 2248 delete axis._ticks; 2249 this.axes[name] = new Axis(name); 2250 this.axes[name]._plotWidth = this._width; 2251 this.axes[name]._plotHeight = this._height; 2252 } 2253 2254 if (data) { 2255 if (options.dataRenderer && $.isFunction(options.dataRenderer)) { 2256 if (options.dataRendererOptions) { 2257 this.dataRendererOptions = options.dataRendererOptions; 2258 } 2259 this.dataRenderer = options.dataRenderer; 2260 data = this.dataRenderer(data, this, this.dataRendererOptions); 2261 } 2262 2263 // make a copy of the data 2264 this.data = $.extend(true, [], data); 2265 } 2266 2267 if (opts) { 2268 this.parseOptions(options); 2269 } 2270 2271 this.title._plotWidth = this._width; 2272 2273 if (this.textColor) { 2274 this.target.css('color', this.textColor); 2275 } 2276 if (this.fontFamily) { 2277 this.target.css('font-family', this.fontFamily); 2278 } 2279 if (this.fontSize) { 2280 this.target.css('font-size', this.fontSize); 2281 } 2282
2283 this.title.init(); 2284 this.legend.init(); 2285 this._sumy = 0; 2286 this._sumx = 0; 2287 2288 this.seriesStack = []; 2289 this.previousSeriesStack = []; 2290 2291 this.computePlotData(); 2292 for (var i=0, l=this.series.length; i<l; i++) { 2293 // set default stacking order for series canvases 2294 this.seriesStack.push(i); 2295 this.previousSeriesStack.push(i); 2296 this.series[i].shadowCanvas._plotDimensions = this._plotDimensions; 2297 this.series[i].canvas._plotDimensions = this._plotDimensions; 2298 for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) { 2299 $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2300 } 2301 for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) { 2302 this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2303 } 2304 // this.populatePlotData(this.series[i], i); 2305 this.series[i]._plotDimensions = this._plotDimensions; 2306 this.series[i].init(i, this.grid.borderWidth, this); 2307 for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) { 2308 $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2309 } 2310 for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) { 2311 this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2312 } 2313 this._sumy += this.series[i]._sumy; 2314 this._sumx += this.series[i]._sumx; 2315 } 2316 2317 for (var i=0, l=_axisNames.length; i<l; i++) { 2318 name = _axisNames[i]; 2319 axis = this.axes[name]; 2320 2321 axis._plotDimensions = this._plotDimensions; 2322 axis.init(); 2323 if (axis.borderColor == null) { 2324 if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) { 2325 axis.borderColor = axis._series[0].color; 2326 } 2327 else { 2328 axis.borderColor = this.grid.borderColor; 2329 } 2330 } 2331 } 2332 2333 if (this.sortData) { 2334 sortData(this.series); 2335 } 2336 this.grid.init(); 2337 this.grid._axes = this.axes; 2338 2339 this.legend._series = this.series; 2340 2341 for (var i=0, l=$.jqplot.postInitHooks.length; i<l; i++) { 2342 $.jqplot.postInitHooks[i].call(this, target, this.data, options); 2343 } 2344 2345 for (var i=0, l=this.postInitHooks.hooks.length; i<l; i++) { 2346 this.postInitHooks.hooks[i].call(this, target, this.data, options); 2347 } 2348 }; 2349 2350 2351 2352 // method: quickInit 2353 // 2354 // Quick reinitialization plot for replotting. 2355 // Does not parse options ore recreate axes and series. 2356 // not called directly. 2357 this.quickInit = function () { 2358 // Plot should be visible and have a height and width. 2359 // If plot doesn't have height and width for some 2360 // reason, set it by other means. Plot must not have 2361 // a display:none attribute, however. 2362 2363 this._height = this.target.height(); 2364 this._width = this.target.width(); 2365 2366 if (this._height <=0 || this._width <=0 || !this._height || !this._width) { 2367 throw new Error("Target dimension not set"); 2368 } 2369 2370 this._plotDimensions.height = this._height; 2371 this._plotDimensions.width = this._width; 2372 this.grid._plotDimensions = this._plotDimensions; 2373 this.title._plotDimensions = this._plotDimensions; 2374 this.baseCanvas._plotDimensions = this._plotDimensions; 2375 this.eventCanvas._plotDimensions = this._plotDimensions; 2376 this.legend._plotDimensions = this._plotDimensions; 2377 2378 for (var n in this.axes) { 2379 this.axes[n]._plotWidth = this._width; 2380 this.axes[n]._plotHeight = this._height; 2381 } 2382 2383 this.title._plotWidth = this._width; 2384 2385 if (this.textColor) { 2386 this.target.css('color', this.textColor); 2387 } 2388 if (this.fontFamily) { 2389 this.target.css('font-family', this.fontFamily); 2390 } 2391 if (this.fontSize) { 2392 this.target.css('font-size', this.fontSize); 2393 } 2394 2395 this._sumy = 0; 2396 this._sumx = 0; 2397 this.computePlotData(); 2398 for (var i=0; i<this.series.length; i++) { 2399 // this.populatePlotData(this.series[i], i); 2400 if (this.series[i]._type === 'line' && this.series[i].renderer.bands.show) { 2401 this.series[i].renderer.initBands.call(this.series[i], this.series[i].renderer.options, this); 2402 } 2403 this.series[i]._plotDimensions = this._plotDimensions; 2404 this.series[i].canvas._plotDimensions = this._plotDimensions; 2405 //this.series[i].init(i, this.grid.borderWidth); 2406 this._sumy += this.series[i]._sumy; 2407 this._sumx += this.series[i]._sumx; 2408 } 2409 2410 var name; 2411 2412 for (var j=0; j<12; j++) { 2413 name = _axisNames[j]; 2414 // Memory Leaks patch : clear ticks elements 2415 var t = this.axes[name]._ticks; 2416 for (var i = 0; i < t.length; i++) { 2417 var el = t[i]._elem; 2418 if (el) { 2419 // if canvas renderer 2420 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 2421 window.G_vmlCanvasManager.uninitElement(el.get(0)); 2422 } 2423 el.emptyForce(); 2424 el = null; 2425 t._elem = null; 2426 } 2427 } 2428 t = null; 2429 2430 this.axes[name]._plotDimensions = this._plotDimensions; 2431 this.axes[name]._ticks = []; 2432 // this.axes[name].renderer.init.call(this.axes[name], {}); 2433 } 2434 2435 if (this.sortData) { 2436 sortData(this.series); 2437 } 2438 2439 this.grid._axes = this.axes; 2440 2441 this.legend._series = this.series; 2442 }; 2443 2444 // sort the series data in increasing order. 2445 function sortData(series) { 2446 var d, sd, pd, ppd, ret; 2447 for (var i=0; i<series.length; i++) { 2448 var check; 2449 var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData]; 2450 for (var n=0; n<4; n++) { 2451 check = true; 2452 d = bat[n]; 2453 if (series[i]._stackAxis == 'x') { 2454 for (var j = 0; j < d.length; j++) { 2455 if (typeof(d[j][1]) != "number") { 2456 check = false;
2457 break; 2458 } 2459 } 2460 if (check) { 2461 d.sort(function(a,b) { return a[1] - b[1]; }); 2462 } 2463 } 2464 else { 2465 for (var j = 0; j < d.length; j++) { 2466 if (typeof(d[j][0]) != "number") { 2467 check = false; 2468 break; 2469 } 2470 } 2471 if (check) { 2472 d.sort(function(a,b) { return a[0] - b[0]; }); 2473 } 2474 } 2475 } 2476 2477 } 2478 } 2479 2480 this.computePlotData = function() { 2481 this._plotData = []; 2482 this._stackData = []; 2483 var series, 2484 index, 2485 l; 2486 2487 2488 for (index=0, l=this.series.length; index<l; index++) { 2489 series = this.series[index]; 2490 this._plotData.push([]); 2491 this._stackData.push([]); 2492 var cd = series.data; 2493 this._plotData[index] = $.extend(true, [], cd); 2494 this._stackData[index] = $.extend(true, [], cd); 2495 series._plotData = this._plotData[index]; 2496 series._stackData = this._stackData[index]; 2497 var plotValues = {x:[], y:[]}; 2498 2499 if (this.stackSeries && !series.disableStack) { 2500 series._stack = true;
2501 /////////////////////////// 2502 // have to check for nulls 2503 /////////////////////////// 2504 var sidx = (series._stackAxis === 'x') ? 0 : 1; 2505 2506 for (var k=0, cdl=cd.length; k<cdl; k++) { 2507 var temp = cd[k][sidx]; 2508 if (temp == null) { 2509 temp = 0; 2510 } 2511 this._plotData[index][k][sidx] = temp; 2512 this._stackData[index][k][sidx] = temp; 2513 2514 if (index > 0) { 2515 for (var j=index; j--;) { 2516 var prevval = this._plotData[j][k][sidx]; 2517 // only need to sum up the stack axis column of data 2518 // and only sum if it is of same sign. 2519 // if previous series isn't same sign, keep looking 2520 // at earlier series untill we find one of same sign. 2521 if (temp * prevval >= 0) { 2522 this._plotData[index][k][sidx] += prevval; 2523 this._stackData[index][k][sidx] += prevval; 2524 break; 2525 } 2526 } 2527 } 2528 } 2529 2530 } 2531 else { 2532 for (var i=0; i<series.data.length; i++) { 2533 plotValues.x.push(series.data[i][0]); 2534 plotValues.y.push(series.data[i][1]); 2535 } 2536 this._stackData.push(series.data); 2537 this.series[index]._stackData = series.data; 2538 this._plotData.push(series.data); 2539 series._plotData = series.data; 2540 series._plotValues = plotValues; 2541 } 2542 if (index>0) { 2543 series._prevPlotData = this.series[index-1]._plotData; 2544 } 2545 series._sumy = 0; 2546 series._sumx = 0; 2547 for (i=series.data.length-1; i>-1; i--) { 2548 series._sumy += series.data[i][1]; 2549 series._sumx += series.data[i][0]; 2550 } 2551 } 2552 2553 }; 2554 2555 // populate the _stackData and _plotData arrays for the plot and the series. 2556 this.populatePlotData = function(series, index) { 2557 // if a stacked chart, compute the stacked data 2558 this._plotData = []; 2559 this._stackData = []; 2560 series._stackData = []; 2561 series._plotData = []; 2562 var plotValues = {x:[], y:[]}; 2563 if (this.stackSeries && !series.disableStack) { 2564 series._stack = true; 2565 var sidx = (series._stackAxis === 'x') ? 0 : 1; 2566 // var idx = sidx ? 0 : 1; 2567 // push the current data into stackData 2568 //this._stackData.push(this.series[i].data); 2569 var temp = $.extend(true, [], series.data); 2570 // create the data that will be plotted for this series 2571 var plotdata = $.extend(true, [], series.data); 2572 var tempx, tempy, dval, stackval, comparator; 2573 // for first series, nothing to add to stackData. 2574 for (var j=0; j<index; j++) { 2575 var cd = this.series[j].data; 2576 for (var k=0; k<cd.length; k++) { 2577 dval = cd[k]; 2578 tempx = (dval[0] != null) ? dval[0] : 0; 2579 tempy = (dval[1] != null) ? dval[1] : 0; 2580 temp[k][0] += tempx; 2581 temp[k][1] += tempy; 2582 stackval = (sidx) ? tempy : tempx; 2583 // only need to sum up the stack axis column of data 2584 // and only sum if it is of same sign. 2585 if (series.data[k][sidx] * stackval >= 0) { 2586 plotdata[k][sidx] += stackval; 2587 } 2588 } 2589 } 2590 for (var i=0; i<plotdata.length; i++) { 2591 plotValues.x.push(plotdata[i][0]); 2592 plotValues.y.push(plotdata[i][1]); 2593 } 2594 this._plotData.push(plotdata); 2595 this._stackData.push(temp); 2596 series._stackData = temp; 2597 series._plotData = plotdata; 2598 series._plotValues = plotValues; 2599 } 2600 else { 2601 for (var i=0; i<series.data.length; i++) { 2602 plotValues.x.push(series.data[i][0]); 2603 plotValues.y.push(series.data[i][1]); 2604 } 2605 this._stackData.push(series.data); 2606 this.series[index]._stackData = series.data; 2607 this._plotData.push(series.data); 2608 series._plotData = series.data; 2609 series._plotValues = plotValues; 2610 } 2611 if (index>0) { 2612 series._prevPlotData = this.series[index-1]._plotData; 2613 } 2614 series._sumy = 0; 2615 series._sumx = 0; 2616 for (i=series.data.length-1; i>-1; i--) { 2617 series._sumy += series.data[i][1]; 2618 series._sumx += series.data[i][0]; 2619 } 2620 }; 2621 2622 // function to safely return colors from the color array and wrap around at the end. 2623 this.getNextSeriesColor = (function(t) { 2624 var idx = 0; 2625 var sc = t.seriesColors; 2626 2627 return function () { 2628 if (idx < sc.length) { 2629 return sc[idx++]; 2630 } 2631 else { 2632 idx = 0; 2633 return sc[idx++]; 2634 } 2635 }; 2636 })(this); 2637 2638 this.parseOptions = function(options){ 2639 for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) { 2640 this.preParseOptionsHooks.hooks[i].call(this, options); 2641 } 2642 for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) { 2643 $.jqplot.preParseOptionsHooks[i].call(this, options); 2644 } 2645 this.options = $.extend(true, {}, this.defaults, options); 2646 var opts = this.options; 2647 this.animate = opts.animate; 2648 this.animateReplot = opts.animateReplot; 2649 this.stackSeries = opts.stackSeries; 2650 if ($.isPlainObject(opts.fillBetween)) { 2651 2652 var temp = ['series1', 'series2', 'color', 'baseSeries', 'fill'], 2653 tempi; 2654 2655 for (var i=0, l=temp.length; i<l; i++) { 2656 tempi = temp[i]; 2657 if (opts.fillBetween[tempi] != null) { 2658 this.fillBetween[tempi] = opts.fillBetween[tempi]; 2659 } 2660 } 2661 } 2662 2663 if (opts.seriesColors) { 2664 this.seriesColors = opts.seriesColors; 2665 } 2666 if (opts.negativeSeriesColors) { 2667 this.negativeSeriesColors = opts.negativeSeriesColors; 2668 } 2669 if (opts.captureRightClick) { 2670 this.captureRightClick = opts.captureRightClick; 2671 } 2672 this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart; 2673 this.colorGenerator.setColors(this.seriesColors); 2674 this.negativeColorGenerator.setColors(this.negativeSeriesColors); 2675 // var cg = new this.colorGenerator(this.seriesColors); 2676 // var ncg = new this.colorGenerator(this.negativeSeriesColors); 2677 // this._gridPadding = this.options.gridPadding; 2678 $.extend(true, this._gridPadding, opts.gridPadding); 2679 this.sortData = (opts.sortData != null) ? opts.sortData : this.sortData; 2680 for (var i=0; i<12; i++) { 2681 var n = _axisNames[i]; 2682 var axis = this.axes[n]; 2683 axis._options = $.extend(true, {}, opts.axesDefaults, opts.axes[n]); 2684 $.extend(true, axis, opts.axesDefaults, opts.axes[n]); 2685 axis._plotWidth = this._width; 2686 axis._plotHeight = this._height; 2687 } 2688 // if (this.data.length == 0) { 2689 // this.data = []; 2690 // for (var i=0; i<this.options.series.length; i++) { 2691 // this.data.push(this.options.series.data); 2692 // } 2693 // } 2694 2695 var normalizeData = function(data, dir, start) { 2696 // return data as an array of point arrays, 2697 // in form [[x1,y1...], [x2,y2...], ...] 2698 var temp = []; 2699 var i, l; 2700 dir = dir || 'vertical'; 2701 if (!$.isArray(data[0])) { 2702 // we have a series of scalars. One line with just y values. 2703 // turn the scalar list of data into a data array of form: 2704 // [[1, data[0]], [2, data[1]], ...] 2705 for (i=0, l=data.length; i<l; i++) { 2706 if (dir == 'vertical') { 2707 temp.push([start + i, data[i]]); 2708 } 2709 else { 2710 temp.push([data[i], start+i]); 2711 } 2712 } 2713 } 2714 else { 2715 // we have a properly formatted data series, copy it. 2716 $.extend(true, temp, data); 2717 } 2718 return temp; 2719 }; 2720 2721 var colorIndex = 0; 2722 this.series = []; 2723 for (var i=0; i<this.data.length; i++) { 2724 var sopts = $.extend(true, {index: i}, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i], {rendererOptions:{animation:{show: this.animate}}}); 2725 // pass in options in case something needs set prior to initialization. 2726 var temp = new Series(sopts); 2727 for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) { 2728 $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]); 2729 } 2730 for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) { 2731 this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]); 2732 } 2733 // Now go back and apply the options to the series. Really should just do this during initializaiton, but don't want to 2734 // mess up preParseSeriesOptionsHooks at this point. 2735 $.extend(true, temp, sopts); 2736 var dir = 'vertical'; 2737 if (temp.renderer === $.jqplot.BarRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') { 2738 dir = 'horizontal'; 2739 temp._stackAxis = 'x'; 2740 temp._primaryAxis = '_yaxis'; 2741 } 2742 temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart); 2743 switch (temp.xaxis) { 2744 case 'xaxis': 2745 temp._xaxis = this.axes.xaxis; 2746 break; 2747 case 'x2axis': 2748 temp._xaxis = this.axes.x2axis; 2749 break; 2750 default: 2751 break; 2752 } 2753 temp._yaxis = this.axes[temp.yaxis]; 2754 temp._xaxis._series.push(temp); 2755 temp._yaxis._series.push(temp); 2756 if (temp.show) { 2757 temp._xaxis.show = true; 2758 temp._yaxis.show = true; 2759 } 2760 else { 2761 if (temp._xaxis.scaleToHiddenSeries) { 2762 temp._xaxis.show = true; 2763 } 2764 if (temp._yaxis.scaleToHiddenSeries) { 2765 temp._yaxis.show = true; 2766 } 2767 } 2768 2769 // // parse the renderer options and apply default colors if not provided 2770 // if (!temp.color && temp.show != false) { 2771 // temp.color = cg.next(); 2772 // colorIndex = cg.getIndex() - 1;; 2773 // } 2774 // if (!temp.negativeColor && temp.show != false) { 2775 // temp.negativeColor = ncg.get(colorIndex); 2776 // ncg.setIndex(colorIndex); 2777 // } 2778 if (!temp.label) { 2779 temp.label = 'Series '+ (i+1).toString(); 2780 } 2781 // temp.rendererOptions.show = temp.show; 2782 // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions); 2783 this.series.push(temp); 2784 for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) { 2785 $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]); 2786 } 2787 for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) { 2788 this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]); 2789 } 2790 } 2791 2792 // copy the grid and title options into this object. 2793 $.extend(true, this.grid, this.options.grid); 2794 // if axis border properties aren't set, set default. 2795 for (var i=0, l=_axisNames.length; i<l; i++) { 2796 var n = _axisNames[i]; 2797 var axis = this.axes[n]; 2798 if (axis.borderWidth == null) { 2799 axis.borderWidth =this.grid.borderWidth; 2800 } 2801 } 2802 2803 if (typeof this.options.title == 'string') { 2804 this.title.text = this.options.title; 2805 } 2806 else if (typeof this.options.title == 'object') { 2807 $.extend(true, this.title, this.options.title); 2808 } 2809 this.title._plotWidth = this._width; 2810 this.legend.setOptions(this.options.legend); 2811 2812 for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) { 2813 $.jqplot.postParseOptionsHooks[i].call(this, options); 2814 } 2815 for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) { 2816 this.postParseOptionsHooks.hooks[i].call(this, options); 2817 } 2818 }; 2819 2820 // method: destroy 2821 // Releases all resources occupied by the plot 2822 this.destroy = function() {
2823 this.canvasManager.freeAllCanvases(); 2824 if (this.eventCanvas && this.eventCanvas._elem) { 2825 this.eventCanvas._elem.unbind(); 2826 } 2827 // Couple of posts on Stack Overflow indicate that empty() doesn't 2828 // always cear up the dom and release memory. Sometimes setting 2829 // innerHTML property to null is needed. Particularly on IE, may 2830 // have to directly set it to null, bypassing $. 2831 this.target.empty(); 2832 2833 this.target[0].innerHTML = ''; 2834 }; 2835 2836 // method: replot 2837 // Does a reinitialization of the plot followed by 2838 // a redraw. Method could be used to interactively 2839 // change plot characteristics and then replot. 2840 // 2841 // Parameters: 2842 // options - Options used for replotting. 2843 // 2844 // Properties: 2845 // clear - false to not clear (empty) the plot container before replotting (default: true). 2846 // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves. 2847 // optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false). 2848 this.replot = function(options) { 2849 var opts = options || {}; 2850 var data = opts.data || null; 2851 var clear = (opts.clear === false) ? false : true; 2852 var resetAxes = opts.resetAxes || false; 2853 delete opts.data; 2854 delete opts.clear; 2855 delete opts.resetAxes; 2856 2857 this.target.trigger('jqplotPreReplot'); 2858 2859 if (clear) { 2860 this.destroy(); 2861 } 2862 // if have data or other options, full reinit. 2863 // otherwise, quickinit. 2864 if (data || !$.isEmptyObject(opts)) { 2865 this.reInitialize(data, opts); 2866 } 2867 else { 2868 this.quickInit(); 2869 } 2870 2871 if (resetAxes) { 2872 this.resetAxesScale(resetAxes, opts.axes); 2873 } 2874 this.draw(); 2875 this.target.trigger('jqplotPostReplot'); 2876 }; 2877 2878 // method: redraw 2879 // Empties the plot target div and redraws the plot. 2880 // This enables plot data and properties to be changed 2881 // and then to comletely clear the plot and redraw. 2882 // redraw *will not* reinitialize any plot elements. 2883 // That is, axes will not be autoscaled and defaults 2884 // will not be reapplied to any plot elements. redraw 2885 // is used primarily with zooming. 2886 // 2887 // Parameters: 2888 // clear - false to not clear (empty) the plot container before redrawing (default: true). 2889 this.redraw = function(clear) { 2890 clear = (clear != null) ? clear : true; 2891 this.target.trigger('jqplotPreRedraw'); 2892 if (clear) { 2893 this.canvasManager.freeAllCanvases(); 2894 this.eventCanvas._elem.unbind(); 2895 // Dont think I bind any events to the target, this shouldn't be necessary. 2896 // It will remove user's events. 2897 // this.target.unbind(); 2898 this.target.empty(); 2899 } 2900 for (var ax in this.axes) { 2901 this.axes[ax]._ticks = []; 2902 } 2903 this.computePlotData(); 2904 // for (var i=0; i<this.series.length; i++) { 2905 // this.populatePlotData(this.series[i], i); 2906 // } 2907 this._sumy = 0; 2908 this._sumx = 0; 2909 for (var i=0, tsl = this.series.length; i<tsl; i++) { 2910 this._sumy += this.series[i]._sumy; 2911 this._sumx += this.series[i]._sumx; 2912 } 2913 this.draw(); 2914 this.target.trigger('jqplotPostRedraw'); 2915 }; 2916 2917 // method: draw 2918 // Draws all elements of the plot into the container. 2919 // Does not clear the container before drawing. 2920 this.draw = function(){ 2921 if (this.drawIfHidden || this.target.is(':visible')) { 2922 this.target.trigger('jqplotPreDraw'); 2923 var i, 2924 j, 2925 l, 2926 tempseries; 2927 for (i=0, l=$.jqplot.preDrawHooks.length; i<l; i++) { 2928 $.jqplot.preDrawHooks[i].call(this); 2929 } 2930 for (i=0, l=this.preDrawHooks.hooks.length; i<l; i++) { 2931 this.preDrawHooks.hooks[i].apply(this, this.preDrawSeriesHooks.args[i]); 2932 } 2933 // create an underlying canvas to be used for special features. 2934 this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas', null, this)); 2935 this.baseCanvas.setContext(); 2936 this.target.append(this.title.draw());
2937 this.title.pack({top:0, left:0}); 2938 2939 // make room for the legend between the grid and the edge. 2940 // pass a dummy offsets object and a reference to the plot. 2941 var legendElem = this.legend.draw({}, this); 2942 2943 var gridPadding = {top:0, left:0, bottom:0, right:0}; 2944 2945 if (this.legend.placement == "outsideGrid") { 2946 // temporarily append the legend to get dimensions 2947 this.target.append(legendElem); 2948 switch (this.legend.location) { 2949 case 'n': 2950 gridPadding.top += this.legend.getHeight(); 2951 break; 2952 case 's': 2953 gridPadding.bottom += this.legend.getHeight(); 2954 break; 2955 case 'ne': 2956 case 'e': 2957 case 'se': 2958 gridPadding.right += this.legend.getWidth(); 2959 break; 2960 case 'nw': 2961 case 'w': 2962 case 'sw': 2963 gridPadding.left += this.legend.getWidth(); 2964 break; 2965 default: // same as 'ne' 2966 gridPadding.right += this.legend.getWidth(); 2967 break; 2968 } 2969 legendElem = legendElem.detach(); 2970 } 2971 2972 var ax = this.axes; 2973 var name; 2974 // draw the yMidAxis first, so xaxis of pyramid chart can adjust itself if needed. 2975 for (i=0; i<12; i++) { 2976 name = _axisNames[i]; 2977 this.target.append(ax[name].draw(this.baseCanvas._ctx, this)); 2978 ax[name].set(); 2979 } 2980 if (ax.yaxis.show) { 2981 gridPadding.left += ax.yaxis.getWidth(); 2982 } 2983 var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis']; 2984 var rapad = [0, 0, 0, 0, 0, 0, 0, 0]; 2985 var gpr = 0; 2986 var n; 2987 for (n=0; n<8; n++) { 2988 if (ax[ra[n]].show) { 2989 gpr += ax[ra[n]].getWidth(); 2990 rapad[n] = gpr; 2991 } 2992 } 2993 gridPadding.right += gpr; 2994 if (ax.x2axis.show) { 2995 gridPadding.top += ax.x2axis.getHeight(); 2996 } 2997 if (this.title.show) { 2998 gridPadding.top += this.title.getHeight(); 2999 } 3000 if (ax.xaxis.show) { 3001 gridPadding.bottom += ax.xaxis.getHeight(); 3002 } 3003 3004 // end of gridPadding adjustments. 3005 3006 // if user passed in gridDimensions option, check against calculated gridPadding 3007 if (this.options.gridDimensions && $.isPlainObject(this.options.gridDimensions)) { 3008 var gdw = parseInt(this.options.gridDimensions.width, 10) || 0; 3009 var gdh = parseInt(this.options.gridDimensions.height, 10) || 0; 3010 var widthAdj = (this._width - gridPadding.left - gridPadding.right - gdw)/2; 3011 var heightAdj = (this._height - gridPadding.top - gridPadding.bottom - gdh)/2; 3012 3013 if (heightAdj >= 0 && widthAdj >= 0) { 3014 gridPadding.top += heightAdj; 3015 gridPadding.bottom += heightAdj; 3016 gridPadding.left += widthAdj; 3017 gridPadding.right += widthAdj; 3018 } 3019 } 3020 var arr = ['top', 'bottom', 'left', 'right']; 3021 for (var n in arr) { 3022 if (this._gridPadding[arr[n]] == null && gridPadding[arr[n]] > 0) { 3023 this._gridPadding[arr[n]] = gridPadding[arr[n]]; 3024 } 3025 else if (this._gridPadding[arr[n]] == null) { 3026 this._gridPadding[arr[n]] = this._defaultGridPadding[arr[n]]; 3027 } 3028 } 3029 3030 var legendPadding = this._gridPadding; 3031 3032 if (this.legend.placement === 'outsideGrid') { 3033 legendPadding = {top:this.title.getHeight(), left: 0, right: 0, bottom: 0}; 3034 if (this.legend.location === 's') { 3035 legendPadding.left = this._gridPadding.left; 3036 legendPadding.right = this._gridPadding.right; 3037 } 3038 } 3039 3040 ax.xaxis.pack({position:'absolute', bottom:this._gridPadding.bottom - ax.xaxis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
3041 ax.yaxis.pack({position:'absolute', top:0, left:this._gridPadding.left - ax.yaxis.getWidth(), height:this._height}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); 3042 ax.x2axis.pack({position:'absolute', top:this._gridPadding.top - ax.x2axis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right}); 3043 for (i=8; i>0; i--) { 3044 ax[ra[i-1]].pack({position:'absolute', top:0, right:this._gridPadding.right - rapad[i-1]}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); 3045 } 3046 var ltemp = (this._width - this._gridPadding.left - this._gridPadding.right)/2.0 + this._gridPadding.left - ax.yMidAxis.getWidth()/2.0; 3047 ax.yMidAxis.pack({position:'absolute', top:0, left:ltemp, zIndex:9, textAlign: 'center'}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); 3048 3049 this.target.append(this.grid.createElement(this._gridPadding, this)); 3050 this.grid.draw(); 3051 3052 var series = this.series; 3053 var seriesLength = series.length; 3054 // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars. 3055 for (i=0, l=seriesLength; i<l; i++) { 3056 // draw series in order of stacking. This affects only 3057 // order in which canvases are added to dom. 3058 j = this.seriesStack[i]; 3059 this.target.append(series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas', null, this)); 3060 series[j].shadowCanvas.setContext(); 3061 series[j].shadowCanvas._elem.data('seriesIndex', j); 3062 } 3063 3064 for (i=0, l=seriesLength; i<l; i++) { 3065 // draw series in order of stacking. This affects only 3066 // order in which canvases are added to dom. 3067 j = this.seriesStack[i]; 3068 this.target.append(series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas', null, this)); 3069 series[j].canvas.setContext(); 3070 series[j].canvas._elem.data('seriesIndex', j); 3071 } 3072 // Need to use filled canvas to capture events in IE. 3073 // Also, canvas seems to block selection of other elements in document on FF. 3074 this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas', null, this)); 3075 this.eventCanvas.setContext(); 3076 this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)'; 3077 this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height); 3078 3079 // bind custom event handlers to regular events. 3080 this.bindCustomEvents(); 3081 3082 // draw legend before series if the series needs to know the legend dimensions. 3083 if (this.legend.preDraw) { 3084 this.eventCanvas._elem.before(legendElem); 3085 this.legend.pack(legendPadding); 3086 if (this.legend._elem) { 3087 this.drawSeries({legendInfo:{location:this.legend.location, placement:this.legend.placement, width:this.legend.getWidth(), height:this.legend.getHeight(), xoffset:this.legend.xoffset, yoffset:this.legend.yoffset}}); 3088 } 3089 else { 3090 this.drawSeries(); 3091 } 3092 } 3093 else { // draw series before legend 3094 this.drawSeries(); 3095 if (seriesLength) { 3096 $(series[seriesLength-1].canvas._elem).after(legendElem); 3097 } 3098 this.legend.pack(legendPadding); 3099 } 3100 3101 // register event listeners on the overlay canvas 3102 for (var i=0, l=$.jqplot.eventListenerHooks.length; i<l; i++) { 3103 // in the handler, this will refer to the eventCanvas dom element. 3104 // make sure there are references back into plot objects. 3105 this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]); 3106 } 3107 3108 // register event listeners on the overlay canvas 3109 for (var i=0, l=this.eventListenerHooks.hooks.length; i<l; i++) { 3110 // in the handler, this will refer to the eventCanvas dom element. 3111 // make sure there are references back into plot objects. 3112 this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]); 3113 } 3114 3115 var fb = this.fillBetween; 3116 if (fb.fill && fb.series1 !== fb.series2 && fb.series1 < seriesLength && fb.series2 < seriesLength && series[fb.series1]._type === 'line' && series[fb.series2]._type === 'line') { 3117 this.doFillBetweenLines(); 3118 } 3119 3120 for (var i=0, l=$.jqplot.postDrawHooks.length; i<l; i++) { 3121 $.jqplot.postDrawHooks[i].call(this); 3122 } 3123 3124 for (var i=0, l=this.postDrawHooks.hooks.length; i<l; i++) { 3125 this.postDrawHooks.hooks[i].apply(this, this.postDrawHooks.args[i]); 3126 } 3127 3128 if (this.target.is(':visible')) { 3129 this._drawCount += 1; 3130 } 3131 3132 var temps, 3133 tempr, 3134 sel, 3135 _els; 3136 // ughh. ideally would hide all series then show them. 3137 for (i=0, l=seriesLength; i<l; i++) { 3138 temps = series[i]; 3139 tempr = temps.renderer; 3140 sel = '.jqplot-point-label.jqplot-series-'+i; 3141 if (tempr.animation && tempr.animation._supported && tempr.animation.show && (this._drawCount < 2 || this.animateReplot)) { 3142 _els = this.target.find(sel); 3143 _els.stop(true, true).hide(); 3144 temps.canvas._elem.stop(true, true).hide(); 3145 temps.shadowCanvas._elem.stop(true, true).hide(); 3146 temps.canvas._elem.jqplotEffect('blind', {mode: '
3146show', direction: tempr.animation.direction}, tempr.animation.speed); 3147 temps.shadowCanvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed); 3148 _els.fadeIn(tempr.animation.speed*0.8); 3149 } 3150 } 3151 _els = null; 3152 3153 this.target.trigger('jqplotPostDraw', [this]); 3154 } 3155 }; 3156 3157 jqPlot.prototype.doFillBetweenLines = function () { 3158 var fb = this.fillBetween; 3159 var sid1 = fb.series1; 3160 var sid2 = fb.series2; 3161 // first series should always be lowest index 3162 var id1 = (sid1 < sid2) ? sid1 : sid2; 3163 var id2 = (sid2 > sid1) ? sid2 : sid1; 3164 3165 var series1 = this.series[id1]; 3166 var series2 = this.series[id2]; 3167 3168 if (series2.renderer.smooth) { 3169 var tempgd = series2.renderer._smoothedData.slice(0).reverse(); 3170 } 3171 else { 3172 var tempgd = series2.gridData.slice(0).reverse(); 3173 } 3174 3175 if (series1.renderer.smooth) { 3176 var gd = series1.renderer._smoothedData.concat(tempgd); 3177 } 3178 else { 3179 var gd = series1.gridData.concat(tempgd); 3180 } 3181 3182 var color = (fb.color !== null) ? fb.color : this.series[sid1].fillColor; 3183 var baseSeries = (fb.baseSeries !== null) ? fb.baseSeries : id1; 3184 3185 // now apply a fill to the shape on the lower series shadow canvas, 3186 // so it is behind both series. 3187 var sr = this.series[baseSeries].renderer.shapeRenderer; 3188 var opts = {fillStyle: color, fill: true, closePath: true}; 3189 sr.draw(series1.shadowCanvas._ctx, gd, opts); 3190 }; 3191 3192 this.bindCustomEvents = function() { 3193 this.eventCanvas._elem.bind('click', {plot:this}, this.onClick); 3194 this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick); 3195 this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown); 3196 this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove); 3197 this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter); 3198 this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave); 3199 if (this.captureRightClick) { 3200 this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick); 3201 this.eventCanvas._elem.get(0).oncontextmenu = function() { 3202 return false; 3203 }; 3204 } 3205 else { 3206 this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp); 3207 } 3208 }; 3209 3210 function getEventPosition(ev) { 3211 var plot = ev.data.plot; 3212 var go = plot.eventCanvas._elem.offset(); 3213 var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top}; 3214 var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null}; 3215 var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; 3216 var ax = plot.axes; 3217 var n, axis; 3218 for (n=11; n>0; n--) { 3219 axis = an[n-1]; 3220 if (ax[axis].show) { 3221 dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]); 3222 } 3223 } 3224 3225 return {offsets:go, gridPos:gridPos, dataPos:dataPos}; 3226 } 3227 3228 3229 // function to check if event location is over a area area 3230 function checkIntersection(gridpos, plot) { 3231 var series = plot.series; 3232 var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang; 3233 var d0, d, p, pp, points, bw, hp; 3234 var threshold, t; 3235 for (k=plot.seriesStack.length-1; k>=0; k--) { 3236 i = plot.seriesStack[k]; 3237 s = series[i]; 3238 hp = s._highlightThreshold; 3239 switch (s.renderer.constructor) { 3240 case $.jqplot.BarRenderer: 3241 x = gridpos.x; 3242 y = gridpos.y; 3243 for (j=0; j<s._barPoints.length; j++) { 3244 points = s._barPoints[j]; 3245 p = s.gridData[j]; 3246 if (x>points[0][0] && x<points[2][0] && y>points[2][1] && y<points[0][1]) { 3247 return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]}; 3248 } 3249 } 3250 break; 3251 case $.jqplot.PyramidRenderer: 3252 x = gridpos.x; 3253 y = gridpos.y; 3254 for (j=0; j<s._barPoints.length; j++) { 3255 points = s._barPoints[j]; 3256 p = s.gridData[j]; 3257 if (x > points[0][0] + hp[0][0] && x < points[2][0] + hp[2][0] && y > points[2][1] && y < points[0][1]) { 3258 return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]}; 3259 } 3260 } 3261 break; 3262
3263 case $.jqplot.DonutRenderer: 3264 sa = s.startAngle/180*Math.PI; 3265 x = gridpos.x - s._center[0]; 3266 y = gridpos.y - s._center[1]; 3267 r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); 3268 if (x > 0 && -y >= 0) { 3269 theta = 2*Math.PI - Math.atan(-y/x); 3270 } 3271 else if (x > 0 && -y < 0) { 3272 theta = -Math.atan(-y/x); 3273 } 3274 else if (x < 0) { 3275 theta = Math.PI - Math.atan(-y/x); 3276 } 3277 else if (x == 0 && -y > 0) { 3278 theta = 3*Math.PI/2; 3279 } 3280 else if (x == 0 && -y < 0) { 3281 theta = Math.PI/2; 3282 } 3283 else if (x == 0 && y == 0) { 3284 theta = 0; 3285 } 3286 if (sa) { 3287 theta -= sa; 3288 if (theta < 0) { 3289 theta += 2*Math.PI; 3290 } 3291 else if (theta > 2*Math.PI) { 3292 theta -= 2*Math.PI; 3293 } 3294 } 3295 3296 sm = s.sliceMargin/180*Math.PI; 3297 if (r < s._radius && r > s._innerRadius) { 3298 for (j=0; j<s.gridData.length; j++) { 3299 minang = (j>0) ? s.gridData[j-1][1]+sm : sm; 3300 maxang = s.gridData[j][1]; 3301 if (theta > minang && theta < maxang) { 3302 return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]}; 3303 } 3304 } 3305 } 3306 break; 3307 3308 case $.jqplot.PieRenderer: 3309 sa = s.startAngle/180*Math.PI; 3310 x = gridpos.x - s._center[0]; 3311 y = gridpos.y - s._center[1]; 3312 r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); 3313 if (x > 0 && -y >= 0) { 3314 theta = 2*Math.PI - Math.atan(-y/x); 3315 } 3316 else if (x > 0 && -y < 0) { 3317 theta = -Math.atan(-y/x); 3318 } 3319 else if (x < 0) { 3320 theta = Math.PI - Math.atan(-y/x); 3321 } 3322 else if (x == 0 && -y > 0) { 3323 theta = 3*Math.PI/2; 3324 } 3325 else if (x == 0 && -y < 0) { 3326 theta = Math.PI/2; 3327 } 3328 else if (x == 0 && y == 0) { 3329 theta = 0; 3330 } 3331 if (sa) { 3332 theta -= sa; 3333 if (theta < 0) { 3334 theta += 2*Math.PI; 3335 } 3336 else if (theta > 2*Math.PI) { 3337 theta -= 2*Math.PI; 3338 } 3339 } 3340 3341 sm = s.sliceMargin/180*Math.PI; 3342 if (r < s._radius) { 3343 for (j=0; j<s.gridData.length; j++) { 3344 minang = (j>0) ? s.gridData[j-1][1]+sm : sm; 3345 maxang = s.gridData[j][1]; 3346 if (theta > minang && theta < maxang) { 3347 return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]}; 3348 } 3349 } 3350 } 3351 break; 3352 3353 case $.jqplot.BubbleRenderer: 3354 x = gridpos.x; 3355 y = gridpos.y; 3356 var ret = null; 3357 3358 if (s.show) { 3359 for (var j=0; j<s.gridData.length; j++) { 3360 p = s.gridData[j]; 3361 d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-
3361p[1]) * (y-p[1]) ); 3362 if (d <= p[2] && (d <= d0 || d0 == null)) { 3363 d0 = d; 3364 ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3365 } 3366 } 3367 if (ret != null) { 3368 return ret; 3369 } 3370 } 3371 break; 3372 3373 case $.jqplot.FunnelRenderer: 3374 x = gridpos.x; 3375 y = gridpos.y; 3376 var v = s._vertices, 3377 vfirst = v[0], 3378 vlast = v[v.length-1], 3379 lex, 3380 rex, 3381 cv; 3382 3383 // equations of right and left sides, returns x, y values given height of section (y value and 2 points) 3384 3385 function findedge (l, p1 , p2) { 3386 var m = (p1[1] - p2[1])/(p1[0] - p2[0]); 3387 var b = p1[1] - m*p1[0]; 3388 var y = l + p1[1]; 3389 3390 return [(y - b)/m, y]; 3391 } 3392 3393 // check each section 3394 lex = findedge(y, vfirst[0], vlast[3]); 3395 rex = findedge(y, vfirst[1], vlast[2]); 3396 for (j=0; j<v.length; j++) { 3397 cv = v[j]; 3398 if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) { 3399 return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]}; 3400 } 3401 } 3402 break; 3403 3404 case $.jqplot.LineRenderer: 3405 x = gridpos.x; 3406 y = gridpos.y; 3407 r = s.renderer; 3408 if (s.show) { 3409 if ((s.fill || (s.renderer.bands.show && s.renderer.bands.fill)) && (!plot.plugins.highlighter || !plot.plugins.highlighter.show)) { 3410 // first check if it is in bounding box 3411 var inside = false;
3412 if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) { 3413 // now check the crossing number 3414 3415 var numPoints = s._areaPoints.length; 3416 var ii; 3417 var j = numPoints-1; 3418 3419 for(var ii=0; ii < numPoints; ii++) { 3420 var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]]; 3421 var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]]; 3422 3423 if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y) { 3424 if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) { 3425 inside = !inside; 3426 } 3427 } 3428 3429 j = ii; 3430 } 3431 } 3432 if (inside) { 3433 return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints}; 3434 } 3435 break; 3436 3437 } 3438 3439 else { 3440 t = s.markerRenderer.size/2+s.neighborThreshold; 3441 threshold = (t > 0) ? t : 0; 3442 for (var j=0; j<s.gridData.length; j++) { 3443 p = s.gridData[j]; 3444 // neighbor looks different to OHLC chart. 3445 if (r.constructor == $.jqplot.OHLCRenderer) { 3446 if (r.candleStick) { 3447 var yp = s._yaxis.series_u2p; 3448 if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { 3449 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3450 } 3451 } 3452 // if an open hi low close chart 3453 else if (!r.hlc){ 3454 var yp = s._yaxis.series_u2p; 3455 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { 3456 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3457 } 3458 } 3459 // a hi low close chart 3460 else { 3461 var yp = s._yaxis.series_u2p; 3462 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) { 3463 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3464 } 3465 } 3466 3467 } 3468 else if (p[0] != null && p[1] != null){ 3469 d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) ); 3470 if (d <= threshold && (d <= d0 || d0 == null)) { 3471 d0 = d; 3472 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3473 } 3474 } 3475 } 3476 } 3477 } 3478 break; 3479 3480 default: 3481 x = gridpos.x; 3482 y = gridpos.y; 3483 r = s.renderer; 3484 if (s.show) { 3485 t = s.markerRenderer.size/2+s.neighborThreshold; 3486 threshold = (t > 0) ? t : 0; 3487 for (var j=0; j<s.gridData.length; j++) { 3488 p = s.gridData[j]; 3489 // neighbor looks different to OHLC chart. 3490 if (r.constructor == $.jqplot.OHLCRenderer) { 3491 if (r.candleStick) { 3492 var yp = s._yaxis.series_u2p; 3493 if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { 3494 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3495 } 3496 } 3497 // if an open hi low close chart 3498 else if (!r.hlc){ 3499 var yp = s._yaxis.series_u2p; 3500 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { 3501 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3502 } 3503 } 3504 // a hi low close chart 3505 else { 3506 var yp = s._yaxis.series_u2p; 3507 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) { 3508 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3509 } 3510 } 3511 3512 } 3513 else { 3514 d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-
3514p[1]) * (y-p[1]) ); 3515 if (d <= threshold && (d <= d0 || d0 == null)) { 3516 d0 = d; 3517 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3518 } 3519 } 3520 } 3521 } 3522 break; 3523 } 3524 } 3525 3526 return null; 3527 } 3528 3529 3530 3531 this.onClick = function(ev) { 3532 // Event passed in is normalized and will have data attribute. 3533 // Event passed out is unnormalized. 3534 var positions = getEventPosition(ev); 3535 var p = ev.data.plot; 3536 var neighbor = checkIntersection(positions.gridPos, p); 3537 var evt = $.Event('jqplotClick'); 3538 evt.pageX = ev.pageX; 3539 evt.pageY = ev.pageY; 3540 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3541 }; 3542 3543 this.onDblClick = function(ev) { 3544 // Event passed in is normalized and will have data attribute. 3545 // Event passed out is unnormalized. 3546 var positions = getEventPosition(ev); 3547 var p = ev.data.plot; 3548 var neighbor = checkIntersection(positions.gridPos, p); 3549 var evt = $.Event('jqplotDblClick'); 3550 evt.pageX = ev.pageX; 3551 evt.pageY = ev.pageY; 3552 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3553 }; 3554 3555 this.onMouseDown = function(ev) { 3556 var positions = getEventPosition(ev); 3557 var p = ev.data.plot; 3558 var neighbor = checkIntersection(positions.gridPos, p); 3559 var evt = $.Event('jqplotMouseDown'); 3560 evt.pageX = ev.pageX; 3561 evt.pageY = ev.pageY; 3562 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3563 }; 3564 3565 this.onMouseUp = function(ev) { 3566 var positions = getEventPosition(ev); 3567 var evt = $.Event('jqplotMouseUp'); 3568 evt.pageX = ev.pageX; 3569 evt.pageY = ev.pageY; 3570 $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]); 3571 }; 3572 3573 this.onRightClick = function(ev) { 3574 var positions = getEventPosition(ev); 3575 var p = ev.data.plot; 3576 var neighbor = checkIntersection(positions.gridPos, p); 3577 if (p.captureRightClick) { 3578 if (ev.which == 3) { 3579 var evt = $.Event('jqplotRightClick'); 3580 evt.pageX = ev.pageX; 3581 evt.pageY = ev.pageY; 3582 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3583 } 3584 else { 3585 var evt = $.Event('jqplotMouseUp'); 3586 evt.pageX = ev.pageX; 3587 evt.pageY = ev.pageY; 3588 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3589 } 3590 } 3591 }; 3592 3593 this.onMouseMove = function(ev) { 3594 var positions = getEventPosition(ev); 3595 var p = ev.data.plot; 3596 var neighbor = checkIntersection(positions.gridPos, p); 3597 var evt = $.Event('jqplotMouseMove'); 3598 evt.pageX = ev.pageX; 3599 evt.pageY = ev.pageY; 3600 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3601 }; 3602 3603 this.onMouseEnter = function(ev) { 3604 var positions = getEventPosition(ev); 3605 var p = ev.data.plot; 3606 var evt = $.Event('jqplotMouseEnter'); 3607 evt.pageX = ev.pageX; 3608 evt.pageY = ev.pageY; 3609 evt.relatedTarget = ev.relatedTarget; 3610 $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]); 3611 }; 3612 3613 this.onMouseLeave = function(ev) { 3614 var positions = getEventPosition(ev); 3615 var p = ev.data.plot; 3616 var evt = $.Event('jqplotMouseLeave'); 3617 evt.pageX = ev.pageX; 3618 evt.pageY = ev.pageY; 3619 evt.relatedTarget = ev.relatedTarget; 3620 $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]); 3621 }; 3622 3623 // method: drawSeries 3624 // Redraws all or just one series on the plot. No axis scaling 3625 // is performed and no other elements on the plot are redrawn. 3626 // options is an options object to pass on to the series renderers. 3627 // It can be an empty object {}. idx is the series index 3628 // to redraw if only one series is to be redrawn. 3629 this.drawSeries = function(options, idx){ 3630 var i, series, ctx; 3631 // if only one argument passed in and it is a number, use it ad idx.
3632 idx = (typeof(options) === "number" && idx == null) ? options : idx; 3633 options = (typeof(options) === "object") ? options : {}; 3634 // draw specified series 3635 if (idx != undefined) { 3636 series = this.series[idx]; 3637 ctx = series.shadowCanvas._ctx; 3638 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 3639 series.drawShadow(ctx, options, this); 3640 ctx = series.canvas._ctx; 3641 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 3642 series.draw(ctx, options, this); 3643 if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) { 3644 if (idx < this.series.length - 1) { 3645 this.drawSeries(idx+1); 3646 } 3647 } 3648 } 3649 3650 else { 3651 // if call series drawShadow method first, in case all series shadows 3652 // should be drawn before any series. This will ensure, like for 3653 // stacked bar plots, that shadows don't overlap series. 3654 for (i=0; i<this.series.length; i++) { 3655 // first clear the canvas 3656 series = this.series[i]; 3657 ctx = series.shadowCanvas._ctx; 3658 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 3659 series.drawShadow(ctx, options, this); 3660 ctx = series.canvas._ctx; 3661 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 3662 series.draw(ctx, options, this); 3663 } 3664 } 3665 options = idx = i = series = ctx = null; 3666 }; 3667 3668 // method: moveSeriesToFront 3669 // This method requires jQuery 1.4+ 3670 // Moves the specified series canvas in front of all other series canvases. 3671 // This effectively "draws" the specified series on top of all other series, 3672 // although it is performed through DOM manipulation, no redrawing is performed. 3673 // 3674 // Parameters: 3675 // idx - 0 based index of the series to move. This will be the index of the series 3676 // as it was first passed into the jqplot function. 3677 this.moveSeriesToFront = function (idx) { 3678 idx = parseInt(idx, 10); 3679 var stackIndex = $.inArray(idx, this.seriesStack); 3680 // if already in front, return 3681 if (stackIndex == -1) { 3682 return; 3683 } 3684 if (stackIndex == this.seriesStack.length -1) { 3685 this.previousSeriesStack = this.seriesStack.slice(0); 3686 return; 3687 } 3688 var opidx = this.seriesStack[this.seriesStack.length -1]; 3689 var serelem = this.series[idx].canvas._elem.detach(); 3690 var shadelem = this.series[idx].shadowCanvas._elem.detach(); 3691 this.series[opidx].shadowCanvas._elem.after(shadelem); 3692 this.series[opidx].canvas._elem.after(serelem); 3693 this.previousSeriesStack = this.seriesStack.slice(0); 3694 this.seriesStack.splice(stackIndex, 1); 3695 this.seriesStack.push(idx); 3696 }; 3697 3698 // method: moveSeriesToBack 3699 // This method requires jQuery 1.4+ 3700 // Moves the specified series canvas behind all other series canvases. 3701 // 3702 // Parameters: 3703 // idx - 0 based index of the series to move. This will be the index of the series 3704 // as it was first passed into the jqplot function. 3705 this.moveSeriesToBack = function (idx) { 3706 idx = parseInt(idx, 10); 3707 var stackIndex = $.inArray(idx, this.seriesStack); 3708 // if already in back, return 3709 if (stackIndex == 0 || stackIndex == -1) { 3710 return; 3711 } 3712 var opidx = this.seriesStack[0]; 3713 var serelem = this.series[idx].canvas._elem.detach(); 3714 var shadelem = this.series[idx].shadowCanvas._elem.detach(); 3715 this.series[opidx].shadowCanvas._elem.before(shadelem); 3716 this.series[opidx].canvas._elem.before(serelem); 3717 this.previousSeriesStack = this.seriesStack.slice(0); 3718 this.seriesStack.splice(stackIndex, 1); 3719 this.seriesStack.unshift(idx); 3720 }; 3721 3722 // method: restorePreviousSeriesOrder 3723 // This method requires jQuery 1.4+ 3724 // Restore the series canvas order to its previous state.
3725 // Useful to put a series back where it belongs after moving 3726 // it to the front. 3727 this.restorePreviousSeriesOrder = function () { 3728 var i, j, serelem, shadelem, temp, move, keep; 3729 // if no change, return. 3730 if (this.seriesStack == this.previousSeriesStack) { 3731 return; 3732 } 3733 for (i=1; i<this.previousSeriesStack.length; i++) { 3734 move = this.previousSeriesStack[i]; 3735 keep = this.previousSeriesStack[i-1]; 3736 serelem = this.series[move].canvas._elem.detach(); 3737 shadelem = this.series[move].shadowCanvas._elem.detach(); 3738 this.series[keep].shadowCanvas._elem.after(shadelem); 3739 this.series[keep].canvas._elem.after(serelem); 3740 } 3741 temp = this.seriesStack.slice(0); 3742 this.seriesStack = this.previousSeriesStack.slice(0); 3743 this.previousSeriesStack = temp; 3744 }; 3745 3746 // method: restoreOriginalSeriesOrder 3747 // This method requires jQuery 1.4+ 3748 // Restore the series canvas order to its original order 3749 // when the plot was created. 3750 this.restoreOriginalSeriesOrder = function () { 3751 var i, j, arr=[], serelem, shadelem; 3752 for (i=0; i<this.series.length; i++) { 3753 arr.push(i); 3754 } 3755 if (this.seriesStack == arr) { 3756 return; 3757 } 3758 this.previousSeriesStack = this.seriesStack.slice(0); 3759 this.seriesStack = arr; 3760 for (i=1; i<this.seriesStack.length; i++) { 3761 serelem = this.series[i].canvas._elem.detach(); 3762 shadelem = this.series[i].shadowCanvas._elem.detach(); 3763 this.series[i-1].shadowCanvas._elem.after(shadelem); 3764 this.series[i-1].canvas._elem.after(serelem); 3765 } 3766 }; 3767 3768 this.activateTheme = function (name) { 3769 this.themeEngine.activate(this, name); 3770 }; 3771 } 3772 3773 3774 // conpute a highlight color or array of highlight colors from given colors. 3775 $.jqplot.computeHighlightColors = function(colors) { 3776 var ret; 3777 if ($.isArray(colors)) { 3778 ret = []; 3779 for (var i=0; i<colors.length; i++){ 3780 var rgba = $.jqplot.getColorComponents(colors[i]); 3781 var newrgb = [rgba[0], rgba[1], rgba[2]]; 3782 var sum = newrgb[0] + newrgb[1] + newrgb[2]; 3783 for (var j=0; j<3; j++) { 3784 // when darkening, lowest color component can be is 60. 3785 newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90; 3786 newrgb[j] = parseInt(newrgb[j], 10); 3787 (newrgb[j] > 255) ? 255 : newrgb[j]; 3788 } 3789 // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5; 3790 // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2; 3791 newrgb[3] = 0.3 + 0.35 * rgba[3]; 3792 ret.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')'); 3793 } 3794 } 3795 else { 3796 var rgba = $.jqplot.getColorComponents(colors); 3797 var newrgb = [rgba[0], rgba[1], rgba[2]]; 3798 var sum = newrgb[0] + newrgb[1] + newrgb[2]; 3799 for (var j=0; j<3; j++) { 3800 // when darkening, lowest color component can be is 60. 3801 // newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); 3802 // newrgb[j] = parseInt(newrgb[j], 10); 3803 newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90; 3804 newrgb[j] = parseInt(newrgb[j], 10); 3805 (newrgb[j] > 255) ? 255 : newrgb[j]; 3806 } 3807 // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5; 3808 // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2; 3809 newrgb[3] = 0.3 + 0.35 * rgba[3]; 3810 ret = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')'; 3811 } 3812 return ret; 3813 }; 3814 3815 $.jqplot.ColorGenerator = function(colors) { 3816 colors = colors || $.jqplot.config.defaultColors; 3817 var idx = 0; 3818 3819 this.next = function () { 3820 if (idx < colors.length) { 3821 return colors[idx++]; 3822 } 3823 else { 3824 idx = 0; 3825 return colors[idx++]; 3826 } 3827 }; 3828 3829 this.previous = function () { 3830 if (idx > 0) { 3831 return colors[idx--]; 3832 } 3833 else { 3834 idx = colors.length-1; 3835 return colors[idx]; 3836 } 3837 }; 3838 3839 // get a color by index without advancing pointer. 3840 this.get = function(i) { 3841 var idx = i - colors.length * Math.floor(i/colors.length); 3842 return colors[idx]; 3843 }; 3844 3845 this.setColors = function(c) { 3846 colors = c; 3847 }; 3848 3849 this.reset = function() { 3850 idx = 0; 3851 }; 3852 3853 this.getIndex = function() { 3854 return idx; 3855 }; 3856 3857 this.setIndex = function(index) { 3858 idx = index; 3859 }; 3860 }; 3861 3862 // convert a hex color string to rgb string. 3863 // h - 3 or 6 character hex string, with or without leading # 3864 // a - optional alpha 3865 $.jqplot.hex2rgb = function(h, a) { 3866 h = h.replace('#', ''); 3867 if (h.length == 3) { 3868 h = h.charAt(0)+h.charAt(0)+h.charAt(1)+h.charAt(1)+h.charAt(2)+h.charAt(2); 3869 } 3870 var rgb; 3871 rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16); 3872 if (a) { 3873 rgb += ', '+a; 3874 } 3875 rgb += ')'; 3876 return rgb; 3877 }; 3878 3879 // convert an rgb color spec to a hex spec. ignore any alpha specification. 3880 $.jqplot.rgb2hex = function(s) { 3881 var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/; 3882 var m = s.match(pat); 3883 var h = '#'; 3884 for (var i=1; i<4; i++) { 3885 var temp; 3886 if (m[i].search(/%/) != -1) { 3887 temp = parseInt(255*m[i]/100, 10).toString(16); 3888 if (temp.length == 1) { 3889 temp = '0'+temp; 3890 } 3891 } 3892 else { 3893 temp = parseInt(m[i], 10).toString(16); 3894 if (temp.length == 1) { 3895 temp = '0'+temp; 3896 } 3897 } 3898 h += temp; 3899 } 3900 return h; 3901 }; 3902 3903 // given a css color spec, return an rgb css color spec 3904 $.jqplot.normalize2rgb = function(s, a) { 3905 if (s.search(/^ *rgba?\(/) != -1) { 3906 return s; 3907 } 3908 else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) { 3909 return $.jqplot.hex2rgb(s, a); 3910 } 3911 else { 3912 throw new Error('Invalid color spec'); 3913 } 3914 }; 3915 3916 // extract the r, g, b, a color components out of a css color spec. 3917 $.jqplot.getColorComponents = function(s) { 3918 // check to see if a color keyword. 3919 s = $.jqplot.colorKeywordMap[s] || s; 3920 var rgb = $.jqplot.normalize2rgb(s); 3921 var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/; 3922 var m = rgb.match(pat); 3923 var ret = []; 3924 for (var i=1; i<4; i++) { 3925 if (m[i].search(/%/) != -1) { 3926 ret[i-1] = parseInt(255*m[i]/100, 10); 3927 } 3928 else { 3929 ret[i-1] = parseInt(m[i], 10); 3930 } 3931 } 3932 ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0; 3933 return ret; 3934 }; 3935 3936 $.jqplot.colorKeywordMap = { 3937 aliceblue: 'rgb(240, 248, 255)', 3938 antiquewhite: 'rgb(250, 235, 215)', 3939 aqua: 'rgb( 0, 255, 255)', 3940 aquamarine: 'rgb(127, 255, 212)', 3941 azure: 'rgb(240, 255, 255)', 3942 beige: 'rgb(245, 245, 220)', 3943 bisque: 'rgb(255, 228, 196)', 3944 black: 'rgb( 0, 0, 0)', 3945 blanchedalmond: 'rgb(255, 235, 205)', 3946 blue: 'rgb( 0, 0, 255)', 3947 blueviolet: 'rgb(138, 43, 226)', 3948 brown: 'rgb(165, 42, 42)', 3949 burlywood: 'rgb(222, 184, 135)', 3950 cadetblue: 'rgb( 95, 158, 160)', 3951 chartreuse: 'rgb(127, 255, 0)', 3952 chocolate: 'rgb(210, 105, 30)', 3953 coral: 'rgb(255, 127, 80)', 3954 cornflowerblue: 'rgb(100, 149, 237)', 3955 cornsilk: 'rgb(255, 248, 220)', 3956 crimson: 'rgb(220, 20, 60)', 3957 cyan: 'rgb( 0, 255, 255)', 3958 darkblue: 'rgb( 0, 0, 139)', 3959 darkcyan: 'rgb( 0, 139, 139)', 3960 darkgoldenrod: 'rgb(184, 134, 11)', 3961 darkgray: 'rgb(169, 169, 169)', 3962 darkgreen: 'rgb( 0, 100, 0)', 3963 darkgrey: 'rgb(169, 169, 169)', 3964 darkkhaki: 'rgb(189, 183, 107)', 3965 darkmagenta: 'rgb(139, 0, 139)', 3966 darkolivegreen: 'rgb( 85, 107, 47)', 3967 darkorange: 'rgb(255, 140, 0)', 3968 darkorchid: 'rgb(153, 50, 204)',
3969 darkred: 'rgb(139, 0, 0)', 3970 darksalmon: 'rgb(233, 150, 122)', 3971 darkseagreen: 'rgb(143, 188, 143)', 3972 darkslateblue: 'rgb( 72, 61, 139)', 3973 darkslategray: 'rgb( 47, 79, 79)', 3974 darkslategrey: 'rgb( 47, 79, 79)', 3975 darkturquoise: 'rgb( 0, 206, 209)', 3976 darkviolet: 'rgb(148, 0, 211)', 3977 deeppink: 'rgb(255, 20, 147)', 3978 deepskyblue: 'rgb( 0, 191, 255)', 3979 dimgray: 'rgb(105, 105, 105)', 3980 dimgrey: 'rgb(105, 105, 105)', 3981 dodgerblue: 'rgb( 30, 144, 255)', 3982 firebrick: 'rgb(178, 34, 34)', 3983 floralwhite: 'rgb(255, 250, 240)', 3984 forestgreen: 'rgb( 34, 139, 34)', 3985 fuchsia: 'rgb(255, 0, 255)', 3986 gainsboro: 'rgb(220, 220, 220)', 3987 ghostwhite: 'rgb(248, 248, 255)', 3988 gold: 'rgb(255, 215, 0)', 3989 goldenrod: 'rgb(218, 165, 32)', 3990 gray: 'rgb(128, 128, 128)', 3991 grey: 'rgb(128, 128, 128)', 3992 green: 'rgb( 0, 128, 0)', 3993 greenyellow: 'rgb(173, 255, 47)', 3994 honeydew: 'rgb(240, 255, 240)', 3995 hotpink: 'rgb(255, 105, 180)', 3996 indianred: 'rgb(205, 92, 92)', 3997 indigo: 'rgb( 75, 0, 130)', 3998 ivory: 'rgb(255, 255, 240)', 3999 khaki: 'rgb(240, 230, 140)', 4000 lavender: 'rgb(230, 230, 250)', 4001 lavenderblush: 'rgb(255, 240, 245)', 4002 lawngreen: 'rgb(124, 252, 0)', 4003 lemonchiffon: 'rgb(255, 250, 205)', 4004 lightblue: 'rgb(173, 216, 230)', 4005 lightcoral: 'rgb(240, 128, 128)', 4006 lightcyan: 'rgb(224, 255, 255)', 4007 lightgoldenrodyellow: 'rgb(250, 250, 210)', 4008 lightgray: 'rgb(211, 211, 211)', 4009 lightgreen: 'rgb(144, 238, 144)', 4010 lightgrey: 'rgb(211, 211, 211)', 4011 lightpink: 'rgb(255, 182, 193)', 4012 lightsalmon: 'rgb(255, 160, 122)', 4013 lightseagreen: 'rgb( 32, 178, 170)', 4014 lightskyblue: 'rgb(135, 206, 250)', 4015 lightslategray: 'rgb(119, 136, 153)', 4016 lightslategrey: 'rgb(119, 136, 153)', 4017 lightsteelblue: 'rgb(176, 196, 222)', 4018 lightyellow: 'rgb(255, 255, 224)', 4019 lime: 'rgb( 0, 255, 0)', 4020 limegreen: 'rgb( 50, 205, 50)', 4021 linen: 'rgb(250, 240, 230)', 4022 magenta: 'rgb(255, 0, 255)',
4023 maroon: 'rgb(128, 0, 0)', 4024 mediumaquamarine: 'rgb(102, 205, 170)', 4025 mediumblue: 'rgb( 0, 0, 205)', 4026 mediumorchid: 'rgb(186, 85, 211)', 4027 mediumpurple: 'rgb(147, 112, 219)', 4028 mediumseagreen: 'rgb( 60, 179, 113)', 4029 mediumslateblue: 'rgb(123, 104, 238)', 4030 mediumspringgreen: 'rgb( 0, 250, 154)', 4031 mediumturquoise: 'rgb( 72, 209, 204)', 4032 mediumvioletred: 'rgb(199, 21, 133)', 4033 midnightblue: 'rgb( 25, 25, 112)', 4034 mintcream: 'rgb(245, 255, 250)', 4035 mistyrose: 'rgb(255, 228, 225)', 4036 moccasin: 'rgb(255, 228, 181)', 4037 navajowhite: 'rgb(255, 222, 173)', 4038 navy: 'rgb( 0, 0, 128)', 4039 oldlace: 'rgb(253, 245, 230)', 4040 olive: 'rgb(128, 128, 0)', 4041 olivedrab: 'rgb(107, 142, 35)', 4042 orange: 'rgb(255, 165, 0)', 4043 orangered: 'rgb(255, 69, 0)', 4044 orchid: 'rgb(218, 112, 214)', 4045 palegoldenrod: 'rgb(238, 232, 170)', 4046 palegreen: 'rgb(152, 251, 152)', 4047 paleturquoise: 'rgb(175, 238, 238)', 4048 palevioletred: 'rgb(219, 112, 147)', 4049 papayawhip: 'rgb(255, 239, 213)', 4050 peachpuff: 'rgb(255, 218, 185)', 4051 peru: 'rgb(205, 133, 63)', 4052 pink: 'rgb(255, 192, 203)', 4053 plum: 'rgb(221, 160, 221)', 4054 powderblue: 'rgb(176, 224, 230)', 4055 purple: 'rgb(128, 0, 128)', 4056 red: 'rgb(255, 0, 0)', 4057 rosybrown: 'rgb(188, 143, 143)', 4058 royalblue: 'rgb( 65, 105, 225)', 4059 saddlebrown: 'rgb(139, 69, 19)', 4060 salmon: 'rgb(250, 128, 114)', 4061 sandybrown: 'rgb(244, 164, 96)', 4062 seagreen: 'rgb( 46, 139, 87)', 4063 seashell: 'rgb(255, 245, 238)', 4064 sienna: 'rgb(160, 82, 45)', 4065 silver: 'rgb(192, 192, 192)', 4066 skyblue: 'rgb(135, 206, 235)', 4067 slateblue: 'rgb(106, 90, 205)', 4068 slategray: 'rgb(112, 128, 144)', 4069 slategrey: 'rgb(112, 128, 144)', 4070 snow: 'rgb(255, 250, 250)', 4071 springgreen: 'rgb( 0, 255, 127)', 4072 steelblue: 'rgb( 70, 130, 180)', 4073 tan: 'rgb(210, 180, 140)', 4074 teal: 'rgb( 0, 128, 128)', 4075 thistle: 'rgb(216, 191, 216)', 4076 tomato: 'rgb(255, 99, 71)', 4077 turquoise: 'rgb( 64, 224, 208)', 4078 violet: 'rgb(238, 130, 238)', 4079 wheat: 'rgb(245, 222, 179)', 4080 white: 'rgb(255, 255, 255)', 4081 whitesmoke: 'rgb(245, 245, 245)', 4082 yellow: 'rgb(255, 255, 0)', 4083 yellowgreen: 'rgb(154, 205, 50)' 4084 }; 4085 4086 4087 4088 4089 // class: $.jqplot.AxisLabelRenderer 4090 // Renderer to place labels on the axes. 4091 $.jqplot.AxisLabelRenderer = function(options) { 4092 // Group: Properties 4093 $.jqplot.ElemContainer.call(this); 4094 // name of the axis associated with this tick 4095 this.axis; 4096 // prop: show 4097 // whether or not to show the tick (mark and label). 4098 this.show = true; 4099 // prop: label 4100 // The text or html for the label. 4101 this.label = ''; 4102 this.fontFamily = null; 4103 this.fontSize = null; 4104 this.textColor = null; 4105 this._elem; 4106 // prop: escapeHTML 4107 // true to escape HTML entities in the label. 4108 this.escapeHTML = false; 4109 4110 $.extend(true, this, options); 4111 }; 4112 4113 $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer(); 4114 $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer; 4115 4116 $.jqplot.AxisLabelRenderer.prototype.init = function(options) { 4117 $.extend(true, this, options); 4118 }; 4119 4120 $.jqplot.AxisLabelRenderer.prototype.draw = function(ctx, plot) { 4121 // Memory Leaks patch 4122 if (this._elem) { 4123 this._elem.emptyForce(); 4124 this._elem = null; 4125 } 4126 4127 this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>'); 4128 4129 if (Number(this.label)) { 4130 this._elem.css('white-space', 'nowrap'); 4131 } 4132 4133 if (!this.escapeHTML) { 4134 this._elem.html(this.label); 4135 } 4136 else { 4137 this._elem.text(this.label); 4138 } 4139 if (this.fontFamily) { 4140 this._elem.css('font-family', this.fontFamily); 4141 } 4142 if (this.fontSize) { 4143 this._elem.css('font-size', this.fontSize); 4144 } 4145 if (this.textColor) { 4146 this._elem.css('color', this.textColor); 4147 } 4148 4149 return this._elem; 4150 }; 4151 4152 $.jqplot.AxisLabelRenderer.prototype.pack = function() { 4153 }; 4154 4155 // class: $.jqplot.AxisTickRenderer 4156 // A "tick" object showing the value of a tick/gridline on the plot. 4157 $.jqplot.AxisTickRenderer = function(options) { 4158 // Group: Properties 4159 $.jqplot.ElemContainer.call(this); 4160 // prop: mark 4161 // tick mark on the axis. One of 'inside', 'outside', 'cross', '' or null. 4162 this.mark = 'outside'; 4163 // name of the axis associated with this tick 4164 this.axis; 4165 // prop: showMark 4166 // whether or not to show the mark on the axis. 4167 this.showMark = true; 4168 // prop: showGridline 4169 // whether or not to draw the gridline on the grid at this tick. 4170 this.showGridline = true; 4171 // prop: isMinorTick 4172 // if this is a minor tick. 4173 this.isMinorTick = false;
4174 // prop: size 4175 // Length of the tick beyond the grid in pixels. 4176 // DEPRECATED: This has been superceeded by markSize 4177 this.size = 4; 4178 // prop: markSize 4179 // Length of the tick marks in pixels. For 'cross' style, length 4180 // will be stoked above and below axis, so total length will be twice this. 4181 this.markSize = 6; 4182 // prop: show 4183 // whether or not to show the tick (mark and label). 4184 // Setting this to false requires more testing. It is recommended 4185 // to set showLabel and showMark to false instead. 4186 this.show = true; 4187 // prop: showLabel 4188 // whether or not to show the label. 4189 this.showLabel = true; 4190 this.label = null; 4191 this.value = null; 4192 this._styles = {}; 4193 // prop: formatter 4194 // A class of a formatter for the tick text. sprintf by default. 4195 this.formatter = $.jqplot.DefaultTickFormatter; 4196 // prop: prefix 4197 // String to prepend to the tick label. 4198 // Prefix is prepended to the formatted tick label. 4199 this.prefix = ''; 4200 // prop: suffix 4201 // String to append to the tick label. 4202 // Suffix is appended to the formatted tick label. 4203 this.suffix = ''; 4204 // prop: formatString 4205 // string passed to the formatter. 4206 this.formatString = ''; 4207 // prop: fontFamily 4208 // css spec for the font-family css attribute. 4209 this.fontFamily; 4210 // prop: fontSize 4211 // css spec for the font-size css attribute. 4212 this.fontSize; 4213 // prop: textColor 4214 // css spec for the color attribute. 4215 this.textColor; 4216 // prop: escapeHTML 4217 // true to escape HTML entities in the label. 4218 this.escapeHTML = false; 4219 this._elem; 4220 this._breakTick = false; 4221 4222 $.extend(true, this, options); 4223 }; 4224 4225 $.jqplot.AxisTickRenderer.prototype.init = function(options) { 4226 $.extend(true, this, options); 4227 }; 4228 4229 $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer(); 4230 $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer; 4231 4232 $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) { 4233 this.value = value; 4234 this.axis = axisName; 4235 if (isMinor) { 4236 this.isMinorTick = true; 4237 } 4238 return this; 4239 }; 4240 4241 $.jqplot.AxisTickRenderer.prototype.draw = function() { 4242 if (this.label === null) { 4243 this.label = this.prefix + this.formatter(this.formatString, this.value) + this.suffix; 4244 } 4245 var style = {position: 'absolute'}; 4246 if (Number(this.label)) { 4247 style['whitSpace'] = 'nowrap'; 4248 } 4249 4250 // Memory Leaks patch 4251 if (this._elem) { 4252 this._elem.emptyForce(); 4253 this._elem = null; 4254 } 4255 4256 this._elem = $(document.createElement('div')); 4257 this._elem.addClass("jqplot-"+this.axis+"-tick"); 4258 4259 if (!this.escapeHTML) { 4260 this._elem.html(this.label); 4261 } 4262 else { 4263 this._elem.text(this.label); 4264 } 4265 4266 this._elem.css(style); 4267 4268 for (var s in this._styles) { 4269 this._elem.css(s, this._styles[s]); 4270 } 4271 if (this.fontFamily) { 4272 this._elem.css('font-family', this.fontFamily); 4273 } 4274 if (this.fontSize) { 4275 this._elem.css('font-size', this.fontSize); 4276 } 4277 if (this.textColor) { 4278 this._elem.css('color', this.textColor); 4279 } 4280 if (this._breakTick) { 4281 this._elem.addClass('jqplot-breakTick'); 4282 } 4283 4284 return this._elem; 4285 }; 4286 4287 $.jqplot.DefaultTickFormatter = function (format, val) { 4288 if (typeof val == 'number') { 4289 if (!format) { 4290 format = $.jqplot.config.defaultTickFormatString; 4291 } 4292 return $.jqplot.sprintf(format, val); 4293 } 4294 else { 4295 return String(val); 4296 } 4297 }; 4298 4299 $.jqplot.PercentTickFormatter = function (format, val) { 4300 if (typeof val == 'number') { 4301 val = 100 * val; 4302 if (!format) { 4303 format = $.jqplot.config.defaultTickFormatString; 4304 } 4305 return $.jqplot.sprintf(format, val); 4306 } 4307 else { 4308 return String(val); 4309 } 4310 }; 4311 4312 $.jqplot.AxisTickRenderer.prototype.pack = function() { 4313 }; 4314 4315 // Class: $.jqplot.CanvasGridRenderer 4316 // The default jqPlot grid renderer, creating a grid on a canvas element. 4317 // The renderer has no additional options beyond the <Grid> class. 4318 $.jqplot.CanvasGridRenderer = function(){ 4319 this.shadowRenderer = new $.jqplot.ShadowRenderer(); 4320 }; 4321 4322 // called with context of Grid object 4323 $.jqplot.CanvasGridRenderer.prototype.init = function(options) { 4324 this._ctx; 4325 $.extend(true, this, options); 4326 // set the shadow renderer options 4327 var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor}; 4328 this.renderer.shadowRenderer.init(sopts); 4329 }; 4330 4331 // called with context of Grid. 4332 $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) { 4333 var elem; 4334 // Memory Leaks patch 4335 if (this._elem) { 4336 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 4337 elem = this._elem.get(0); 4338 window.G_vmlCanvasManager.uninitElement(elem); 4339 elem = null; 4340 } 4341 4342 this._elem.emptyForce(); 4343 this._elem = null; 4344 } 4345 4346 elem = plot.canvasManager.getCanvas(); 4347 4348 var w = this._plotDimensions.width; 4349 var h = this._plotDimensions.height; 4350 elem.width = w; 4351 elem.height = h; 4352 this._elem = $(elem); 4353 this._elem.addClass('jqplot-grid-canvas'); 4354 this._elem.css({ position: 'absolute', left: 0, top: 0 }); 4355 4356 elem = plot.canvasManager.initCanvas(elem); 4357 4358 this._top = this._offsets.top; 4359 this._bottom = h - this._offsets.bottom; 4360 this._left = this._offsets.left; 4361 this._right = w - this._offsets.right; 4362 this._width = this._right - this._left; 4363 this._height = this._bottom - this._top; 4364 // avoid memory leak 4365 elem = null; 4366 return this._elem; 4367 }; 4368 4369 $.jqplot.CanvasGridRenderer.prototype.draw = function() { 4370 this._ctx = this._elem.get(0).getContext("2d"); 4371 var ctx = this._ctx; 4372 var axes = this._axes; 4373 // Add the grid onto the grid canvas. This is the bottom most layer. 4374 ctx.save(); 4375 ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height); 4376 ctx.fillStyle = this.backgroundColor || this.background; 4377 ctx.fillRect(this._left, this._top, this._width, this._height); 4378 4379 ctx.save(); 4380 ctx.lineJoin = 'miter'; 4381 ctx.lineCap = 'butt'; 4382 ctx.lineWidth = this.gridLineWidth; 4383 ctx.strokeStyle = this.gridLineColor; 4384 var b, e, s, m; 4385 var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis']; 4386 for (var i=4; i>0; i--) { 4387 var name = ax[i-1]; 4388 var axis = axes[name]; 4389 var ticks = axis._ticks; 4390 var numticks = ticks.length; 4391 if (axis.show) { 4392 if (axis.drawBaseline) { 4393 var bopts = {}; 4394 if (axis.baselineWidth !== null) { 4395 bopts.lineWidth = axis.baselineWidth; 4396 } 4397 if (axis.baselineColor !== null) { 4398 bopts.strokeStyle = axis.baselineColor; 4399 } 4400 switch (name) { 4401 case 'xaxis': 4402 drawLine (this._left, this._bottom, this._right, this._bottom, bopts); 4403 break; 4404 case 'yaxis': 4405 drawLine (this._left, this._bottom, this._left, this._top, bopts); 4406 break; 4407 case 'x2axis': 4408 drawLine (this._left, this._bottom, this._right, this._bottom, bopts); 4409 break; 4410 case 'y2axis': 4411 drawLine (this._right, this._bottom, this._right, this._top, bopts); 4412 break; 4413 } 4414 } 4415 for (var j=numticks; j>0; j--) { 4416 var t = ticks[j-1]; 4417 if (t.show) { 4418 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4419 switch (name) { 4420 case 'xaxis': 4421 // draw the grid line if we should 4422 if (t.showGridline && this.drawGri
4422dlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { 4423 drawLine(pos, this._top, pos, this._bottom); 4424 } 4425 // draw the mark 4426 if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { 4427 s = t.markSize; 4428 m = t.mark; 4429 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4430 switch (m) { 4431 case 'outside': 4432 b = this._bottom; 4433 e = this._bottom+s; 4434 break; 4435 case 'inside': 4436 b = this._bottom-s; 4437 e = this._bottom; 4438 break; 4439 case 'cross': 4440 b = this._bottom-s; 4441 e = this._bottom+s; 4442 break; 4443 default: 4444 b = this._bottom; 4445 e = this._bottom+s; 4446 break; 4447 } 4448 // draw the shadow 4449 if (this.shadow) { 4450 this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); 4451 } 4452 // draw the line 4453 drawLine(pos, b, pos, e); 4454 } 4455 break; 4456 case 'yaxis': 4457 // draw the grid line 4458 if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { 4459 drawLine(this._right, pos, this._left, pos); 4460 } 4461 // draw the mark 4462 if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { 4463 s = t.markSize; 4464 m = t.mark; 4465 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4466 switch (m) { 4467 case 'outside': 4468 b = this._left-s; 4469 e = this._left; 4470 break; 4471 case 'inside': 4472 b = this._left; 4473 e = this._left+s; 4474 break; 4475 case 'cross': 4476 b = this._left-s; 4477 e = this._left+s; 4478 break; 4479 default: 4480 b = this._left-s; 4481 e = this._left; 4482 break; 4483 } 4484 // draw the shadow 4485 if (this.shadow) { 4486 this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); 4487 } 4488 drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); 4489 } 4490 break; 4491 case 'x2axis': 4492 // draw the grid line 4493 if (t.showGridline && this.drawGri
4493dlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { 4494 drawLine(pos, this._bottom, pos, this._top); 4495 } 4496 // draw the mark 4497 if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { 4498 s = t.markSize; 4499 m = t.mark; 4500 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4501 switch (m) { 4502 case 'outside': 4503 b = this._top-s; 4504 e = this._top; 4505 break; 4506 case 'inside': 4507 b = this._top; 4508 e = this._top+s; 4509 break; 4510 case 'cross': 4511 b = this._top-s; 4512 e = this._top+s; 4513 break; 4514 default: 4515 b = this._top-s; 4516 e = this._top; 4517 break; 4518 } 4519 // draw the shadow 4520 if (this.shadow) { 4521 this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); 4522 } 4523 drawLine(pos, b, pos, e); 4524 } 4525 break; 4526 case 'y2axis': 4527 // draw the grid line 4528 if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { 4529 drawLine(this._left, pos, this._right, pos); 4530 } 4531 // draw the mark 4532 if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { 4533 s = t.markSize; 4534 m = t.mark; 4535 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4536 switch (m) { 4537 case 'outside': 4538 b = this._right; 4539 e = this._right+s; 4540 break; 4541 case 'inside': 4542 b = this._right-s; 4543 e = this._right; 4544 break; 4545 case 'cross': 4546 b = this._right-s; 4547 e = this._right+s; 4548 break; 4549 default: 4550 b = this._right; 4551 e = this._right+s; 4552 break; 4553 } 4554 // draw the shadow 4555 if (this.shadow) { 4556 this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); 4557 } 4558 drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); 4559 } 4560 break; 4561 default: 4562 break; 4563 } 4564 } 4565 } 4566 t = null; 4567 } 4568 axis = null; 4569 ticks = null; 4570 } 4571 // Now draw grid lines for additional y axes 4572 ////// 4573 // TO DO: handle yMidAxis 4574 ////// 4575 ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; 4576 for (var i=7; i>0; i--) { 4577 var axis = axes[ax[i-1]]; 4578 var ticks = axis._ticks; 4579 if (axis.show) { 4580 var tn = ticks[axis.numberTicks-1]; 4581 var t0 = ticks[0]; 4582 var left = axis.getLeft(); 4583 var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]]; 4584 // draw the shadow 4585 if (this.shadow) { 4586 this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false}); 4587 } 4588 // draw the line 4589 drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth}); 4590 // draw the tick marks 4591 for (var j=ticks.length; j>0; j--) { 4592 var t = ticks[j-1]; 4593 s = t.markSize; 4594 m = t.mark; 4595 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4596 if (t.showMark && t.mark) { 4597 switch (m) { 4598 case 'outside': 4599 b = left; 4600 e = left+s; 4601 break; 4602 case 'inside': 4603 b = left-s; 4604 e = left; 4605 break; 4606 case 'cross': 4607 b = left-s; 4608 e = left+s; 4609 break; 4610 default: 4611 b = left; 4612 e = left+s; 4613 break; 4614 } 4615 points = [[b,pos], [e,pos]]; 4616 // draw the shadow 4617 if (this.shadow) { 4618 this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); 4619 } 4620 // draw the line 4621 drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); 4622 } 4623 t = null; 4624 } 4625 t0 = null; 4626 } 4627 axis = null; 4628 ticks = null; 4629 } 4630
4631 ctx.restore(); 4632 4633 function drawLine(bx, by, ex, ey, opts) { 4634 ctx.save(); 4635 opts = opts || {}; 4636 if (opts.lineWidth == null || opts.lineWidth != 0){ 4637 $.extend(true, ctx, opts); 4638 ctx.beginPath(); 4639 ctx.moveTo(bx, by); 4640 ctx.lineTo(ex, ey); 4641 ctx.stroke(); 4642 ctx.restore(); 4643 } 4644 } 4645 4646 if (this.shadow) { 4647 var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]]; 4648 this.renderer.shadowRenderer.draw(ctx, points); 4649 } 4650 // Now draw border around grid. Use axis border definitions. start at 4651 // upper left and go clockwise. 4652 if (this.borderWidth != 0 && this.drawBorder) { 4653 drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); 4654 drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth}); 4655 drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); 4656 drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); 4657 } 4658 // ctx.lineWidth = this.borderWidth; 4659 // ctx.strokeStyle = this.borderColor; 4660 // ctx.strokeRect(this._left, this._top, this._width, this._height); 4661 4662 ctx.restore(); 4663 ctx = null; 4664 axes = null; 4665 }; 4666 4667 // Class: $.jqplot.DivTitleRenderer 4668 // The default title renderer for jqPlot. This class has no options beyond the <Title> class. 4669 $.jqplot.DivTitleRenderer = function() { 4670 }; 4671 4672 $.jqplot.DivTitleRenderer.prototype.init = function(options) { 4673 $.extend(true, this, options); 4674 }; 4675 4676 $.jqplot.DivTitleRenderer.prototype.draw = function() { 4677 // Memory Leaks patch 4678 if (this._elem) { 4679 this._elem.emptyForce(); 4680 this._elem = null; 4681 } 4682 4683 var r = this.renderer; 4684 var elem = document.createElement('div'); 4685 this._elem = $(elem); 4686 this._elem.addClass('jqplot-title'); 4687 4688 if (!this.text) { 4689 this.show = false; 4690 this._elem.height(0); 4691 this._elem.width(0); 4692 } 4693 else if (this.text) { 4694 var color; 4695 if (this.color) { 4696 color = this.color; 4697 } 4698 else if (this.textColor) { 4699 color = this.textColor; 4700 } 4701 4702 // don't trust that a stylesheet is present, set the position. 4703 var styles = {position:'absolute', top:'0px', left:'0px'}; 4704 4705 if (this._plotWidth) { 4706 styles['width'] = this._plotWidth+'px'; 4707 } 4708 if (this.fontSize) { 4709 styles['fontSize'] = this.fontSize; 4710 } 4711 if (typeof this.textAlign === 'string') { 4712 styles['textAlign'] = this.textAlign; 4713 } 4714 else { 4715 styles['textAlign'] = 'center'; 4716 } 4717 if (color) { 4718 styles['color'] = color; 4719 } 4720 if (this.paddingBottom) { 4721 styles['paddingBottom'] = this.paddingBottom; 4722 } 4723 if (this.fontFamily) { 4724 styles['fontFamily'] = this.fontFamily; 4725 } 4726 4727 this._elem.css(styles); 4728 if (this.escapeHtml) { 4729 this._elem.text(this.text); 4730 } 4731 else { 4732 this._elem.html(this.text); 4733 } 4734 4735 4736 // styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : ''; 4737 // styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; 4738 // styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;'; 4739 // styletext += (color) ? 'color:'+color+';' : ''; 4740 // styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : ''; 4741 // this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>'); 4742 // if (this.fontFamily) { 4743 // this._elem.css('font-family', this.fontFamily); 4744 // } 4745 } 4746 4747 elem = null; 4748 4749 return this._elem; 4750 }; 4751 4752 $.jqplot.DivTitleRenderer.prototype.pack = function() { 4753 // nothing to do here 4754 }; 4755 4756 4757 var dotlen = 0.1; 4758 4759 $.jqplot.LinePattern = function (ctx, pattern) { 4760 4761 var defaultLinePatterns = { 4762 dotted: [ dotlen, $.jqplot.config.dotGapLength ], 4763 dashed: [ $.jqplot.config.dashLength, $.jqplot.config.gapLength ], 4764 solid: null 4765 }; 4766 4767 if (typeof pattern === 'string') {
4768 if (pattern[0] === '.' || pattern[0] === '-') { 4769 var s = pattern; 4770 pattern = []; 4771 for (var i=0, imax=s.length; i<imax; i++) { 4772 if (s[i] === '.') { 4773 pattern.push( dotlen ); 4774 } 4775 else if (s[i] === '-') { 4776 pattern.push( $.jqplot.config.dashLength ); 4777 } 4778 else { 4779 continue; 4780 } 4781 pattern.push( $.jqplot.config.gapLength ); 4782 } 4783 } 4784 else { 4785 pattern = defaultLinePatterns[pattern]; 4786 } 4787 } 4788 4789 if (!(pattern && pattern.length)) { 4790 return ctx; 4791 } 4792 4793 var patternIndex = 0; 4794 var patternDistance = pattern[0]; 4795 var px = 0; 4796 var py = 0; 4797 var pathx0 = 0; 4798 var pathy0 = 0; 4799 4800 var moveTo = function (x, y) { 4801 ctx.moveTo( x, y ); 4802 px = x; 4803 py = y; 4804 pathx0 = x; 4805 pathy0 = y; 4806 }; 4807 4808 var lineTo = function (x, y) { 4809 var scale = ctx.lineWidth; 4810 var dx = x - px; 4811 var dy = y - py; 4812 var dist = Math.sqrt(dx*dx+dy*dy); 4813 if ((dist > 0) && (scale > 0)) { 4814 dx /= dist; 4815 dy /= dist; 4816 while (true) { 4817 var dp = scale * patternDistance; 4818 if (dp < dist) { 4819 px += dp * dx; 4820 py += dp * dy; 4821 if ((patternIndex & 1) == 0) { 4822 ctx.lineTo( px, py ); 4823 } 4824 else { 4825 ctx.moveTo( px, py ); 4826 } 4827 dist -= dp; 4828 patternIndex++; 4829 if (patternIndex >= pattern.length) { 4830 patternIndex = 0; 4831 } 4832 patternDistance = pattern[patternIndex]; 4833 } 4834 else { 4835 px = x; 4836 py = y; 4837 if ((patternIndex & 1) == 0) { 4838 ctx.lineTo( px, py ); 4839 } 4840 else { 4841 ctx.moveTo( px, py ); 4842 } 4843 patternDistance -= dist / scale; 4844 break; 4845 } 4846 } 4847 } 4848 }; 4849 4850 var beginPath = function () { 4851 ctx.beginPath(); 4852 }; 4853 4854 var closePath = function () { 4855 lineTo( pathx0, pathy0 ); 4856 }; 4857 4858 return { 4859 moveTo: moveTo, 4860 lineTo: lineTo, 4861 beginPath: beginPath, 4862 closePath: closePath 4863 }; 4864 }; 4865 4866 // Class: $.jqplot.LineRenderer 4867 // The default line renderer for jqPlot, this class has no options beyond the <Series> class. 4868 // Draws series as a line. 4869 $.jqplot.LineRenderer = function(){ 4870 this.shapeRenderer = new $.jqplot.ShapeRenderer(); 4871 this.shadowRenderer = new $.jqplot.ShadowRenderer(); 4872 }; 4873 4874 // called with scope of series. 4875 $.jqplot.LineRenderer.prototype.init = function(options, plot) { 4876 // Group: Properties 4877 // 4878 options = options || {}; 4879 this._type='line'; 4880 this.renderer.animation = { 4881 show: false, 4882 direction: 'left', 4883 speed: 2500, 4884 _supported: true 4885 }; 4886 // prop: smooth 4887 // True to draw a smoothed (interpolated) line through the data points 4888 // with automatically computed number of smoothing points. 4889 // Set to an integer number > 2 to specify number of smoothing points 4890 // to use between each data point. 4891 this.renderer.smooth = false; // true or a number > 2 for smoothing. 4892 this.renderer.tension = null; // null to auto compute or a number typically > 6. Fewer points requires higher tension. 4893 // prop: constrainSmoothing 4894 // True to use a more accurate smoothing algorithm that will 4895 // not overshoot any data points. False to allow overshoot but 4896 // produce a smoother looking line. 4897 this.renderer.constrainSmoothing = true; 4898 // this is smoothed data in grid coordinates, like gridData 4899 this.renderer._smoothedData = []; 4900 // this is smoothed data in plot units (plot coordinates), like plotData. 4901 this.renderer._smoothedPlotData = []; 4902 this.renderer._hiBandGridData = []; 4903 this.renderer._lowBandGridData = []; 4904 this.renderer._hiBandSmoothedData = []; 4905 this.renderer._lowBandSmoothedData = []; 4906 4907 // prop: bandData 4908 // Data used to draw error bands or confidence intervals above/below a line. 4909 // 4910 // bandData can be input in 3 forms. jqPlot will figure out which is the 4911 // low band line and which is the high band line for all forms: 4912 // 4913 // A 2 dimensional array like [[yl1, yl2, ...], [yu1, yu2, ...]] where 4914 // [yl1, yl2, ...] are y values of the lower line and 4915 // [yu1, yu2, ...] are y values of the upper line. 4916 // In this case there must be the same number of y data points as data points 4917 // in the series and the bands will inherit the x values of the series. 4918 // 4919 // A 2 dimensional array like [[[xl1, yl1], [xl2, yl2], ...], [[xh1, yh1], [xh2, yh2], ...]] 4920 // where [xl1, yl1] are x,y data points for the lower line and 4921 // [xh1, yh1] are x,y data points for the high line. 4922 // x values do not have to correspond to the x values of the series and can 4923 // be of any arbitrary length. 4924 // 4925 // Can be of form [[yl1, yu1], [yl2, yu2], [yl3, yu3], ...] where 4926 // there must be 3 or more arrays and there must be the same number of arrays 4927 // as there are data points in the series. In this case, 4928 // [yl1, yu1] specifies the lower and upper y values for the 1st 4929 // data point and so on. The bands will inherit the x 4930 // values from the series. 4931 this.renderer.bandData = []; 4932 4933 // Group: bands 4934 // Banding around line, e.g error bands or confidence intervals. 4935 this.renderer.bands = { 4936 // prop: show 4937 // true to show the bands. If bandData or interval is 4938 // supplied, show will be set to true by default. 4939 show: false, 4940 hiData: [], 4941 lowData: [], 4942 // prop: color 4943 // color of lines at top and bottom of bands [default: series color]. 4944 color: this.color, 4945 // prop: showLines 4946 // True to show lines at top and bottom of bands [default: false]. 4947 showLines: false, 4948 // prop: fill 4949 // True to fill area between bands [default: true]. 4950 fill: true, 4951 // prop: fillColor 4952 // css color spec for filled area. [default: series color]. 4953 fillColor: null, 4954 _min: null, 4955 _max: null, 4956 // prop: interval 4957 // User specified interval above and below line for bands [default: '3%'']. 4958 // Can be a value like 3 or a string like '3%' 4959 // or an upper/lower array like [1, -2] or ['2%', '-1.5%'] 4960 interval: '3%' 4961 }; 4962 4963 4964 var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor}; 4965 4966 delete (options.highlightMouseOver); 4967 delete (options.highlightMouseDown); 4968 delete (options.highlightColor); 4969 4970 $.extend(true, this.renderer, options); 4971 4972 this.renderer.options = options; 4973 4974 // if we are given some band data, and bands aren't explicity set to false in options, turn them on. 4975 if (this.renderer.bandData.length > 1 && (!options.bands || options.bands.show == null)) { 4976 this.renderer.bands.show = true; 4977 } 4978 4979 // if we are given an interval, and bands aren't explicity set to false in options, turn them on. 4980 else if (options.bands && options.bands.show == null && options.bands.interval != null) { 4981 this.renderer.bands.show = true; 4982 } 4983 4984 // if plot is filled, turn off bands. 4985 if (this.fill) { 4986 this.renderer.bands.show = false;
4987 } 4988 4989 if (this.renderer.bands.show) { 4990 this.renderer.initBands.call(this, this.renderer.options, plot); 4991 } 4992 4993 4994 // smoothing is not compatible with stacked lines, disable 4995 if (this._stack) { 4996 this.renderer.smooth = false; 4997 } 4998 4999 // set the shape renderer options 5000 var opts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.fillColor, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill}; 5001 this.renderer.shapeRenderer.init(opts); 5002 5003 var shadow_offset = options.shadowOffset; 5004 // set the shadow renderer options 5005 if (shadow_offset == null) { 5006 // scale the shadowOffset to the width of the line. 5007 if (this.lineWidth > 2.5) { 5008 shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6); 5009 // var shadow_offset = this.shadowOffset; 5010 } 5011 // for skinny lines, don't make such a big shadow. 5012 else { 5013 shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163; 5014 } 5015 } 5016 5017 var sopts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill}; 5018 this.renderer.shadowRenderer.init(sopts); 5019 this._areaPoints = []; 5020 this._boundingBox = [[],[]]; 5021 5022 if (!this.isTrendline && this.fill || this.renderer.bands.show) { 5023 // Group: Properties 5024 // 5025 // prop: highlightMouseOver 5026 // True to highlight area on a filled plot when moused over. 5027 // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot. 5028 this.highlightMouseOver = true; 5029 // prop: highlightMouseDown 5030 // True to highlight when a mouse button is pressed over an area on a filled plot. 5031 // This will be disabled if highlightMouseOver is true. 5032 this.highlightMouseDown = false; 5033 // prop: highlightColor 5034 // color to use when highlighting an area on a filled plot. 5035 this.highlightColor = null; 5036 // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver 5037 if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) { 5038 lopts.highlightMouseOver = false; 5039 } 5040 5041 $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor}); 5042 5043 if (!this.highlightColor) { 5044 var fc = (this.renderer.bands.show) ? this.renderer.bands.fillColor : this.fillColor; 5045 this.highlightColor = $.jqplot.computeHighlightColors(fc); 5046 } 5047 // turn off (disable) the highlighter plugin 5048 if (this.highlighter) { 5049 this.highlighter.show = false; 5050 } 5051 } 5052 5053 if (!this.isTrendline && plot) { 5054 plot.plugins.lineRenderer = {}; 5055 plot.postInitHooks.addOnce(postInit); 5056 plot.postDrawHooks.addOnce(postPlotDraw); 5057 plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); 5058 plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); 5059 plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); 5060 plot.eventListenerHooks.addOnce('jqplotClick', handleClick); 5061 plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); 5062 } 5063 5064 }; 5065 5066 $.jqplot.LineRenderer.prototype.initBands = function(options, plot) { 5067 // use bandData if no data specified in bands option 5068 //var bd = this.renderer.bandData; 5069 var bd = options.bandData || []; 5070 var bands = this.renderer.bands; 5071 bands.hiData = []; 5072 bands.lowData = []; 5073 var data = this.data; 5074 bands._max = null; 5075 bands._min = null; 5076 // If 2 arrays, and each array greater than 2 elements, assume it is hi and low data bands of y values. 5077 if (bd.length == 2) { 5078 // Do we have an array of x,y values?
5079 // like [[[1,1], [2,4], [3,3]], [[1,3], [2,6], [3,5]]] 5080 if ($.isArray(bd[0][0])) { 5081 // since an arbitrary array of points, spin through all of them to determine max and min lines. 5082 5083 var p; 5084 var bdminidx = 0, bdmaxidx = 0; 5085 for (var i = 0, l = bd[0].length; i<l; i++) { 5086 p = bd[0][i]; 5087 if ((p[1] != null && p[1] > bands._max) || bands._max == null) { 5088 bands._max = p[1]; 5089 } 5090 if ((p[1] != null && p[1] < bands._min) || bands._min == null) { 5091 bands._min = p[1]; 5092 } 5093 } 5094 for (var i = 0, l = bd[1].length; i<l; i++) { 5095 p = bd[1][i]; 5096 if ((p[1] != null && p[1] > bands._max) || bands._max == null) { 5097 bands._max = p[1]; 5098 bdmaxidx = 1; 5099 } 5100 if ((p[1] != null && p[1] < bands._min) || bands._min == null) { 5101 bands._min = p[1]; 5102 bdminidx = 1; 5103 } 5104 } 5105 5106 if (bdmaxidx === bdminidx) { 5107 bands.show = false; 5108 } 5109 5110 bands.hiData = bd[bdmaxidx]; 5111 bands.lowData = bd[bdminidx]; 5112 } 5113 // else data is arrays of y values 5114 // like [[1,4,3], [3,6,5]] 5115 // must have same number of band data points as points in series 5116 else if (bd[0].length === data.length && bd[1].length === data.length) { 5117 var hi = (bd[0][0] > bd[1][0]) ? 0 : 1; 5118 var low = (hi) ? 0 : 1; 5119 for (var i=0, l=data.length; i < l; i++) { 5120 bands.hiData.push([data[i][0], bd[hi][i]]); 5121 bands.lowData.push([data[i][0], bd[low][i]]); 5122 } 5123 } 5124 5125 // we don't have proper data array, don't show bands. 5126 else { 5127 bands.show = false; 5128 } 5129 } 5130 5131 // if more than 2 arrays, have arrays of [ylow, yhi] values. 5132 // note, can't distinguish case of [[ylow, yhi], [ylow, yhi]] from [[ylow, ylow], [yhi, yhi]] 5133 // this is assumed to be of the latter form. 5134 else if (bd.length > 2 && !$.isArray(bd[0][0])) { 5135 var hi = (bd[0][0] > bd[0][1]) ? 0 : 1; 5136 var low = (hi) ? 0 : 1; 5137 for (var i=0, l=bd.length; i<l; i++) { 5138 bands.hiData.push([data[i][0], bd[i][hi]]); 5139 bands.lowData.push([data[i][0], bd[i][low]]); 5140 } 5141 } 5142 5143 // don't have proper data, auto calculate 5144 else { 5145 var intrv = bands.interval; 5146 var a = null; 5147 var b = null; 5148 var afunc = null; 5149 var bfunc = null; 5150 5151 if ($.isArray(intrv)) { 5152 a = intrv[0]; 5153 b = intrv[1]; 5154 } 5155 else { 5156 a = intrv; 5157 } 5158 5159 if (isNaN(a)) { 5160 // we have a string 5161 if (a.charAt(a.length - 1) === '%') { 5162 afunc = 'multiply'; 5163 a = parseFloat(a)/100 + 1; 5164 } 5165 } 5166 5167 else { 5168 a = parseFloat(a); 5169 afunc = 'add'; 5170 } 5171 5172 if (b !== null && isNaN(b)) { 5173 // we have a string 5174 if (b.charAt(b.length - 1) === '%') { 5175 bfunc = 'multiply'; 5176 b = parseFloat(b)/100 + 1; 5177 } 5178 } 5179 5180 else if (b !== null) { 5181 b = parseFloat(b); 5182 bfunc = 'add'; 5183 } 5184 5185 if (a !== null) { 5186 if (b === null) { 5187 b = -a; 5188 bfunc = afunc; 5189 if (bfunc === 'multiply') { 5190 b += 2; 5191 } 5192 } 5193 5194 // make sure a always applies to hi band. 5195 if (a < b) { 5196 var temp = a; 5197 a = b; 5198 b = temp; 5199 temp = afunc; 5200 afunc = bfunc; 5201 bfunc = temp; 5202 } 5203 5204 for (var i=0, l = data.length; i < l; i++) { 5205 switch (afunc) { 5206 case 'add': 5207 bands.hiData.push([data[i][0], data[i][1] + a]); 5208 break; 5209 case 'multiply': 5210 bands.hiData.push([data[i][0], data[i][1] * a]); 5211 break; 5212 } 5213 switch (bfunc) { 5214 case 'add': 5215 bands.lowData.push([data[i][0], data[i][1] + b]); 5216 break; 5217 case 'multiply': 5218 bands.lowData.push([data[i][0], data[i][1] * b]); 5219 break; 5220 } 5221 } 5222 } 5223 5224 else { 5225 bands.show = false;
5226 } 5227 } 5228 5229 var hd = bands.hiData; 5230 var ld = bands.lowData; 5231 for (var i = 0, l = hd.length; i<l; i++) { 5232 if ((hd[i][1] != null && hd[i][1] > bands._max) || bands._max == null) { 5233 bands._max = hd[i][1]; 5234 } 5235 } 5236 for (var i = 0, l = ld.length; i<l; i++) { 5237 if ((ld[i][1] != null && ld[i][1] < bands._min) || bands._min == null) { 5238 bands._min = ld[i][1]; 5239 } 5240 } 5241 5242 // one last check for proper data 5243 // these don't apply any more since allowing arbitrary x,y values 5244 // if (bands.hiData.length != bands.lowData.length) { 5245 // bands.show = false; 5246 // } 5247 5248 // if (bands.hiData.length != this.data.length) { 5249 // bands.show = false; 5250 // } 5251 5252 if (bands.fillColor === null) { 5253 var c = $.jqplot.getColorComponents(bands.color); 5254 // now adjust alpha to differentiate fill 5255 c[3] = c[3] * 0.5; 5256 bands.fillColor = 'rgba(' + c[0] +', '+ c[1] +', '+ c[2] +', '+ c[3] + ')'; 5257 } 5258 }; 5259 5260 function getSteps (d, f) { 5261 return (3.4182054+f) * Math.pow(d, -0.3534992); 5262 } 5263 5264 function computeSteps (d1, d2) { 5265 var s = Math.sqrt(Math.pow((d2[0]- d1[0]), 2) + Math.pow ((d2[1] - d1[1]), 2)); 5266 return 5.7648 * Math.log(s) + 7.4456; 5267 } 5268 5269 function tanh (x) { 5270 var a = (Math.exp(2*x) - 1) / (Math.exp(2*x) + 1); 5271 return a; 5272 } 5273 5274 ////////// 5275 // computeConstrainedSmoothedData 5276 // An implementation of the constrained cubic spline interpolation 5277 // method as presented in: 5278 // 5279 // Kruger, CJC, Constrained Cubic Spine Interpolation for Chemical Engineering Applications 5280 // http://www.korf.co.uk/spline.pdf 5281 // 5282 // The implementation below borrows heavily from the sample Visual Basic 5283 // implementation by CJC Kruger found in http://www.korf.co.uk/spline.xls 5284 // 5285 ///////// 5286 5287 // called with scope of series 5288 function computeConstrainedSmoothedData (gd) { 5289 var smooth = this.renderer.smooth; 5290 var dim = this.canvas.getWidth(); 5291 var xp = this._xaxis.series_p2u; 5292 var yp = this._yaxis.series_p2u; 5293 var steps =null; 5294 var _steps = null; 5295 var dist = gd.length/dim; 5296 var _smoothedData = []; 5297 var _smoothedPlotData = []; 5298 5299 if (!isNaN(parseFloat(smooth))) { 5300 steps = parseFloat(smooth); 5301 } 5302 else { 5303 steps = getSteps(dist, 0.5); 5304 } 5305 5306 var yy = []; 5307 var xx = []; 5308 5309 for (var i=0, l = gd.length; i<l; i++) { 5310 yy.push(gd[i][1]); 5311 xx.push(gd[i][0]); 5312 } 5313 5314 function dxx(x1, x0) { 5315 if (x1 - x0 == 0) { 5316 return Math.pow(10,10); 5317 } 5318 else { 5319 return x1 - x0; 5320 } 5321 } 5322 5323 var A, B, C, D; 5324 // loop through each line segment. Have # points - 1 line segments. Nmber segments starting at 1. 5325 var nmax = gd.length - 1; 5326 for (var num = 1, gdl = gd.length; num<gdl; num++) { 5327 var gxx = []; 5328 var ggxx = []; 5329 // point at each end of segment. 5330 for (var j = 0; j < 2; j++) { 5331 var i = num - 1 + j; // point number, 0 to # points. 5332 5333 if (i == 0 || i == nmax) { 5334 gxx[j] = Math.pow(10, 10); 5335 } 5336 else if (yy[i+1] - yy[i] == 0 || yy[i] - yy[i-1] == 0) { 5337 gxx[j] = 0; 5338 } 5339 else if (((xx[i+1] - xx[i]) / (yy[i+1] - yy[i]) + (xx[i] - xx[i-1]) / (yy[i] - yy[i-1])) == 0 ) { 5340 gxx[j] = 0; 5341 } 5342 else if ( (yy[i+1] - yy[i]) * (yy[i] - yy[i-1]) < 0 ) { 5343 gxx[j] = 0; 5344 } 5345 5346 else { 5347 gxx[j] = 2 / (dxx(xx[i + 1], xx[i]) / (yy[i + 1] - yy[i]) + dxx(xx[i], xx[i - 1]) / (yy[i] - yy[i - 1])); 5348 } 5349 } 5350 5351 // Reset first derivative (slope) at first and last point 5352 if (num == 1) { 5353 // First point has 0 2nd derivative 5354 gxx[0] = 3 / 2 * (yy[1] - yy[0]) / dxx(xx[1], xx[0]) - gxx[1] / 2; 5355 } 5356 else if (num == nmax) { 5357 // Last point has 0 2nd derivative 5358 gxx[1] = 3 / 2 * (yy[nmax] - yy[nmax - 1]) / dxx(xx[nmax], xx[nmax - 1]) - gxx[0] / 2; 5359 } 5360 5361 // Calc second derivative at points 5362 ggxx[0] = -2 * (gxx[1] + 2 * gxx[0]) / dxx(xx[num], xx[num - 1]) + 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2); 5363 ggxx[1] = 2 * (2 * gxx[1] + gxx[0]) / dxx(xx[num], xx[num - 1]) - 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2); 5364 5365 // Calc constants for cubic interpolation 5366 D = 1 / 6 * (ggxx[1] - ggxx[0]) / dxx(xx[num], xx[num - 1]); 5367 C = 1 / 2 * (xx[num] * ggxx[0] - xx[num - 1] * ggxx[1]) / dxx(xx[num], xx[num - 1]); 5368 B = (yy[num] - yy[num - 1] - C * (Math.pow(xx[num], 2) - Math.pow(xx[num - 1], 2)) - D * (Math.pow(xx[num], 3) - Math.pow(xx[num - 1], 3))) / dxx(xx[num], xx[num - 1]); 5369 A = yy[num - 1] - B * xx[num - 1] - C * Math.pow(xx[num - 1], 2) - D * Math.pow(xx[num - 1], 3); 5370 5371 var increment = (xx[num] - xx[num - 1]) / steps; 5372 var temp, tempx; 5373 5374 for (var j = 0, l = steps; j < l; j++) { 5375 temp = []; 5376 tempx = xx[num - 1] + j * increment; 5377 temp.push(tempx); 5378 temp.push(A + B * tempx + C * Math.pow(tempx, 2) + D * Math.pow(tempx, 3)); 5379 _smoothedData.push(temp); 5380 _smoothedPlotData.push([xp(temp[0]), yp(temp[1])]); 5381 } 5382 } 5383 5384 _smoothedData.push(gd[i]); 5385 _smoothedPlotData.push([xp(gd[i][0]), yp(gd[i][1])]); 5386 5387 return [_smoothedData, _smoothedPlotData]; 5388 } 5389
5390 /////// 5391 // computeHermiteSmoothedData 5392 // A hermite spline smoothing of the plot data. 5393 // This implementation is derived from the one posted 5394 // by krypin on the jqplot-users mailing list: 5395 // 5396 // http://groups.google.com/group/jqplot-users/browse_thread/thread/748be6a445723cea?pli=1 5397 // 5398 // with a blog post: 5399 // 5400 // http://blog.statscollector.com/a-plugin-renderer-for-jqplot-to-draw-a-hermite-spline/ 5401 // 5402 // and download of the original plugin: 5403 // 5404 // http://blog.statscollector.com/wp-content/uploads/2010/02/jqplot.hermiteSplineRenderer.js 5405 ////////// 5406 5407 // called with scope of series 5408 function computeHermiteSmoothedData (gd) { 5409 var smooth = this.renderer.smooth; 5410 var tension = this.renderer.tension; 5411 var dim = this.canvas.getWidth(); 5412 var xp = this._xaxis.series_p2u; 5413 var yp = this._yaxis.series_p2u; 5414 var steps =null; 5415 var _steps = null; 5416 var a = null; 5417 var a1 = null; 5418 var a2 = null; 5419 var slope = null; 5420 var slope2 = null; 5421 var temp = null; 5422 var t, s, h1, h2, h3, h4; 5423 var TiX, TiY, Ti1X, Ti1Y; 5424 var pX, pY, p; 5425 var sd = []; 5426 var spd = []; 5427 var dist = gd.length/dim; 5428 var min, max, stretch, scale, shift; 5429 var _smoothedData = []; 5430 var _smoothedPlotData = []; 5431 if (!isNaN(parseFloat(smooth))) { 5432 steps = parseFloat(smooth); 5433 } 5434 else { 5435 steps = getSteps(dist, 0.5); 5436 } 5437 if (!isNaN(parseFloat(tension))) { 5438 tension = parseFloat(tension); 5439 } 5440 5441 for (var i=0, l = gd.length-1; i < l; i++) { 5442 5443 if (tension === null) { 5444 slope = Math.abs((gd[i+1][1] - gd[i][1]) / (gd[i+1][0] - gd[i][0])); 5445 5446 min = 0.3; 5447 max = 0.6; 5448 stretch = (max - min)/2.0; 5449 scale = 2.5; 5450 shift = -1.4; 5451 5452 temp = slope/scale + shift; 5453 5454 a1 = stretch * tanh(temp) - stretch * tanh(shift) + min; 5455 5456 // if have both left and right line segments, will use minimum tension. 5457 if (i > 0) { 5458 slope2 = Math.abs((gd[i][1] - gd[i-1][1]) / (gd[i][0] - gd[i-1][0])); 5459 } 5460 temp = slope2/scale + shift; 5461 5462 a2 = stretch * tanh(temp) - stretch * tanh(shift) + min; 5463 5464 a = (a1 + a2)/2.0; 5465 5466 } 5467 else { 5468 a = tension; 5469 } 5470 for (t=0; t < steps; t++) { 5471 s = t / steps; 5472 h1 = (1 + 2*s)*Math.pow((1-s),2); 5473 h2 = s*Math.pow((1-s),2); 5474 h3 = Math.pow(s,2)*(3-2*s); 5475 h4 = Math.pow(s,2)*(s-1); 5476 5477 if (gd[i-1]) { 5478 TiX = a * (gd[i+1][0] - gd[i-1][0]); 5479 TiY = a * (gd[i+1][1] - gd[i-1][1]); 5480 } else { 5481 TiX = a * (gd[i+1][0] - gd[i][0]); 5482 TiY = a * (gd[i+1][1] - gd[i][1]); 5483 } 5484 if (gd[i+2]) { 5485 Ti1X = a * (gd[i+2][0] - gd[i][0]); 5486 Ti1Y = a * (gd[i+2][1] - gd[i][1]); 5487 } else { 5488 Ti1X = a * (gd[i+1][0] - gd[i][0]); 5489 Ti1Y = a * (gd[i+1][1] - gd[i][1]); 5490 } 5491 5492 pX = h1*gd[i][0] + h3*gd[i+1][0] + h2*TiX + h4*Ti1X; 5493 pY = h1*gd[i][1] + h3*gd[i+1][1] + h2*TiY + h4*Ti1Y; 5494 p = [pX, pY]; 5495 5496 _smoothedData.push(p); 5497 _smoothedPlotData.push([xp(pX), yp(pY)]); 5498 } 5499 } 5500 _smoothedData.push(gd[l]); 5501 _smoothedPlotData.push([xp(gd[l][0]), yp(gd[l][1])]); 5502 5503 return [_smoothedData, _smoothedPlotData]; 5504 } 5505 5506 // setGridData 5507 // converts the user data values to grid coordinates and stores them 5508 // in the gridData array. 5509 // Called with scope of a series. 5510 $.jqplot.LineRenderer.prototype.setGridData = function(plot) { 5511 // recalculate the grid data 5512 var xp = this._xaxis.series_u2p; 5513 var yp = this._yaxis.series_u2p; 5514 var data = this._plotData; 5515 var pdata = this._prevPlotData; 5516 this.gridData = []; 5517 this._prevGridData = []; 5518 this.renderer._smoothedData = []; 5519 this.renderer._smoothedPlotData = []; 5520 this.renderer._hiBandGridData = []; 5521 this.renderer._lowBandGridData = []; 5522 this.renderer._hiBandSmoothedData = []; 5523 this.renderer._lowBandSmoothedData = []; 5524 var bands = this.renderer.bands; 5525 var hasNull = false;
5526 for (var i=0, l=data.length; i < l; i++) { 5527 // if not a line series or if no nulls in data, push the converted point onto the array. 5528 if (data[i][0] != null && data[i][1] != null) { 5529 this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]); 5530 } 5531 // else if there is a null, preserve it. 5532 else if (data[i][0] == null) { 5533 hasNull = true; 5534 this.gridData.push([null, yp.call(this._yaxis, data[i][1])]); 5535 } 5536 else if (data[i][1] == null) { 5537 hasNull = true; 5538 this.gridData.push([xp.call(this._xaxis, data[i][0]), null]); 5539 } 5540 // if not a line series or if no nulls in data, push the converted point onto the array. 5541 if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) { 5542 this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]); 5543 } 5544 // else if there is a null, preserve it. 5545 else if (pdata[i] != null && pdata[i][0] == null) { 5546 this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]); 5547 } 5548 else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) { 5549 this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]); 5550 } 5551 } 5552 5553 // don't do smoothing or bands on broken lines. 5554 if (hasNull) { 5555 this.renderer.smooth = false; 5556 if (this._type === 'line') { 5557 bands.show = false; 5558 } 5559 } 5560 5561 if (this._type === 'line' && bands.show) { 5562 for (var i=0, l=bands.hiData.length; i<l; i++) { 5563 this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]); 5564 } 5565 for (var i=0, l=bands.lowData.length; i<l; i++) { 5566 this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]); 5567 } 5568 } 5569 5570 // calculate smoothed data if enough points and no nulls 5571 if (this._type === 'line' && this.renderer.smooth && this.gridData.length > 2) { 5572 var ret; 5573 if (this.renderer.constrainSmoothing) { 5574 ret = computeConstrainedSmoothedData.call(this, this.gridData); 5575 this.renderer._smoothedData = ret[0]; 5576 this.renderer._smoothedPlotData = ret[1]; 5577 5578 if (bands.show) { 5579 ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData); 5580 this.renderer._hiBandSmoothedData = ret[0]; 5581 ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData); 5582 this.renderer._lowBandSmoothedData = ret[0]; 5583 } 5584 5585 ret = null; 5586 } 5587 else { 5588 ret = computeHermiteSmoothedData.call(this, this.gridData); 5589 this.renderer._smoothedData = ret[0]; 5590 this.renderer._smoothedPlotData = ret[1]; 5591 5592 if (bands.show) { 5593 ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData); 5594 this.renderer._hiBandSmoothedData = ret[0]; 5595 ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData); 5596 this.renderer._lowBandSmoothedData = ret[0]; 5597 } 5598 5599 ret = null; 5600 } 5601 } 5602 }; 5603 5604 // makeGridData 5605 // converts any arbitrary data values to grid coordinates and 5606 // returns them. This method exists so that plugins can use a series' 5607 // linerenderer to generate grid data points without overwriting the 5608 // grid data associated with that series. 5609 // Called with scope of a series. 5610 $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) { 5611 // recalculate the grid data 5612 var xp = this._xaxis.series_u2p; 5613 var yp = this._yaxis.series_u2p; 5614 var gd = []; 5615 var pgd = []; 5616 this.renderer._smoothedData = []; 5617 this.renderer._smoothedPlotData = []; 5618 this.renderer._hiBandGridData = []; 5619 this.renderer._lowBandGridData = []; 5620 this.renderer._hiBandSmoothedData = []; 5621 this.renderer._lowBandSmoothedData = []; 5622 var bands = this.renderer.bands; 5623 var hasNull = false;
5624 for (var i=0; i<data.length; i++) { 5625 // if not a line series or if no nulls in data, push the converted point onto the array. 5626 if (data[i][0] != null && data[i][1] != null) { 5627 gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]); 5628 } 5629 // else if there is a null, preserve it. 5630 else if (data[i][0] == null) { 5631 hasNull = true; 5632 gd.push([null, yp.call(this._yaxis, data[i][1])]); 5633 } 5634 else if (data[i][1] == null) { 5635 hasNull = true; 5636 gd.push([xp.call(this._xaxis, data[i][0]), null]); 5637 } 5638 } 5639 5640 // don't do smoothing or bands on broken lines. 5641 if (hasNull) { 5642 this.renderer.smooth = false; 5643 if (this._type === 'line') { 5644 bands.show = false; 5645 } 5646 } 5647 5648 if (this._type === 'line' && bands.show) { 5649 for (var i=0, l=bands.hiData.length; i<l; i++) { 5650 this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]); 5651 } 5652 for (var i=0, l=bands.lowData.length; i<l; i++) { 5653 this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]); 5654 } 5655 } 5656 5657 if (this._type === 'line' && this.renderer.smooth && gd.length > 2) { 5658 var ret; 5659 if (this.renderer.constrainSmoothing) { 5660 ret = computeConstrainedSmoothedData.call(this, gd); 5661 this.renderer._smoothedData = ret[0]; 5662 this.renderer._smoothedPlotData = ret[1]; 5663 5664 if (bands.show) { 5665 ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData); 5666 this.renderer._hiBandSmoothedData = ret[0]; 5667 ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData); 5668 this.renderer._lowBandSmoothedData = ret[0]; 5669 } 5670 5671 ret = null; 5672 } 5673 else { 5674 ret = computeHermiteSmoothedData.call(this, gd); 5675 this.renderer._smoothedData = ret[0]; 5676 this.renderer._smoothedPlotData = ret[1]; 5677 5678 if (bands.show) { 5679 ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData); 5680 this.renderer._hiBandSmoothedData = ret[0]; 5681 ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData); 5682 this.renderer._lowBandSmoothedData = ret[0]; 5683 } 5684 5685 ret = null; 5686 } 5687 } 5688 return gd; 5689 }; 5690 5691 5692 // called within scope of series. 5693 $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options, plot) { 5694 var i; 5695 // get a copy of the options, so we don't modify the original object. 5696 var opts = $.extend(true, {}, options); 5697 var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; 5698 var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; 5699 var fill = (opts.fill != undefined) ? opts.fill : this.fill; 5700 var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke; 5701 var xmin, ymin, xmax, ymax; 5702 ctx.save(); 5703 if (gd.length) { 5704 if (showLine) { 5705 // if we fill, we'll have to add points to close the curve. 5706 if (fill) { 5707 if (this.fillToZero) { 5708 // have to break line up into shapes at axis crossings 5709 var negativeColor = this.negativeColor; 5710 if (! this.useNegativeColors) { 5711 negativeColor = opts.fillStyle; 5712 } 5713 var isnegative = false; 5714 var posfs = opts.fillStyle; 5715 5716 // if stoking line as well as filling, get a copy of line data. 5717 if (fillAndStroke) { 5718 var fasgd = gd.slice(0); 5719 } 5720 // if not stacked, fill down to axis 5721 if (this.index == 0 || !this._stack) { 5722 5723 var tempgd = []; 5724 var pd = (this.renderer.smooth) ? this.renderer._smoothedPlotData : this._plotData; 5725 this._areaPoints = []; 5726 var pyzero = this._yaxis.series_u2p(this.fillToValue); 5727 var pxzero = this._xaxis.series_u2p(this.fillToValue); 5728 5729 opts.closePath = true; 5730
5731 if (this.fillAxis == 'y') { 5732 tempgd.push([gd[0][0], pyzero]); 5733 this._areaPoints.push([gd[0][0], pyzero]); 5734 5735 for (var i=0; i<gd.length-1; i++) { 5736 tempgd.push(gd[i]); 5737 this._areaPoints.push(gd[i]); 5738 // do we have an axis crossing? 5739 if (pd[i][1] * pd[i+1][1] <= 0) { 5740 if (pd[i][1] < 0) { 5741 isnegative = true; 5742 opts.fillStyle = negativeColor; 5743 } 5744 else { 5745 isnegative = false; 5746 opts.fillStyle = posfs; 5747 } 5748 5749 var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]); 5750 tempgd.push([xintercept, pyzero]); 5751 this._areaPoints.push([xintercept, pyzero]); 5752 // now draw this shape and shadow. 5753 if (shadow) { 5754 this.renderer.shadowRenderer.draw(ctx, tempgd, opts); 5755 } 5756 this.renderer.shapeRenderer.draw(ctx, tempgd, opts); 5757 // now empty temp array and continue 5758 tempgd = [[xintercept, pyzero]]; 5759 // this._areaPoints = [[xintercept, pyzero]]; 5760 } 5761 } 5762 if (pd[gd.length-1][1] < 0) { 5763 isnegative = true; 5764 opts.fillStyle = negativeColor; 5765 } 5766 else { 5767 isnegative = false; 5768 opts.fillStyle = posfs; 5769 } 5770 tempgd.push(gd[gd.length-1]); 5771 this._areaPoints.push(gd[gd.length-1]); 5772 tempgd.push([gd[gd.length-1][0], pyzero]); 5773 this._areaPoints.push([gd[gd.length-1][0], pyzero]); 5774 } 5775 // now draw the last area. 5776 if (shadow) { 5777 this.renderer.shadowRenderer.draw(ctx, tempgd, opts); 5778 } 5779 this.renderer.shapeRenderer.draw(ctx, tempgd, opts); 5780 5781 5782 // var gridymin = this._yaxis.series_u2p(0); 5783 // // IE doesn't return new length on unshift 5784 // gd.unshift([gd[0][0], gridymin]); 5785 // len = gd.length; 5786 // gd.push([gd[len - 1][0], gridymin]); 5787 } 5788 // if stacked, fill to line below 5789 else { 5790 var prev = this._prevGridData; 5791 for (var i=prev.length; i>0; i--) { 5792 gd.push(prev[i-1]); 5793 // this._areaPoints.push(prev[i-1]); 5794 } 5795 if (shadow) { 5796 this.renderer.shadowRenderer.draw(ctx, gd, opts); 5797 } 5798 this._areaPoints = gd; 5799 this.renderer.shapeRenderer.draw(ctx, gd, opts); 5800 } 5801 }
5802 ///////////////////////// 5803 // Not filled to zero 5804 //////////////////////// 5805 else { 5806 // if stoking line as well as filling, get a copy of line data. 5807 if (fillAndStroke) { 5808 var fasgd = gd.slice(0); 5809 } 5810 // if not stacked, fill down to axis 5811 if (this.index == 0 || !this._stack) { 5812 // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2; 5813 var gridymin = ctx.canvas.height; 5814 // IE doesn't return new length on unshift 5815 gd.unshift([gd[0][0], gridymin]); 5816 var len = gd.length; 5817 gd.push([gd[len - 1][0], gridymin]); 5818 } 5819 // if stacked, fill to line below 5820 else { 5821 var prev = this._prevGridData; 5822 for (var i=prev.length; i>0; i--) { 5823 gd.push(prev[i-1]); 5824 } 5825 } 5826 this._areaPoints = gd; 5827 5828 if (shadow) { 5829 this.renderer.shadowRenderer.draw(ctx, gd, opts); 5830 } 5831 5832 this.renderer.shapeRenderer.draw(ctx, gd, opts); 5833 } 5834 if (fillAndStroke) { 5835 var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false}); 5836 this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
5837 ////////// 5838 // TODO: figure out some way to do shadows nicely 5839 // if (shadow) { 5840 // this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts); 5841 // } 5842 // now draw the markers 5843 if (this.markerRenderer.show) { 5844 if (this.renderer.smooth) { 5845 fasgd = this.gridData; 5846 } 5847 for (i=0; i<fasgd.length; i++) { 5848 this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions); 5849 } 5850 } 5851 } 5852 } 5853 else { 5854 5855 if (this.renderer.bands.show) { 5856 var bdat; 5857 var bopts = $.extend(true, {}, opts); 5858 if (this.renderer.bands.showLines) { 5859 bdat = (this.renderer.smooth) ? this.renderer._hiBandSmoothedData : this.renderer._hiBandGridData; 5860 this.renderer.shapeRenderer.draw(ctx, bdat, opts); 5861 bdat = (this.renderer.smooth) ? this.renderer._lowBandSmoothedData : this.renderer._lowBandGridData; 5862 this.renderer.shapeRenderer.draw(ctx, bdat, bopts); 5863 } 5864 5865 if (this.renderer.bands.fill) { 5866 if (this.renderer.smooth) { 5867 bdat = this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse()); 5868 } 5869 else { 5870 bdat = this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse()); 5871 } 5872 this._areaPoints = bdat; 5873 bopts.closePath = true; 5874 bopts.fill = true; 5875 bopts.fillStyle = this.renderer.bands.fillColor; 5876 this.renderer.shapeRenderer.draw(ctx, bdat, bopts); 5877 } 5878 } 5879 5880 if (shadow) { 5881 this.renderer.shadowRenderer.draw(ctx, gd, opts); 5882 } 5883 5884 this.renderer.shapeRenderer.draw(ctx, gd, opts); 5885 } 5886 } 5887 // calculate the bounding box 5888 var xmin = xmax = ymin = ymax = null; 5889 for (i=0; i<this._areaPoints.length; i++) { 5890 var p = this._areaPoints[i]; 5891 if (xmin > p[0] || xmin == null) { 5892 xmin = p[0]; 5893 } 5894 if (ymax < p[1] || ymax == null) { 5895 ymax = p[1]; 5896 } 5897 if (xmax < p[0] || xmax == null) { 5898 xmax = p[0]; 5899 } 5900 if (ymin > p[1] || ymin == null) { 5901 ymin = p[1]; 5902 } 5903 } 5904 5905 if (this.type === 'line' && this.renderer.bands.show) { 5906 ymax = this._yaxis.series_u2p(this.renderer.bands._min); 5907 ymin = this._yaxis.series_u2p(this.renderer.bands._max); 5908 } 5909 5910 this._boundingBox = [[xmin, ymax], [xmax, ymin]]; 5911 5912 // now draw the markers 5913 if (this.markerRenderer.show && !fill) { 5914 if (this.renderer.smooth) { 5915 gd = this.gridData; 5916 } 5917 for (i=0; i<gd.length; i++) { 5918 if (gd[i][0] != null && gd[i][1] != null) { 5919 this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions); 5920 } 5921 } 5922 } 5923 } 5924 5925 ctx.restore(); 5926 }; 5927 5928 $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) { 5929 // This is a no-op, shadows drawn with lines. 5930 }; 5931 5932 // called with scope of plot. 5933 // make sure to not leave anything highlighted. 5934 function postInit(target, data, options) { 5935 for (var i=0; i<this.series.length; i++) { 5936 if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) { 5937 // don't allow mouseover and mousedown at same time. 5938 if (this.series[i].highlightMouseOver) { 5939 this.series[i].highlightMouseDown = false;
5940 } 5941 } 5942 } 5943 } 5944 5945 // called within context of plot 5946 // create a canvas which we can draw on. 5947 // insert it before the eventCanvas, so eventCanvas will still capture events. 5948 function postPlotDraw() { 5949 // Memory Leaks patch 5950 if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) { 5951 this.plugins.lineRenderer.highlightCanvas.resetCanvas(); 5952 this.plugins.lineRenderer.highlightCanvas = null; 5953 } 5954 5955 this.plugins.lineRenderer.highlightedSeriesIndex = null; 5956 this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); 5957 5958 this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions, this)); 5959 this.plugins.lineRenderer.highlightCanvas.setContext(); 5960 this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); 5961 } 5962 5963 function highlight (plot, sidx, pidx, points) { 5964 var s = plot.series[sidx]; 5965 var canvas = plot.plugins.lineRenderer.highlightCanvas; 5966 canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); 5967 s._highlightedPoint = pidx; 5968 plot.plugins.lineRenderer.highlightedSeriesIndex = sidx; 5969 var opts = {fillStyle: s.highlightColor}; 5970 if (s.type === 'line' && s.renderer.bands.show) { 5971 opts.fill = true; 5972 opts.closePath = true; 5973 } 5974 s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); 5975 canvas = null; 5976 } 5977 5978 function unhighlight (plot) { 5979 var canvas = plot.plugins.lineRenderer.highlightCanvas; 5980 canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); 5981 for (var i=0; i<plot.series.length; i++) { 5982 plot.series[i]._highlightedPoint = null; 5983 } 5984 plot.plugins.lineRenderer.highlightedSeriesIndex = null; 5985 plot.target.trigger('jqplotDataUnhighlight'); 5986 canvas = null; 5987 } 5988 5989 5990 function handleMove(ev, gridpos, datapos, neighbor, plot) { 5991 if (neighbor) { 5992 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 5993 var evt1 = jQuery.Event('jqplotDataMouseOver'); 5994 evt1.pageX = ev.pageX; 5995 evt1.pageY = ev.pageY; 5996 plot.target.trigger(evt1, ins); 5997 if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) { 5998 var evt = jQuery.Event('jqplotDataHighlight'); 5999 evt.which = ev.which; 6000 evt.pageX = ev.pageX; 6001 evt.pageY = ev.pageY; 6002 plot.target.trigger(evt, ins); 6003 highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); 6004 } 6005 } 6006 else if (neighbor == null) { 6007 unhighlight (plot); 6008 } 6009 } 6010 6011 function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { 6012 if (neighbor) { 6013 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 6014 if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) { 6015 var evt = jQuery.Event('jqplotDataHighlight'); 6016 evt.which = ev.which; 6017 evt.pageX = ev.pageX; 6018 evt.pageY = ev.pageY; 6019 plot.target.trigger(evt, ins); 6020 highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); 6021 } 6022 } 6023 else if (neighbor == null) { 6024 unhighlight (plot); 6025 } 6026 } 6027 6028 function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { 6029 var idx = plot.plugins.lineRenderer.highlightedSeriesIndex; 6030 if (idx != null && plot.series[idx].highlightMouseDown) { 6031 unhighlight(plot); 6032 } 6033 } 6034 6035 function handleClick(ev, gridpos, datapos, neighbor, plot) { 6036 if (neighbor) { 6037 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 6038 var evt = jQuery.Event('jqplotDataClick'); 6039 evt.which = ev.which; 6040 evt.pageX = ev.pageX; 6041 evt.pageY = ev.pageY; 6042 plot.target.trigger(evt, ins); 6043 } 6044 } 6045 6046 function handleRightClick(ev, gridpos, datapos, neighbor, plot) { 6047 if (neighbor) { 6048 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 6049 var idx = plot.plugins.lineRenderer.highlightedSeriesIndex; 6050 if (idx != null && plot.series[idx].highlightMouseDown) { 6051 unhighlight(plot); 6052 } 6053 var evt = jQuery.Event('jqplotDataRightClick'); 6054 evt.which = ev.which; 6055 evt.pageX = ev.pageX; 6056 evt.pageY = ev.pageY; 6057 plot.target.trigger(evt, ins); 6058 } 6059 } 6060 6061 6062 // class: $.jqplot.LinearAxisRenderer 6063 // The default jqPlot axis renderer, creating a numeric axis. 6064 $.jqplot.LinearAxisRenderer = function() { 6065 }; 6066 6067 // called with scope of axis object. 6068 $.jqplot.LinearAxisRenderer.prototype.init = function(options){ 6069 // prop: breakPoints 6070 // EXPERIMENTAL!! Use at your own risk! 6071 // Works only with linear axes and the default tick renderer. 6072 // Array of [start, stop] points to create a broken axis. 6073 // Broken axes have a "jump" in them, which is an immediate 6074 // transition from a smaller value to a larger value. 6075 // Currently, axis ticks MUST be manually assigned if using breakPoints 6076 // by using the axis ticks array option. 6077 this.breakPoints = null; 6078 // prop: breakTickLabel 6079 // Label to use at the axis break if breakPoints are specified. 6080 this.breakTickLabel = "≈"; 6081 // prop: drawBaseline 6082 // True to draw the axis baseline. 6083 this.drawBaseline = true; 6084 // prop: baselineWidth 6085 // width of the baseline in pixels. 6086 this.baselineWidth = null; 6087 // prop: baselineColor 6088 // CSS color spec for the baseline. 6089 this.baselineColor = null; 6090 // prop: forceTickAt0 6091 // This will ensure that there is always a tick mark at 0. 6092 // If data range is strictly positive or negative,
6093 // this will force 0 to be inside the axis bounds unless 6094 // the appropriate axis pad (pad, padMin or padMax) is set 6095 // to 0, then this will force an axis min or max value at 0. 6096 // This has know effect when any of the following options 6097 // are set: autoscale, min, max, numberTicks or tickInterval. 6098 this.forceTickAt0 = false; 6099 // prop: forceTickAt100 6100 // This will ensure that there is always a tick mark at 100. 6101 // If data range is strictly above or below 100, 6102 // this will force 100 to be inside the axis bounds unless 6103 // the appropriate axis pad (pad, padMin or padMax) is set 6104 // to 0, then this will force an axis min or max value at 100. 6105 // This has know effect when any of the following options 6106 // are set: autoscale, min, max, numberTicks or tickInterval. 6107 this.forceTickAt100 = false; 6108 // prop: tickInset 6109 // Controls the amount to inset the first and last ticks from 6110 // the edges of the grid, in multiples of the tick interval. 6111 // 0 is no inset, 0.5 is one half a tick interval, 1 is a full 6112 // tick interval, etc. 6113 this.tickInset = 0; 6114 // prop: minorTicks 6115 // Number of ticks to add between "major" ticks. 6116 // Major ticks are ticks supplied by user or auto computed. 6117 // Minor ticks cannot be created by user. 6118 this.minorTicks = 0; 6119 // prop: alignTicks 6120 // true to align tick marks across opposed axes 6121 // such as from the y2axis to yaxis. 6122 this.alignTicks = false; 6123 this._autoFormatString = ''; 6124 this._overrideFormatString = false; 6125 this._scalefact = 1.0; 6126 $.extend(true, this, options); 6127 if (this.breakPoints) { 6128 if (!$.isArray(this.breakPoints)) { 6129 this.breakPoints = null; 6130 } 6131 else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) { 6132 this.breakPoints = null; 6133 } 6134 } 6135 if (this.numberTicks != null && this.numberTicks < 2) { 6136 this.numberTicks = 2; 6137 } 6138 this.resetDataBounds(); 6139 }; 6140 6141 // called with scope of axis 6142 $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) { 6143 if (this.show) { 6144 // populate the axis label and value properties. 6145 // createTicks is a method on the renderer, but 6146 // call it within the scope of the axis. 6147 this.renderer.createTicks.call(this, plot); 6148 // fill a div with axes labels in the right direction. 6149 // Need to pregenerate each axis to get its bounds and 6150 // position it and the labels correctly on the plot. 6151 var dim=0; 6152 var temp; 6153 // Added for theming. 6154 if (this._elem) { 6155 // Memory Leaks patch 6156 //this._elem.empty(); 6157 this._elem.emptyForce(); 6158 this._elem = null; 6159 } 6160 6161 this._elem = $(document.createElement('div')); 6162 this._elem.addClass('jqplot-axis jqplot-'+this.name); 6163 this._elem.css('position', 'absolute'); 6164 6165 6166 if (this.name == 'xaxis' || this.name == 'x2axis') { 6167 this._elem.width(this._plotDimensions.width); 6168 } 6169 else { 6170 this._elem.height(this._plotDimensions.height); 6171 } 6172 6173 // create a _label object. 6174 this.labelOptions.axis = this.name; 6175 this._label = new this.labelRenderer(this.labelOptions); 6176 if (this._label.show) { 6177 var elem = this._label.draw(ctx, plot); 6178 elem.appendTo(this._elem); 6179 elem = null; 6180 } 6181 6182 var t = this._ticks; 6183 var tick; 6184 for (var i=0; i<t.length; i++) { 6185 tick = t[i]; 6186 if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { 6187 this._elem.append(tick.draw(ctx, plot)); 6188 } 6189 } 6190 tick = null; 6191 t = null; 6192 } 6193 return this._elem; 6194 }; 6195 6196 // called with scope of an axis 6197 $.jqplot.LinearAxisRenderer.prototype.reset = function() { 6198 this.min = this._options.min; 6199 this.max = this._options.max; 6200 this.tickInterval = this._options.tickInterval; 6201 this.numberTicks = this._options.numberTicks; 6202 this._autoFormatString = ''; 6203 if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) { 6204 this.tickOptions.formatString = ''; 6205 } 6206 6207 // this._ticks = this.__ticks; 6208 }; 6209 6210 // called with scope of axis 6211 $.jqplot.LinearAxisRenderer.prototype.set = function() { 6212 var dim = 0; 6213 var temp; 6214 var w = 0; 6215 var h = 0; 6216 var lshow = (this._label == null) ? false : this._label.show; 6217 if (this.show) { 6218 var t = this._ticks; 6219 var tick; 6220 for (var i=0; i<t.length; i++) { 6221 tick = t[i]; 6222 if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { 6223 if (this.name == 'xaxis' || this.name == 'x2axis') { 6224 temp = tick._elem.outerHeight(true); 6225 } 6226 else { 6227 temp = tick._elem.outerWidth(true); 6228 } 6229 if (temp > dim) { 6230 dim = temp; 6231 } 6232 } 6233 } 6234 tick = null; 6235 t = null; 6236 6237 if (lshow) { 6238 w = this._label._elem.outerWidth(true); 6239 h = this._label._elem.outerHeight(true); 6240 } 6241 if (this.name == 'xaxis') { 6242 dim = dim + h; 6243 this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); 6244 } 6245 else if (this.name == 'x2axis') { 6246 dim = dim + h; 6247 this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); 6248 } 6249 else if (this.name == 'yaxis') { 6250 dim = dim + w; 6251 this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); 6252 if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { 6253 this._label._elem.css('width', w+'px'); 6254 } 6255 } 6256 else { 6257 dim = dim + w; 6258 this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); 6259 if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { 6260 this._label._elem.css('width', w+'px'); 6261 } 6262 } 6263 } 6264 }; 6265 6266 // called with scope of axis 6267 $.jqplot.LinearAxisRenderer.prototype.createTicks = function(plot) { 6268 // we're are operating on an axis here 6269 var ticks = this._ticks; 6270 var userTicks = this.ticks; 6271 var name = this.name; 6272 // databounds were set on axis initialization. 6273 var db = this._dataBounds; 6274 var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; 6275 var interval; 6276 var min, max; 6277 var pos1, pos2; 6278 var tt, i; 6279 // get a copy of user's settings for min/max. 6280 var userMin = this.min; 6281 var userMax = this.max; 6282 var userNT = this.numberTicks; 6283 var userTI = this.tickInterval; 6284 6285 var threshold = 30; 6286 this._scalefact = (Math.max(dim, threshold+1) - threshold)/300.0; 6287 6288 // if we already have ticks, use them. 6289 // ticks must be in order of increasing value. 6290 6291 if (userTicks.length) { 6292 // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed 6293 for (i=0; i<userTicks.length; i++){ 6294 var ut = userTicks[i]; 6295 var t = new this.tickRenderer(this.tickOptions); 6296 if ($.isArray(ut)) { 6297 t.value = ut[0]; 6298 if (this.breakPoints) { 6299 if (ut[0] == this.breakPoints[0]) { 6300 t.label = this.breakTickLabel; 6301 t._breakTick = true; 6302 t.showGridline = false;
6303 t.showMark = false; 6304 } 6305 else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) { 6306 t.show = false; 6307 t.showGridline = false; 6308 t.label = ut[1]; 6309 } 6310 else { 6311 t.label = ut[1]; 6312 } 6313 } 6314 else { 6315 t.label = ut[1]; 6316 } 6317 t.setTick(ut[0], this.name); 6318 this._ticks.push(t); 6319 } 6320 6321 else if ($.isPlainObject(ut)) { 6322 $.extend(true, t, ut); 6323 t.axis = this.name; 6324 this._ticks.push(t); 6325 } 6326 6327 else { 6328 t.value = ut; 6329 if (this.breakPoints) { 6330 if (ut == this.breakPoints[0]) { 6331 t.label = this.breakTickLabel; 6332 t._breakTick = true; 6333 t.showGridline = false; 6334 t.showMark = false; 6335 } 6336 else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) { 6337 t.show = false; 6338 t.showGridline = false; 6339 } 6340 } 6341 t.setTick(ut, this.name); 6342 this._ticks.push(t); 6343 } 6344 } 6345 this.numberTicks = userTicks.length; 6346 this.min = this._ticks[0].value; 6347 this.max = this._ticks[this.numberTicks-1].value; 6348 this.tickInterval = (this.max - this.min) / (this.numberTicks - 1); 6349 } 6350 6351 // we don't have any ticks yet, let's make some! 6352 else { 6353 if (name == 'xaxis' || name == 'x2axis') { 6354 dim = this._plotDimensions.width; 6355 } 6356 else { 6357 dim = this._plotDimensions.height; 6358 } 6359 6360 var _numberTicks = this.numberTicks; 6361 6362 // if aligning this axis, use number of ticks from previous axis. 6363 // Do I need to reset somehow if alignTicks is changed and then graph is replotted?? 6364 if (this.alignTicks) { 6365 if (this.name === 'x2axis' && plot.axes.xaxis.show) { 6366 _numberTicks = plot.axes.xaxis.numberTicks; 6367 } 6368 else if (this.name.charAt(0) === 'y' && this.name !== 'yaxis' && this.name !== 'yMidAxis' && plot.axes.yaxis.show) { 6369 _numberTicks = plot.axes.yaxis.numberTicks; 6370 } 6371 } 6372 6373 min = ((this.min != null) ? this.min : db.min); 6374 max = ((this.max != null) ? this.max : db.max); 6375 6376 var range = max - min; 6377 var rmin, rmax; 6378 var temp; 6379 6380 if (this.tickOptions == null || !this.tickOptions.formatString) { 6381 this._overrideFormatString = true; 6382 } 6383 6384 // Doing complete autoscaling 6385 if (this.min == null || this.max == null && this.tickInterval == null && !this.autoscale) { 6386 // Check if user must have tick at 0 or 100 and ensure they are in range. 6387 // The autoscaling algorithm will always place ticks at 0 and 100 if they are in range. 6388 if (this.forceTickAt0) { 6389 if (min > 0) { 6390 min = 0; 6391 } 6392 if (max < 0) { 6393 max = 0; 6394 } 6395 } 6396 6397 if (this.forceTickAt100) { 6398 if (min > 100) { 6399 min = 100; 6400 } 6401 if (max < 100) { 6402 max = 100; 6403 } 6404 } 6405 6406 var keepMin = false, 6407 keepMax = false; 6408 6409 if (this.min != null) { 6410 keepMin = true; 6411 } 6412 6413 else if (this.max != null) { 6414 keepMax = true; 6415 } 6416 6417 // var threshold = 30; 6418 // var tdim = Math.max(dim, threshold+1); 6419 // this._scalefact = (tdim-threshold)/300.0; 6420 var ret = $.jqplot.LinearTickGenerator(min, max, this._scalefact, _numberTicks, keepMin, keepMax); 6421 // calculate a padded max and min, points should be less than these 6422 // so that they aren't too close to the edges of the plot. 6423 // User can adjust how much padding is allowed with pad, padMin and PadMax options. 6424 // If min or max is set, don't pad that end of axis. 6425 var tumin = (this.min != null) ? min : min + range*(this.padMin - 1); 6426 var tumax = (this.max != null) ? max : max - range*(this.padMax - 1); 6427 6428 // if they're equal, we shouldn't have to do anything, r
6428ight? 6429 // if (min <=tumin || max >= tumax) { 6430 if (min <tumin || max > tumax) { 6431 tumin = (this.min != null) ? min : min - range*(this.padMin - 1); 6432 tumax = (this.max != null) ? max : max + range*(this.padMax - 1); 6433 ret = $.jqplot.LinearTickGenerator(tumin, tumax, this._scalefact, _numberTicks, keepMin, keepMax); 6434 } 6435 6436 this.min = ret[0]; 6437 this.max = ret[1]; 6438 // if numberTicks specified, it should return the same. 6439 this.numberTicks = ret[2]; 6440 this._autoFormatString = ret[3]; 6441 this.tickInterval = ret[4]; 6442 } 6443 6444 // User has specified some axis scale related option, can use auto algorithm 6445 else { 6446 6447 // if min and max are same, space them out a bit 6448 if (min == max) { 6449 var adj = 0.05; 6450 if (min > 0) { 6451 adj = Math.max(Math.log(min)/Math.LN10, 0.05); 6452 } 6453 min -= adj; 6454 max += adj; 6455 } 6456 6457 // autoscale. Can't autoscale if min or max is supplied.
6458 // Will use numberTicks and tickInterval if supplied. Ticks 6459 // across multiple axes may not line up depending on how 6460 // bars are to be plotted. 6461 if (this.autoscale && this.min == null && this.max == null) { 6462 var rrange, ti, margin; 6463 var forceMinZero = false; 6464 var forceZeroLine = false; 6465 var intervals = {min:null, max:null, average:null, stddev:null}; 6466 // if any series are bars, or if any are fill to zero, and if this 6467 // is the axis to fill toward, check to see if we can start axis at zero. 6468 for (var i=0; i<this._series.length; i++) { 6469 var s = this._series[i]; 6470 var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name; 6471 // check to see if this is the fill axis 6472 if (this.name == faname) { 6473 var vals = s._plotValues[s.fillAxis]; 6474 var vmin = vals[0]; 6475 var vmax = vals[0]; 6476 for (var j=1; j<vals.length; j++) { 6477 if (vals[j] < vmin) { 6478 vmin = vals[j]; 6479 } 6480 else if (vals[j] > vmax) { 6481 vmax = vals[j]; 6482 } 6483 } 6484 var dp = (vmax - vmin) / vmax; 6485 // is this sries a bar? 6486 if (s.renderer.constructor == $.jqplot.BarRenderer) { 6487 // if no negative values and could also check range. 6488 if (vmin >= 0 && (s.fillToZero || dp > 0.1)) { 6489 forceMinZero = true; 6490 } 6491 else { 6492 forceMinZero = false; 6493 if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) { 6494 forceZeroLine = true; 6495 } 6496 else { 6497 forceZeroLine = false; 6498 } 6499 } 6500 } 6501 6502 // if not a bar and filling, use appropriate method. 6503 else if (s.fill) { 6504 if (vmin >= 0 && (s.fillToZero || dp > 0.1)) { 6505 forceMinZero = true; 6506 } 6507 else if (vmin < 0 && vmax > 0 && s.fillToZero) { 6508 forceMinZero = false; 6509 forceZeroLine = true; 6510 } 6511 else { 6512 forceMinZero = false; 6513 forceZeroLine = false; 6514 } 6515 } 6516 6517 // if not a bar and not filling, only change existing state 6518 // if it doesn't make sense 6519 else if (vmin < 0) { 6520 forceMinZero = false; 6521 } 6522 } 6523 } 6524 6525 // check if we need make axis min at 0. 6526 if (forceMinZero) { 6527 // compute number of ticks 6528 this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); 6529 this.min = 0; 6530 userMin = 0; 6531 // what order is this range? 6532 // what tick interval does that give us? 6533 ti = max/(this.numberTicks-1); 6534 temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); 6535 if (ti/temp == parseInt(ti/temp, 10)) { 6536 ti += temp; 6537 }
6538 this.tickInterval = Math.ceil(ti/temp) * temp; 6539 this.max = this.tickInterval * (this.numberTicks - 1); 6540 } 6541 6542 // check if we need to make sure there is a tick at 0. 6543 else if (forceZeroLine) { 6544 // compute number of ticks 6545 this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); 6546 var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1)); 6547 var ntmax = this.numberTicks - 1 - ntmin; 6548 ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax)); 6549 temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); 6550 this.tickInterval = Math.ceil(ti/temp) * temp; 6551 this.max = this.tickInterval * ntmax; 6552 this.min = -this.tickInterval * ntmin; 6553 } 6554 6555 // if nothing else, do autoscaling which will try to line up ticks across axes. 6556 else { 6557 if (this.numberTicks == null){ 6558 if (this.tickInterval) { 6559 this.numberTicks = 3 + Math.ceil(range / this.tickInterval); 6560 } 6561 else { 6562 this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); 6563 } 6564 } 6565 6566 if (this.tickInterval == null) { 6567 // get a tick interval 6568 ti = range/(this.numberTicks - 1); 6569 6570 if (ti < 1) { 6571 temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); 6572 } 6573 else { 6574 temp = 1; 6575 } 6576 this.tickInterval = Math.ceil(ti*temp*this.pad)/temp; 6577 } 6578 else { 6579 temp = 1 / this.tickInterval; 6580 } 6581 6582 // try to compute a nicer, more even tick interval 6583 // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10)); 6584 // this.tickInterval = Math.ceil(ti/temp) * temp; 6585 rrange = this.tickInterval * (this.numberTicks - 1); 6586 margin = (rrange - range)/2; 6587 6588 if (this.min == null) { 6589 this.min = Math.floor(temp*(min-margin))/temp; 6590 } 6591 if (this.max == null) { 6592 this.max = this.min + rrange; 6593 } 6594 } 6595 6596 // Compute a somewhat decent format string if it is needed. 6597 // get precision of interval and determine a format string. 6598 var sf = $.jqplot.getSignificantFigures(this.tickInterval); 6599 6600 var fstr; 6601 6602 // if we have only a whole number, use integer formatting 6603 if (sf.digitsLeft >= sf.significantDigits) { 6604 fstr = '%d'; 6605 } 6606 6607 else { 6608 var temp = Math.max(0, 5 - sf.digitsLeft); 6609 temp = Math.min(temp, sf.digitsRight); 6610 fstr = '%.'+ temp + 'f'; 6611 } 6612 6613 this._autoFormatString = fstr; 6614 } 6615 6616 // Use the default algorithm which pads each axis to make the chart 6617 // centered nicely on the grid. 6618 else { 6619 6620 rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1); 6621 rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1); 6622 range = rmax - rmin; 6623 6624 if (this.numberTicks == null){ 6625 // if tickInterval is specified by user, we will ignore computed maximum. 6626 // max will be equal or greater to fit even # of ticks.
6627 if (this.tickInterval != null) { 6628 this.numberTicks = Math.ceil((rmax - rmin)/this.tickInterval)+1; 6629 } 6630 else if (dim > 100) { 6631 this.numberTicks = parseInt(3+(dim-100)/75, 10); 6632 } 6633 else { 6634 this.numberTicks = 2; 6635 } 6636 } 6637 6638 if (this.tickInterval == null) { 6639 this.tickInterval = range / (this.numberTicks-1); 6640 } 6641 6642 if (this.max == null) { 6643 rmax = rmin + this.tickInterval*(this.numberTicks - 1); 6644 } 6645 if (this.min == null) { 6646 rmin = rmax - this.tickInterval*(this.numberTicks - 1); 6647 } 6648 6649 // get precision of interval and determine a format string. 6650 var sf = $.jqplot.getSignificantFigures(this.tickInterval); 6651 6652 var fstr; 6653 6654 // if we have only a whole number, use integer formatting 6655 if (sf.digitsLeft >= sf.significantDigits) { 6656 fstr = '%d'; 6657 } 6658 6659 else { 6660 var temp = Math.max(0, 5 - sf.digitsLeft); 6661 temp = Math.min(temp, sf.digitsRight); 6662 fstr = '%.'+ temp + 'f'; 6663 } 6664 6665 6666 this._autoFormatString = fstr; 6667 6668 this.min = rmin; 6669 this.max = rmax; 6670 } 6671 6672 if (this.renderer.constructor == $.jqplot.LinearAxisRenderer && this._autoFormatString == '') { 6673 // fix for misleading tick display with small range and low precision. 6674 range = this.max - this.min; 6675 // figure out precision 6676 var temptick = new this.tickRenderer(this.tickOptions); 6677 // use the tick formatString or, the default. 6678 var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString; 6679 var fs = fs.match($.jqplot.sprintf.regex)[0]; 6680 var precision = 0; 6681 if (fs) { 6682 if (fs.search(/[fFeEgGpP]/) > -1) { 6683 var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/); 6684 if (m) { 6685 precision = parseInt(m[1], 10); 6686 } 6687 else { 6688 precision = 6; 6689 } 6690 } 6691 else if (fs.search(/[di]/) > -1) { 6692 precision = 0; 6693 } 6694 // fact will be <= 1; 6695 var fact = Math.pow(10, -precision); 6696 if (this.tickInterval < fact) { 6697 // need to correct underrange 6698 if (userNT == null && userTI == null) { 6699 this.tickInterval = fact; 6700 if (userMax == null && userMin == null) { 6701 // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact; 6702 this.min = Math.floor(this._dataBounds.min/fact) * fact; 6703 if (this.min == this._dataBounds.min) { 6704 this.min = this._dataBounds.min - this.tickInterval; 6705 } 6706 // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact; 6707 this.max = Math.ceil(this._dataBounds.max/fact) * fact; 6708 if (this.max == this._dataBounds.max) { 6709 this.max = this._dataBounds.max + this.tickInterval; 6710 } 6711 var n = (this.max - this.min)/this.tickInterval; 6712 n = n.toFixed(11); 6713 n = Math.ceil(n); 6714 this.numberTicks = n + 1; 6715 } 6716 else if (userMax == null) { 6717 // add one tick for top of range. 6718 var n = (this._dataBounds.max - this.min) / this.tickInterval; 6719 n = n.toFixed(11); 6720 this.numberTicks = Math.ceil(n) + 2; 6721 this.max = this.min + this.tickInterval * (this.numberTicks-1); 6722 } 6723 else if (userMin == null) { 6724 // add one tick for bottom of range. 6725 var n = (this.max - this._dataBounds.min) / this.tickInterval; 6726 n = n.toFixed(11); 6727 this.numberTicks = Math.ceil(n) + 2; 6728 this.min = this.max - this.tickInterval * (this.numberTicks-1); 6729 } 6730 else { 6731 // calculate a number of ticks so max is within axis scale 6732 this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1; 6733 // if user's min and max don't fit evenly in ticks, adju
6733st. 6734 // This takes care of cases such as user min set to 0, max set to 3.5 but tick 6735 // format string set to %d (integer ticks) 6736 this.min = Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision); 6737 this.max = Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision); 6738 // this.max = this.min + this.tickInterval*(this.numberTicks-1); 6739 this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1; 6740 } 6741 } 6742 } 6743 } 6744 } 6745 6746 } 6747 6748 if (this._overrideFormatString && this._autoFormatString != '') { 6749 this.tickOptions = this.tickOptions || {}; 6750 this.tickOptions.formatString = this._autoFormatString; 6751 } 6752 6753 var t, to; 6754 for (var i=0; i<this.numberTicks; i++){ 6755 tt = this.min + i * this.tickInterval; 6756 t = new this.tickRenderer(this.tickOptions); 6757 // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); 6758 6759 t.setTick(tt, this.name); 6760 this._ticks.push(t); 6761 6762 if (i < this.numberTicks - 1) { 6763 for (var j=0; j<this.minorTicks; j++) { 6764 tt += this.tickInterval/(this.minorTicks+1); 6765 to = $.extend(true, {}, this.tickOptions, {name:this.name, value:tt, label:'', isMinorTick:true}); 6766 t = new this.tickRenderer(to); 6767 this._ticks.push(t); 6768 } 6769 } 6770 t = null; 6771 } 6772 } 6773 6774 if (this.tickInset) { 6775 this.min = this.min - this.tickInset * this.tickInterval; 6776 this.max = this.max + this.tickInset * this.tickInterval; 6777 } 6778 6779 ticks = null; 6780 }; 6781 6782 // Used to reset just the values of the ticks and then repack, which will 6783 // recalculate the positioning functions. It is assuemd that the 6784 // number of ticks is the same and the values of the new array are at the 6785 // proper interval. 6786 // This method needs to be called with the scope of an axis object, like: 6787 // 6788 // > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr); 6789 // 6790 $.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) { 6791 if ($.isArray(opts) && opts.length == this._ticks.length) { 6792 var t; 6793 for (var i=0; i<opts.length; i++) { 6794 t = this._ticks[i]; 6795 t.value = opts[i]; 6796 t.label = t.formatter(t.formatString, opts[i]); 6797 t.label = t.prefix + t.label; 6798 t._elem.html(t.label); 6799 } 6800 t = null; 6801 this.min = $.jqplot.arrayMin(opts); 6802 this.max = $.jqplot.arrayMax(opts); 6803 this.pack(); 6804 } 6805 // Not implemented yet. 6806 // else if ($.isPlainObject(opts)) { 6807 // 6808 // } 6809 }; 6810 6811 // called with scope of axis 6812 $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) { 6813 // Add defaults for repacking from resetTickValues function. 6814 pos = pos || {}; 6815 offsets = offsets || this._offsets; 6816 6817 var ticks = this._ticks; 6818 var max = this.max; 6819 var min = this.min; 6820 var offmax = offsets.max; 6821 var offmin = offsets.min; 6822 var lshow = (this._label == null) ? false : this._label.show; 6823 6824 for (var p in pos) { 6825 this._elem.css(p, pos[p]); 6826 } 6827 6828 this._offsets = offsets; 6829 // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. 6830 var pixellength = offmax - offmin; 6831 var unitlength = max - min; 6832 6833 // point to unit and unit to point conversions references to Plot DOM element top left corner. 6834 if (this.breakPoints) { 6835 unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0]; 6836 6837 this.p2u = function(p){ 6838 return (p - offmin) * unitlength / pixellength + min; 6839 }; 6840 6841 this.u2p = function(u){ 6842 if (u > this.breakPoints[0] && u < this.breakPoints[1]){ 6843 u = this.breakPoints[0]; 6844 } 6845 if (u <= this.breakPoints[0]) { 6846 return (u - min) * pixellength / unitlength + offmin; 6847 } 6848 else { 6849 return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin; 6850 } 6851 }; 6852 6853 if (this.name.charAt(0) == 'x'){ 6854 this.series_u2p = function(u){ 6855 if (u > this.breakPoints[0] && u < this.breakPoints[1]){ 6856 u = this.breakPoints[0]; 6857 } 6858 if (u <= this.breakPoints[0]) { 6859 return (u - min) * pixellength / unitlength; 6860 } 6861 else { 6862 return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength; 6863 } 6864 }; 6865 this.series_p2u = function(p){ 6866 return p * unitlength / pixellength + min; 6867 }; 6868 } 6869 6870 else { 6871 this.series_u2p = function(u){ 6872 if (u > this.breakPoints[0] && u < this.breakPoints[1]){ 6873 u = this.breakPoints[0]; 6874 } 6875 if (u >= this.breakPoints[1]) { 6876 return (u - max) * pixellength / unitlength; 6877 } 6878 else { 6879 return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength; 6880 } 6881 }; 6882 this.series_p2u = function(p){ 6883 return p * unitlength / pixellength + max; 6884 }; 6885 } 6886 } 6887 else { 6888 this.p2u = function(p){ 6889 return (p - offmin) * unitlength / pixellength + min; 6890 }; 6891 6892 this.u2p = function(u){ 6893 return (u - min) * pixellength / unitlength + offmin; 6894 }; 6895 6896 if (this.name == 'xaxis' || this.name == 'x2axis'){ 6897 this.series_u2p = function(u){ 6898 return (u - min) * pixellength / unitlength; 6899 }; 6900 this.series_p2u = function(p){ 6901 return p * unitlength / pixellength + min; 6902 }; 6903 } 6904 6905 else { 6906 this.series_u2p = function(u){ 6907 return (u - max) * pixellength / unitlength; 6908 }; 6909 this.series_p2u = function(p){ 6910 return p * unitlength / pixellength + max; 6911 }; 6912 } 6913 } 6914 6915 if (this.show) { 6916 if (this.name == 'xaxis' || this.name == 'x2axis') { 6917 for (var i=0; i<ticks.length; i++) { 6918 var t = ticks[i]; 6919 if (t.show && t.showLabel) { 6920 var shim; 6921 6922 if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { 6923 // will need to adjust auto positioning based on which axis this is. 6924 var temp = (this.name == 'xaxis') ? 1 : -1; 6925 switch (t.labelPosition) { 6926 case 'auto': 6927 // position at end 6928 if (temp * t.angle < 0) {
6929 shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 6930 } 6931 // position at start 6932 else { 6933 shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; 6934 } 6935 break; 6936 case 'end': 6937 shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 6938 break; 6939 case 'start': 6940 shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; 6941 break; 6942 case 'middle': 6943 shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 6944 break; 6945 default: 6946 shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 6947 break; 6948 } 6949 } 6950 else { 6951 shim = -t.getWidth()/2; 6952 } 6953 var val = this.u2p(t.value) + shim + 'px'; 6954 t._elem.css('left', val); 6955 t.pack(); 6956 } 6957 } 6958 if (lshow) { 6959 var w = this._label._elem.outerWidth(true); 6960 this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); 6961 if (this.name == 'xaxis') { 6962 this._label._elem.css('bottom', '0px'); 6963 } 6964 else { 6965 this._label._elem.css('top', '0px'); 6966 } 6967 this._label.pack(); 6968 } 6969 } 6970 else { 6971 for (var i=0; i<ticks.length; i++) { 6972 var t = ticks[i]; 6973 if (t.show && t.showLabel) { 6974 var shim; 6975 if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { 6976 // will need to adjust auto positioning based on which axis this is. 6977 var temp = (this.name == 'yaxis') ? 1 : -1; 6978 switch (t.labelPosition) { 6979 case 'auto': 6980 // position at end 6981 case 'end': 6982 if (temp * t.angle < 0) { 6983 shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; 6984 } 6985 else { 6986 shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; 6987 } 6988 break; 6989 case 'start': 6990 if (t.angle > 0) { 6991 shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; 6992 } 6993 else { 6994 shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; 6995 } 6996 break; 6997 case 'middle': 6998 // if (t.angle > 0) { 6999 // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 7000 // } 7001 // else { 7002 // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; 7003 // } 7004 shim = -t.getHeight()/2; 7005 break; 7006 default: 7007 shim = -t.getHeight()/2; 7008 break; 7009 } 7010 } 7011 else { 7012 shim = -t.getHeight()/2; 7013 } 7014 7015 var val = this.u2p(t.value) + shim + 'px'; 7016 t._elem.css('top', val); 7017 t.pack(); 7018 } 7019 } 7020 if (lshow) { 7021 var h = this._label._elem.outerHeight(true); 7022 this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); 7023 if (this.name == 'yaxis') { 7024 this._label._elem.css('left', '0px'); 7025 } 7026 else { 7027 this._label._elem.css('right', '0px'); 7028 } 7029 this._label.pack(); 7030 } 7031 } 7032 } 7033 7034 ticks = null; 7035 }; 7036 7037 7038 /** 7039 * The following code was generaously given to me a while back by Scott Prahl. 7040 * He did a good job at computing axes min, max and number of ticks for the 7041 * case where the user has not set any scale related parameters (tickInterval, 7042 * numberTicks, min or max). I had ignored this use case for a long time, 7043 * focusing on the more difficult case where user has set some option controlling 7044 * tick generation. Anyway, about time I got this into jqPlot. 7045 * Thanks Scott!! 7046 */ 7047 7048 /** 7049 * Copyright (c) 2010 Scott Prahl 7050 * The next three routines are currently available for use in all personal 7051 * or commercial projects under both the MIT and GPL version 2.0 licenses. 7052 * This means that you can choose the license that best suits your project 7053 * and use it accordingly. 7054 */ 7055
7056 // A good format string depends on the interval. If the interval is greater 7057 // than 1 then there is no need to show any decimal digits. If it is < 1.0, then 7058 // use the magnitude of the interval to determine the number of digits to show. 7059 function bestFormatString (interval) 7060 { 7061 var fstr; 7062 interval = Math.abs(interval); 7063 if (interval >= 10) { 7064 fstr = '%d'; 7065 } 7066 7067 else if (interval > 1) { 7068 if (interval === parseInt(interval, 10)) { 7069 fstr = '%d'; 7070 } 7071 else { 7072 fstr = '%.1f'; 7073 } 7074 } 7075 7076 else { 7077 var expv = -Math.floor(Math.log(interval)/Math.LN10); 7078 fstr = '%.' + expv + 'f'; 7079 } 7080 7081 return fstr; 7082 } 7083 7084 var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5]; 7085 7086 var _getLowerFactor = function(f) { 7087 var i = _factors.indexOf(f); 7088 if (i > 0) { 7089 return _factors[i-1]; 7090 } 7091 else { 7092 return _factors[_factors.length - 1] / 100; 7093 } 7094 }; 7095 7096 var _getHigherFactor = function(f) { 7097 var i = _factors.indexOf(f); 7098 if (i < _factors.length-1) { 7099 return _factors[i+1]; 7100 } 7101 else { 7102 return _factors[0] * 100; 7103 } 7104 }; 7105 7106 // Given a fixed minimum and maximum and a target number ot ticks 7107 // figure out the best interval and 7108 // return min, max, number ticks, format string and tick interval 7109 function bestConstrainedInterval(min, max, nttarget) { 7110 // run through possible number to ticks and see which interval is best 7111 var low = Math.floor(nttarget/2); 7112 var hi = Math.ceil(nttarget*1.5); 7113 var badness = Number.MAX_VALUE; 7114 var r = (max - min); 7115 var temp; 7116 var sd; 7117 var bestNT; 7118 var gsf = $.jqplot.getSignificantFigures; 7119 var fsd; 7120 var fs; 7121 var currentNT; 7122 var bestPrec; 7123 7124 for (var i=0, l=hi-low+1; i<l; i++) { 7125 currentNT = low + i; 7126 temp = r/(currentNT-1); 7127 sd = gsf(temp); 7128 7129 temp = Math.abs(nttarget - currentNT) + sd.digitsRight; 7130 if (temp < badness) { 7131 badness = temp; 7132 bestNT = currentNT; 7133 bestPrec = sd.digitsRight; 7134 } 7135 else if (temp === badness) { 7136 // let nicer ticks trump number ot ticks 7137 if (sd.digitsRight < bestPrec) { 7138 bestNT = currentNT; 7139 bestPrec = sd.digitsRight; 7140 } 7141 } 7142 7143 } 7144 7145 fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight)); 7146 if (fsd === 0) { 7147 fs = '%d'; 7148 } 7149 else { 7150 fs = '%.' + fsd + 'f'; 7151 } 7152 temp = r / (bestNT - 1); 7153 // min, max, number ticks, format string, tick interval 7154 return [min, max, bestNT, fs, temp]; 7155 } 7156 7157 // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n 7158 // it is based soley on the range and number of ticks. So if user specifies 7159 // number of ticks, use this. 7160 function bestInterval(range, numberTicks) { 7161 numberTicks = numberTicks || 7; 7162 var minimum = range / (numberTicks - 1); 7163 var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10)); 7164 var residual = minimum / magnitude; 7165 var interval; 7166 // "nicest" ranges are 1, 2, 5 or powers of these. 7167 // for magnitudes below 1, only allow these. 7168 if (magnitude < 1) { 7169 if (residual > 5) { 7170 interval = 10 * magnitude; 7171 } 7172 else if (residual > 2) { 7173 interval = 5 * magnitude; 7174 } 7175 else if (residual > 1) { 7176 interval = 2 * magnitude; 7177 } 7178 else { 7179 interval = magnitude; 7180 } 7181 } 7182 // for large ranges (whole integers), allow intervals like 3, 4 or powers of these. 7183 // this helps a lot with poor choices for number of ticks. 7184 else { 7185 if (residual > 5) { 7186 interval = 10 * magnitude; 7187 } 7188 else if (residual > 4) { 7189 interval = 5 * magnitude; 7190 } 7191 else if (residual > 3) { 7192 interval = 4 * magnitude; 7193 } 7194 else if (residual > 2) { 7195 interval = 3 * magnitude; 7196 } 7197 else if (residual > 1) { 7198 interval = 2 * magnitude; 7199 } 7200 else { 7201 interval = magnitude; 7202 } 7203 } 7204 7205 return interval; 7206 } 7207 7208 // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n 7209 // it is based soley on the range of data, number of ticks must be computed later. 7210 function bestLinearInterval(range, scalefact) { 7211 scalefact = scalefact || 1; 7212 var expv = Math.floor(Math.log(range)/Math.LN10); 7213 var magnitude = Math.pow(10, expv); 7214 // 0 < f < 10 7215 var f = range / magnitude; 7216 var fact; 7217 // for large plots, scalefact will decrease f and increase number of ticks.
7218 // for small plots, scalefact will increase f and decrease number of ticks. 7219 f = f/scalefact; 7220 7221 // for large plots, smaller interval, more ticks. 7222 if (f<=0.38) { 7223 fact = 0.1; 7224 } 7225 else if (f<=1.6) { 7226 fact = 0.2; 7227 } 7228 else if (f<=4.0) { 7229 fact = 0.5; 7230 } 7231 else if (f<=8.0) { 7232 fact = 1.0; 7233 } 7234 // for very small plots, larger interval, less ticks in number ticks 7235 else if (f<=16.0) { 7236 fact = 2; 7237 } 7238 else { 7239 fact = 5; 7240 } 7241 7242 return fact*magnitude; 7243 } 7244 7245 function bestLinearComponents(range, scalefact) { 7246 var expv = Math.floor(Math.log(range)/Math.LN10); 7247 var magnitude = Math.pow(10, expv); 7248 // 0 < f < 10 7249 var f = range / magnitude; 7250 var interval; 7251 var fact; 7252 // for large plots, scalefact will decrease f and increase number of ticks. 7253 // for small plots, scalefact will increase f and decrease number of ticks. 7254 f = f/scalefact; 7255 7256 // for large plots, smaller interval, more ticks. 7257 if (f<=0.38) { 7258 fact = 0.1; 7259 } 7260 else if (f<=1.6) { 7261 fact = 0.2; 7262 } 7263 else if (f<=4.0) { 7264 fact = 0.5; 7265 } 7266 else if (f<=8.0) { 7267 fact = 1.0; 7268 } 7269 // for very small plots, larger interval, less ticks in number ticks 7270 else if (f<=16.0) { 7271 fact = 2; 7272 } 7273 // else if (f<=20.0) { 7274 // fact = 3; 7275 // } 7276 // else if (f<=24.0) { 7277 // fact = 4; 7278 // } 7279 else { 7280 fact = 5; 7281 } 7282 7283 interval = fact * magnitude; 7284 7285 return [interval, fact, magnitude]; 7286 } 7287 7288 // Given the min and max for a dataset, return suitable endpoints 7289 // for the graphing, a good number for the number of ticks, and a 7290 // format string so that extraneous digits are not displayed. 7291 // returned is an array containing [min, max, nTicks, format] 7292 $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) { 7293 // Set to preserve EITHER min OR max. 7294 // If min is preserved, max must be free. 7295 keepMin = (keepMin === null) ? false : keepMin; 7296 keepMax = (keepMax === null || keepMin) ? false : keepMax; 7297 // if endpoints are equal try to include zero otherwise include one 7298 if (axis_min === axis_max) { 7299 axis_max = (axis_max) ? 0 : 1; 7300 } 7301 7302 scalefact = scalefact || 1.0; 7303 7304 // make sure range is positive 7305 if (axis_max < axis_min) { 7306 var a = axis_max; 7307 axis_max = axis_min; 7308 axis_min = a; 7309 } 7310 7311 var r = []; 7312 var ss = bestLinearInterval(axis_max - axis_min, scalefact); 7313 7314 var gsf = $.jqplot.getSignificantFigures; 7315 7316 if (numberTicks == null) { 7317 7318 // Figure out the axis min, max and number of ticks 7319 // the min and max will be some multiple of the tick interval, 7320 // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the 7321 // axis min is negative, 0 will be a tick. 7322 if (!keepMin && !keepMax) { 7323 r[0] = Math.floor(axis_min / ss) * ss; // min 7324 r[1] = Math.ceil(axis_max / ss) * ss; // max 7325 r[2] = Math.round((r[1]-r[0])/ss+1.0); // number of ticks 7326 r[3] = bestFormatString(ss); // format string 7327 r[4] = ss; // tick Interval 7328 } 7329 7330 else if (keepMin) { 7331 r[0] = axis_min; // min 7332 r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks 7333 r[1] = axis_min + (r[2] - 1) * ss; // max 7334 var digitsMin = gsf(axis_min).digitsRight; 7335 var digitsSS = gsf(ss).digitsRight; 7336 if (digitsMin < digitsSS) { 7337 r[3] = bestFormatString(ss);
7337 // format string 7338 } 7339 else { 7340 r[3] = '%.' + digitsMin + 'f'; 7341 } 7342 r[4] = ss; // tick Interval 7343 } 7344 7345 else if (keepMax) { 7346 r[1] = axis_max; // max 7347 r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks 7348 r[0] = axis_max - (r[2] - 1) * ss; // min 7349 var digitsMax = gsf(axis_max).digitsRight; 7350 var digitsSS = gsf(ss).digitsRight; 7351 if (digitsMax < digitsSS) { 7352 r[3] = bestFormatString(ss); // format string 7353 } 7354 else { 7355 r[3] = '%.' + digitsMax + 'f'; 7356 } 7357 r[4] = ss; // tick Interval 7358 } 7359 } 7360 7361 else { 7362 var tempr = []; 7363 7364 // Figure out the axis min, max and number of ticks 7365 // the min and max will be some multiple of the tick interval, 7366 // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the 7367 // axis min is negative, 0 will be a tick. 7368 tempr[0] = Math.floor(axis_min / ss) * ss; // min 7369 tempr[1] = Math.ceil(axis_max / ss) * ss; // max 7370 tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0); // number of ticks 7371 tempr[3] = bestFormatString(ss); // format string 7372 tempr[4] = ss; // tick Interval 7373 7374 // first, see if we happen to get the right number of ticks 7375 if (tempr[2] === numberTicks) { 7376 r = tempr; 7377 } 7378 7379 else { 7380 7381 var newti = bestInterval(tempr[1] - tempr[0], numberTicks); 7382 7383 r[0] = tempr[0]; // min 7384 r[2] = numberTicks; // number of ticks 7385 r[4] = newti; // tick interval 7386 r[3] = bestFormatString(newti); // format string 7387 r[1] = r[0] + (r[2] - 1) * r[4]; // max 7388 } 7389 } 7390 7391 return r; 7392 }; 7393 7394 $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval; 7395 $.jqplot.LinearTickGenerator.bestInterval = bestInterval; 7396 $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents; 7397 $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval; 7398 7399 7400 // class: $.jqplot.MarkerRenderer 7401 // The default jqPlot marker renderer, rendering the points on the line. 7402 $.jqplot.MarkerRenderer = function(options){ 7403 // Group: Properties 7404 7405 // prop: show 7406 // whether or not to show the marker. 7407 this.show = true; 7408 // prop: style 7409 // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare 7410 this.style = 'filledCircle'; 7411 // prop: lineWidth 7412 // size of the line for non-filled markers. 7413 this.lineWidth = 2; 7414 // prop: size 7415 // Size of the marker (diameter or circle, length of edge of square, etc.) 7416 this.size = 9.0; 7417 // prop: color 7418 // color of marker. Will be set to color of series by default on init. 7419 this.color = '#666666'; 7420 // prop: shadow 7421 // whether or not to draw a shadow on the line 7422 this.shadow = true; 7423 // prop: shadowAngle 7424 // Shadow angle in degrees 7425 this.shadowAngle = 45; 7426 // prop: shadowOffset 7427 // Shadow offset from line in pixels 7428 this.shadowOffset = 1; 7429 // prop: shadowDepth 7430 // Number of times shadow is stroked, each stroke offset shadowOffset from the last. 7431 this.shadowDepth = 3; 7432 // prop: shadowAlpha 7433 // Alpha channel transparency of shadow. 0 = transparent. 7434 this.shadowAlpha = '0.07'; 7435 // prop: shadowRenderer 7436 // Renderer that will draws the shadows on the marker. 7437 this.shadowRenderer = new $.jqplot.ShadowRenderer(); 7438 // prop: shapeRenderer 7439 // Renderer that will draw the marker. 7440 this.shapeRenderer = new $.jqplot.ShapeRenderer(); 7441 7442 $.extend(true, this, options); 7443 }; 7444 7445 $.jqplot.MarkerRenderer.prototype.init = function(options) { 7446 $.extend(true, this, options);
7447 var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true}; 7448 if (this.style.indexOf('filled') != -1) { 7449 sdopt.fill = true; 7450 } 7451 if (this.style.indexOf('ircle') != -1) { 7452 sdopt.isarc = true; 7453 sdopt.closePath = false; 7454 } 7455 this.shadowRenderer.init(sdopt); 7456 7457 var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true}; 7458 if (this.style.indexOf('filled') != -1) { 7459 shopt.fill = true; 7460 } 7461 if (this.style.indexOf('ircle') != -1) { 7462 shopt.isarc = true; 7463 shopt.closePath = false; 7464 } 7465 this.shapeRenderer.init(shopt); 7466 }; 7467 7468 $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) { 7469 var stretch = 1.2; 7470 var dx = this.size/2/stretch; 7471 var dy = this.size/2*stretch; 7472 var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]]; 7473 if (this.shadow) { 7474 this.shadowRenderer.draw(ctx, points); 7475 } 7476 this.shapeRenderer.draw(ctx, points, options); 7477 }; 7478 7479 $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) { 7480 var stretch = 1.0; 7481 var dx = this.size/2*stretch; 7482 var dy = this.size/2*stretch; 7483 var points1 = [[x, y-dy], [x, y+dy]]; 7484 var points2 = [[x+dx, y], [x-dx, y]]; 7485 var opts = $.extend(true, {}, this.options, {closePath:false}); 7486 if (this.shadow) { 7487 this.shadowRenderer.draw(ctx, points1, {closePath:false}); 7488 this.shadowRenderer.draw(ctx, points2, {closePath:false}); 7489 } 7490 this.shapeRenderer.draw(ctx, points1, opts); 7491 this.shapeRenderer.draw(ctx, points2, opts); 7492 }; 7493 7494 $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) { 7495 var stretch = 1.0; 7496 var dx = this.size/2*stretch; 7497 var dy = this.size/2*stretch; 7498 var opts = $.extend(true, {}, this.options, {closePath:false}); 7499 var points1 = [[x-dx, y-dy], [x+dx, y+dy]]; 7500 var points2 = [[x-dx, y+dy], [x+dx, y-dy]]; 7501 if (this.shadow) { 7502 this.shadowRenderer.draw(ctx, points1, {closePath:false}); 7503 this.shadowRenderer.draw(ctx, points2, {closePath:false}); 7504 } 7505 this.shapeRenderer.draw(ctx, points1, opts); 7506 this.shapeRenderer.draw(ctx, points2, opts); 7507 }; 7508 7509 $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) { 7510 var stretch = 1.0; 7511 var dx = this.size/2*stretch; 7512 var dy = this.size/2*stretch; 7513 var points = [[x-dx, y], [x+dx, y]]; 7514 if (this.shadow) { 7515 this.shadowRenderer.draw(ctx, points); 7516 } 7517 this.shapeRenderer.draw(ctx, points, options); 7518 }; 7519 7520 $.jqplot.MarkerRenderer.prototype.drawLine = function(p1, p2, ctx, fill, options) { 7521 var points = [p1, p2]; 7522 if (this.shadow) { 7523 this.shadowRenderer.draw(ctx, points); 7524 } 7525 this.shapeRenderer.draw(ctx, points, options); 7526 }; 7527 7528 $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) { 7529 var stretch = 1.0; 7530 var dx = this.size/2/stretch; 7531 var dy = this.size/2*stretch; 7532 var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]]; 7533 if (this.shadow) { 7534 this.shadowRenderer.draw(ctx, points); 7535 } 7536 this.shapeRenderer.draw(ctx, points, options); 7537 }; 7538 7539 $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) { 7540 var radius = this.size/2; 7541 var end = 2*Math.PI; 7542 var points = [x, y, radius, 0, end, true]; 7543 if (this.shadow) { 7544 this.shadowRenderer.draw(ctx, points); 7545 } 7546 this.shapeRenderer.draw(ctx, points, options); 7547 }; 7548 7549 $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) { 7550 options = options || {}; 7551 // hack here b/c shape renderer uses canvas based color style options 7552 // and marker uses css style names. 7553 if (options.show == null || options.show != false) { 7554 if (options.color && !options.fillStyle) { 7555 options.fillStyle = options.color; 7556 } 7557 if (options.color && !options.strokeStyle) { 7558 options.strokeStyle = options.color; 7559 } 7560 switch (this.style) { 7561 case 'diamond': 7562 this.drawDiamond(x,y,ctx, false, options); 7563 break; 7564 case 'filledDiamond': 7565 this.drawDiamond(x,y,ctx, true, options); 7566 break; 7567 case 'circle': 7568 this.drawCircle(x,y,ctx, false, options); 7569 break; 7570 case 'filledCircle': 7571 this.drawCircle(x,y,ctx, true, options); 7572 break; 7573 case 'square': 7574 this.drawSquare(x,y,ctx, false, options); 7575 break; 7576 case 'filledSquare': 7577 this.drawSquare(x,y,ctx, true, options); 7578 break; 7579 case 'x': 7580 this.drawX(x,y,ctx, true, options); 7581 break; 7582 case 'plus': 7583 this.drawPlus(x,y,ctx, true, options); 7584 break; 7585 case 'dash': 7586 this.drawDash(x,y,ctx, true, options); 7587 break; 7588 case 'line': 7589 this.drawLine(x, y, ctx, false, options); 7590 break; 7591 default: 7592 this.drawDiamond(x,y,ctx, false, options); 7593 break; 7594 } 7595 } 7596 }; 7597 7598 // class: $.jqplot.shadowRenderer 7599 // The default jqPlot shadow renderer, rendering shadows behind shapes. 7600 $.jqplot.ShadowRenderer = function(options){ 7601 // Group: Properties 7602 7603 // prop: angle 7604 // Angle of the shadow in degrees. Measured counter-clockwise from the x axis. 7605 this.angle = 45; 7606 // prop: offset 7607 // Pixel offset at the given shadow angle of each shadow stroke from the last stroke. 7608 this.offset = 1; 7609 // prop: alpha 7610 // alpha transparency of shadow stroke. 7611 this.alpha = 0.07; 7612 // prop: lineWidth 7613 // width of the shadow line stroke. 7614 this.lineWidth = 1.5; 7615 // prop: lineJoin 7616 // How line segments of the shadow are joined. 7617 this.lineJoin = 'miter'; 7618 // prop: lineCap 7619 // how ends of the shadow line are rendered. 7620 this.lineCap = 'round'; 7621 // prop; closePath 7622 // whether line path segment is closed upon itself. 7623 this.closePath = false;
7624 // prop: fill 7625 // whether to fill the shape. 7626 this.fill = false; 7627 // prop: depth 7628 // how many times the shadow is stroked. Each stroke will be offset by offset at angle degrees. 7629 this.depth = 3; 7630 this.strokeStyle = 'rgba(0,0,0,0.1)'; 7631 // prop: isarc 7632 // whether the shadow is an arc or not. 7633 this.isarc = false; 7634 7635 $.extend(true, this, options); 7636 }; 7637 7638 $.jqplot.ShadowRenderer.prototype.init = function(options) { 7639 $.extend(true, this, options); 7640 }; 7641 7642 // function: draw 7643 // draws an transparent black (i.e. gray) shadow. 7644 // 7645 // ctx - canvas drawing context 7646 // points - array of points or [x, y, radius, start angle (rad), end angle (rad)] 7647 $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) { 7648 ctx.save(); 7649 var opts = (options != null) ? options : {}; 7650 var fill = (opts.fill != null) ? opts.fill : this.fill; 7651 var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; 7652 var closePath = (opts.closePath != null) ? opts.closePath : this.closePath; 7653 var offset = (opts.offset != null) ? opts.offset : this.offset; 7654 var alpha = (opts.alpha != null) ? opts.alpha : this.alpha; 7655 var depth = (opts.depth != null) ? opts.depth : this.depth; 7656 var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; 7657 var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; 7658 ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth; 7659 ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin; 7660 ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap; 7661 ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')'; 7662 ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')'; 7663 for (var j=0; j<depth; j++) { 7664 var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); 7665 ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset); 7666 ctxPattern.beginPath(); 7667 if (isarc) { 7668 ctx.arc(points[0], points[1], points[2], points[3], points[4], true); 7669 } 7670 else if (fillRect) { 7671 if (fillRect) { 7672 ctx.fillRect(points[0], points[1], points[2], points[3]); 7673 } 7674 } 7675 else if (points && points.length){ 7676 var move = true; 7677 for (var i=0; i<points.length; i++) { 7678 // skip to the first non-null point and move to it. 7679 if (points[i][0] != null && points[i][1] != null) { 7680 if (move) { 7681 ctxPattern.moveTo(points[i][0], points[i][1]); 7682 move = false; 7683 } 7684 else { 7685 ctxPattern.lineTo(points[i][0], points[i][1]); 7686 } 7687 } 7688 else { 7689 move = true; 7690 } 7691 } 7692 7693 } 7694 if (closePath) { 7695 ctxPattern.closePath(); 7696 } 7697 if (fill) { 7698 ctx.fill(); 7699 } 7700 else { 7701 ctx.stroke(); 7702 } 7703 } 7704 ctx.restore(); 7705 }; 7706 7707 // class: $.jqplot.shapeRenderer 7708 // The default jqPlot shape renderer. Given a set of points will 7709 // plot them and either stroke a line (fill = false) or fill them (fill = true). 7710 // If a filled shape is desired, closePath = true must also be set to close 7711 // the shape. 7712 $.jqplot.ShapeRenderer = function(options){ 7713 7714 this.lineWidth = 1.5; 7715 // prop: linePattern 7716 // line pattern 'dashed', 'dotted', 'solid', some combination 7717 // of '-' and '.' characters such as '.-.' or a numerical array like 7718 // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, 7719 // [1, 10, 20, 10] to draw a dot-dash line, and so on. 7720 this.linePattern = 'solid'; 7721 // prop: lineJoin 7722 // How line segments of the shadow are joined. 7723 this.lineJoin = 'miter'; 7724 // prop: lineCap 7725 // how ends of the shadow line are rendered. 7726 this.lineCap = 'round'; 7727 // prop; closePath 7728 // whether line path segment is closed upon itself. 7729 this.closePath = false;
7730 // prop: fill 7731 // whether to fill the shape. 7732 this.fill = false; 7733 // prop: isarc 7734 // whether the shadow is an arc or not. 7735 this.isarc = false; 7736 // prop: fillRect 7737 // true to draw shape as a filled rectangle. 7738 this.fillRect = false; 7739 // prop: strokeRect 7740 // true to draw shape as a stroked rectangle. 7741 this.strokeRect = false; 7742 // prop: clearRect 7743 // true to cear a rectangle. 7744 this.clearRect = false; 7745 // prop: strokeStyle 7746 // css color spec for the stoke style 7747 this.strokeStyle = '#999999'; 7748 // prop: fillStyle 7749 // css color spec for the fill style. 7750 this.fillStyle = '#999999'; 7751 7752 $.extend(true, this, options); 7753 }; 7754 7755 $.jqplot.ShapeRenderer.prototype.init = function(options) { 7756 $.extend(true, this, options); 7757 }; 7758 7759 // function: draw 7760 // draws the shape. 7761 // 7762 // ctx - canvas drawing context 7763 // points - array of points for shapes or 7764 // [x, y, width, height] for rectangles or 7765 // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs. 7766 $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) { 7767 ctx.save(); 7768 var opts = (options != null) ? options : {}; 7769 var fill = (opts.fill != null) ? opts.fill : this.fill; 7770 var closePath = (opts.closePath != null) ? opts.closePath : this.closePath; 7771 var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; 7772 var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect; 7773 var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect; 7774 var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; 7775 var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; 7776 var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); 7777 ctx.lineWidth = opts.lineWidth || this.lineWidth; 7778 ctx.lineJoin = opts.lineJoin || this.lineJoin; 7779 ctx.lineCap = opts.lineCap || this.lineCap; 7780 ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle; 7781 ctx.fillStyle = opts.fillStyle || this.fillStyle; 7782 ctx.beginPath(); 7783 if (isarc) { 7784 ctx.arc(points[0], points[1], points[2], points[3], points[4], true); 7785 if (closePath) { 7786 ctx.closePath(); 7787 } 7788 if (fill) { 7789 ctx.fill(); 7790 } 7791 else { 7792 ctx.stroke(); 7793 } 7794 ctx.restore(); 7795 return; 7796 } 7797 else if (clearRect) { 7798 ctx.clearRect(points[0], points[1], points[2], points[3]); 7799 ctx.restore(); 7800 return; 7801 } 7802 else if (fillRect || strokeRect) { 7803 if (fillRect) { 7804 ctx.fillRect(points[0], points[1], points[2], points[3]); 7805 } 7806 if (strokeRect) { 7807 ctx.strokeRect(points[0], points[1], points[2], points[3]); 7808 ctx.restore(); 7809 return; 7810 } 7811 } 7812 else if (points && points.length){ 7813 var move = true; 7814 for (var i=0; i<points.length; i++) { 7815 // skip to the first non-null point and move to it. 7816 if (points[i][0] != null && points[i][1] != null) { 7817 if (move) { 7818 ctxPattern.moveTo(points[i][0], points[i][1]); 7819 move = false; 7820 } 7821 else { 7822 ctxPattern.lineTo(points[i][0], points[i][1]); 7823 } 7824 } 7825 else { 7826 move = true; 7827 } 7828 } 7829 if (closePath) { 7830 ctxPattern.closePath(); 7831 } 7832 if (fill) { 7833 ctx.fill(); 7834 } 7835 else { 7836 ctx.stroke(); 7837 } 7838 } 7839 ctx.restore(); 7840 }; 7841 7842 // class $.jqplot.TableLegendRenderer 7843 // The default legend renderer for jqPlot. 7844 $.jqplot.TableLegendRenderer = function(){ 7845 // 7846 }; 7847 7848 $.jqplot.TableLegendRenderer.prototype.init = function(options) { 7849 $.extend(true, this, options); 7850 }; 7851 7852 $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) { 7853 var rs = (pad) ? this.rowSpacing+'px' : '0px'; 7854 var tr; 7855 var td; 7856 var elem; 7857 var div0; 7858 var div1; 7859 elem = document.createElement('tr'); 7860 tr = $(elem); 7861 tr.addClass('jqplot-table-legend'); 7862 elem = null; 7863 7864 if (reverse){ 7865 tr.prependTo(this._elem); 7866 } 7867 7868 else{ 7869 tr.appendTo(this._elem); 7870 } 7871 7872 if (this.showSwatches) { 7873 td = $(document.createElement('td')); 7874 td.addClass('jqplot-table-legend jqplot-table-legend-swatch'); 7875 td.css({textAlign: 'center', paddingTop: rs}); 7876 7877 div0 = $(document.createElement('div')); 7878 div0.addClass('jqplot-table-legend-swatch-outline'); 7879 div1 = $(document.createElement('div')); 7880 div1.addClass('jqplot-table-legend-swatch'); 7881 div1.css({backgroundColor: color, borderColor: color}); 7882 7883 tr.append(td.append(div0.append(div1))); 7884 7885 // $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ 7886 // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+ 7887 // '</div></td>').appendTo(tr); 7888 } 7889 if (this.showLabels) { 7890 td = $(document.createElement('td')); 7891 td.addClass('jqplot-table-legend jqplot-table-legend-label'); 7892 td.css('paddingTop', rs); 7893 tr.append(td); 7894 7895 // elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
7896 // elem.appendTo(tr); 7897 if (this.escapeHtml) { 7898 td.text(label); 7899 } 7900 else { 7901 td.html(label); 7902 } 7903 } 7904 td = null; 7905 div0 = null; 7906 div1 = null; 7907 tr = null; 7908 elem = null; 7909 }; 7910 7911 // called with scope of legend 7912 $.jqplot.TableLegendRenderer.prototype.draw = function() { 7913 if (this._elem) { 7914 this._elem.emptyForce(); 7915 this._elem = null; 7916 } 7917 7918 if (this.show) { 7919 var series = this._series; 7920 // make a table. one line label per row. 7921 var elem = document.createElement('table'); 7922 this._elem = $(elem); 7923 this._elem.addClass('jqplot-table-legend'); 7924 7925 var ss = {position:'absolute'}; 7926 if (this.background) { 7927 ss['background'] = this.background; 7928 } 7929 if (this.border) { 7930 ss['border'] = this.border; 7931 } 7932 if (this.fontSize) { 7933 ss['fontSize'] = this.fontSize; 7934 } 7935 if (this.fontFamily) { 7936 ss['fontFamily'] = this.fontFamily; 7937 } 7938 if (this.textColor) { 7939 ss['textColor'] = this.textColor; 7940 } 7941 if (this.marginTop != null) { 7942 ss['marginTop'] = this.marginTop; 7943 } 7944 if (this.marginBottom != null) { 7945 ss['marginBottom'] = this.marginBottom; 7946 } 7947 if (this.marginLeft != null) { 7948 ss['marginLeft'] = this.marginLeft; 7949 } 7950 if (this.marginRight != null) { 7951 ss['marginRight'] = this.marginRight; 7952 } 7953 7954 7955 var pad = false, 7956 reverse = false, 7957 s; 7958 for (var i = 0; i< series.length; i++) { 7959 s = series[i]; 7960 if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){ 7961 reverse = true; 7962 } 7963 if (s.show && s.showLabel) { 7964 var lt = this.labels[i] || s.label.toString(); 7965 if (lt) { 7966 var color = s.color; 7967 if (reverse && i < series.length - 1){ 7968 pad = true; 7969 } 7970 else if (reverse && i == series.length - 1){ 7971 pad = false; 7972 } 7973 this.renderer.addrow.call(this, lt, color, pad, reverse); 7974 pad = true; 7975 } 7976 // let plugins add more rows to legend. Used by trend line plugin. 7977 for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) { 7978 var item = $.jqplot.addLegendRowHooks[j].call(this, s); 7979 if (item) { 7980 this.renderer.addrow.call(this, item.label, item.color, pad); 7981 pad = true; 7982 } 7983 } 7984 lt = null; 7985 } 7986 } 7987 } 7988 return this._elem; 7989 }; 7990 7991 $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) { 7992 if (this.show) { 7993 if (this.placement == 'insideGrid') { 7994 switch (this.location) { 7995 case 'nw': 7996 var a = offsets.left; 7997 var b = offsets.top; 7998 this._elem.css('left', a); 7999 this._elem.css('top', b); 8000 break; 8001 case 'n': 8002 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8003 var b = offsets.top; 8004 this._elem.css('left', a); 8005 this._elem.css('top', b); 8006 break; 8007 case 'ne': 8008 var a = offsets.right; 8009 var b = offsets.top; 8010 this._elem.css({right:a, top:b}); 8011 break; 8012 case 'e': 8013 var a = offsets.right; 8014 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8015 this._elem.css({right:a, top:b}); 8016 break; 8017 case 'se': 8018 var a = offsets.right; 8019 var b = offsets.bottom; 8020 this._elem.css({right:a, bottom:b}); 8021 break; 8022 case 's': 8023 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8024 var b = offsets.bottom; 8025 this._elem.css({left:a, bottom:b}); 8026 break; 8027 case 'sw': 8028 var a = offsets.left; 8029 var b = offsets.bottom; 8030 this._elem.css({left:a, bottom:b}); 8031 break; 8032 case 'w': 8033 var a = offsets.left; 8034 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8035 this._elem.css({left:a, top:b}); 8036 break; 8037 default: // same as 'se' 8038 var a = offsets.right; 8039 var b = offsets.bottom; 8040 this._elem.css({right:a, bottom:b}); 8041 break; 8042 } 8043 8044 } 8045 else if (this.placement == 'outside'){ 8046 switch (this.location) { 8047 case 'nw': 8048 var a = this._plotDimensions.width - offsets.left; 8049 var b = offsets.top; 8050 this._elem.css('right', a); 8051 this._elem.css('top', b); 8052 break; 8053 case 'n': 8054 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8055 var b = this._plotDimensions.height - offsets.top; 8056 this._elem.css('left', a); 8057 this._elem.css('bottom', b); 8058 break;
8059 case 'ne': 8060 var a = this._plotDimensions.width - offsets.right; 8061 var b = offsets.top; 8062 this._elem.css({left:a, top:b}); 8063 break; 8064 case 'e': 8065 var a = this._plotDimensions.width - offsets.right; 8066 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8067 this._elem.css({left:a, top:b}); 8068 break; 8069 case 'se': 8070 var a = this._plotDimensions.width - offsets.right; 8071 var b = offsets.bottom; 8072 this._elem.css({left:a, bottom:b}); 8073 break; 8074 case 's': 8075 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8076 var b = this._plotDimensions.height - offsets.bottom; 8077 this._elem.css({left:a, top:b}); 8078 break; 8079 case 'sw': 8080 var a = this._plotDimensions.width - offsets.left; 8081 var b = offsets.bottom; 8082 this._elem.css({right:a, bottom:b}); 8083 break; 8084 case 'w': 8085 var a = this._plotDimensions.width - offsets.left; 8086 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8087 this._elem.css({right:a, top:b}); 8088 break; 8089 default: // same as 'se' 8090 var a = offsets.right; 8091 var b = offsets.bottom; 8092 this._elem.css({right:a, bottom:b}); 8093 break; 8094 } 8095 } 8096 else { 8097 switch (this.location) { 8098 case 'nw': 8099 this._elem.css({left:0, top:offsets.top}); 8100 break; 8101 case 'n': 8102 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8103 this._elem.css({left: a, top:offsets.top}); 8104 break; 8105 case 'ne': 8106 this._elem.css({right:0, top:offsets.top}); 8107 break; 8108 case 'e': 8109 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8110 this._elem.css({right:offsets.right, top:b}); 8111 break; 8112 case 'se': 8113 this._elem.css({right:offsets.right, bottom:offsets.bottom}); 8114 break; 8115 case 's': 8116 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8117 this._elem.css({left: a, bottom:offsets.bottom}); 8118 break; 8119 case 'sw': 8120 this._elem.css({left:offsets.left, bottom:offsets.bottom}); 8121 break; 8122 case 'w': 8123 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8124 this._elem.css({left:offsets.left, top:b}); 8125 break; 8126 default: // same as 'se' 8127 this._elem.css({right:offsets.right, bottom:offsets.bottom}); 8128 break; 8129 } 8130 } 8131 } 8132 }; 8133 8134 /** 8135 * Class: $.jqplot.ThemeEngine 8136 * Theme Engine provides a programatic way to change some of the more 8137 * common jqplot styling options such as fonts, colors and grid options. 8138 * A theme engine instance is created with each plot. The theme engine 8139 * manages a collection of themes which can be modified, added to, or 8140 * applied to the plot. 8141 * 8142 * The themeEngine class is not instantiated directly. 8143 * When a plot is initialized, the current plot options are scanned 8144 * an a default theme named "Default" is created. This theme is 8145 * used as the basis for other themes added to the theme engine and 8146 * is always available. 8147 * 8148 * A theme is a simple javascript object with styling parameters for 8149 * various entities of the plot. A theme has the form: 8150 * 8151 * 8152 * > { 8153 * > _name:f "Default", 8154 * > target: { 8155 * > backgroundColor: "transparent" 8156 * > }, 8157 * > legend: { 8158 * > textColor: null, 8159 * > fontFamily: null, 8160 * > fontSize: null, 8161 * > border: null, 8162 * > background: null 8163 * > }, 8164 * > title: { 8165 * > textColor: "rgb(102, 102, 102)", 8166 * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif", 8167 * > fontSize: "19.2px", 8168 * > textAlign: "center" 8169 * > }, 8170 * > seriesStyles: {}, 8171 * > series: [{ 8172 * > color: "#4bb2c5", 8173 * > lineWidth: 2.5, 8174 * > linePattern: "solid", 8175 * > shadow: true, 8176 * > fillColor: "#4bb2c5", 8177 * > showMarker: true, 8178 * > markerOptions: { 8179 * > color: "#4bb2c5", 8180 * > show: true, 8181 * > style: 'filledCircle', 8182 * > lineWidth: 1.5, 8183 * > size: 4, 8184 * > shadow: true 8185 * > } 8186 * > }], 8187 * > grid: { 8188 * >
8188 drawGridlines: true, 8189 * > gridLineColor: "#cccccc", 8190 * > gridLineWidth: 1, 8191 * > backgroundColor: "#fffdf6", 8192 * > borderColor: "#999999", 8193 * > borderWidth: 2, 8194 * > shadow: true 8195 * > }, 8196 * > axesStyles: { 8197 * > label: {}, 8198 * > ticks: {} 8199 * > }, 8200 * > axes: { 8201 * > xaxis: { 8202 * > borderColor: "#999999", 8203 * > borderWidth: 2, 8204 * > ticks: { 8205 * > show: true, 8206 * > showGridline: true, 8207 * > showLabel: true, 8208 * > showMark: true, 8209 * > size: 4, 8210 * > textColor: "", 8211 * > whiteSpace: "nowrap", 8212 * > fontSize: "12px", 8213 * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif" 8214 * > }, 8215 * > label: { 8216 * > textColor: "rgb(102, 102, 102)", 8217 * > whiteSpace: "normal", 8218 * > fontSize: "14.6667px", 8219 * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif", 8220 * > fontWeight: "400" 8221 * > } 8222 * > }, 8223 * > yaxis: { 8224 * > borderColor: "#999999", 8225 * > borderWidth: 2, 8226 * > ticks: { 8227 * > show: true, 8228 * > showGridline: true, 8229 * > showLabel: true, 8230 * > showMark: true, 8231 * > size: 4, 8232 * > textColor: "", 8233 * > whiteSpace: "nowrap", 8234 * > fontSize: "12px", 8235 * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif" 8236 * > }, 8237 * > label: { 8238 * > textColor: null, 8239 * > whiteSpace: null, 8240 * > fontSize: null, 8241 * > fontFamily: null, 8242 * > fontWeight: null 8243 * > } 8244 * > }, 8245 * > x2axis: {... 8246 * > }, 8247 * > ... 8248 * > y9axis: {... 8249 * > } 8250 * > } 8251 * > } 8252 * 8253 * "seriesStyles" is a style object that will be applied to all series in the plot. 8254 * It will forcibly override any styles applied on the individual series. "axesStyles" is 8255 * a style object that will be applied to all axes in the plot. It will also forcibly 8256 * override any styles on the individual axes. 8257 * 8258 * The example shown above has series options for a line series. Options for other 8259 * series types are shown below: 8260 * 8261 * Bar Series: 8262 * 8263 * > { 8264 * > color: "#4bb2c5", 8265 * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], 8266 * > lineWidth: 2.5, 8267 * > shadow: true, 8268 * > barPadding: 2, 8269 * > barMargin: 10, 8270 * > barWidth: 15.09375, 8271 * > highlightColors: ["rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)"] 8272 * > } 8273 * 8274 * Pie Series: 8275 * 8276 * > { 8277 * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], 8278 * > padding: 20, 8279 * > sliceMargin: 0, 8280 * > fill: true, 8281 * > shadow: true, 8282 * > startAngle: 0, 8283 * > lineWidth: 2.5, 8284 * > highlightColors: ["rgb(129,201,214)", "rgb(240,189,104)", "rgb(214,202,165)", "rgb(137,180,158)", "rgb(168,180,137)", "rgb(180,174,89)", "rgb(180,113,161)", "rgb(129,141,236)", "rgb(227,205,120)", "rgb(255,138,76)", "rgb(76,169,219)", "rgb(215,126,190)", "rgb(220,232,135)", "rgb(200,167,96)", "rgb(103,202,235)", "rgb(208,154,215)"] 8285 * > } 8286 * 8287 * Funnel Series: 8288 * 8289 * > { 8290 * > color: "#4bb2c5", 8291 * > lineWidth: 2, 8292 * > shadow: true, 8293 * > padding: { 8294 * > top: 20, 8295 * > right: 20, 8296 * > bottom: 20, 8297 * > left: 20 8298 * > }, 8299 * > sectionMargin: 6, 8300 * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], 8301 * > highlightColors: ["rgb(147,208,220)", "rgb(242,199,126)", "rgb(220,210,178)", "rgb(154,191,172)", "rgb(180,191,154)", "rgb(191,186,112)", "rgb(191,133,174)", "rgb(147,157,238)", "rgb(231,212,139)", "rgb(255,154,102)", "rgb(102,181,224)", "rgb(221,144,199)", "rgb(225,235,152)", "rgb(200,167,96)", "rgb(124,210,238)", "rgb(215,169,221)"] 8302 * > } 8303 * 8304 */ 8305 $.jqplot.ThemeEngine = function(){ 8306 // Group: Properties 8307 // 8308 // prop: themes 8309 // hash of themes managed by the theme engine. 8310 // Indexed by theme name. 8311 this.themes = {}; 8312 // prop: activeTheme 8313 // Pointer to currently active theme 8314 this.activeTheme=null; 8315 8316 }; 8317 8318 // called with scope of plot 8319 $.jqplot.ThemeEngine.prototype.init = function() { 8320 // get the Default theme from the current plot settings. 8321 var th = new $.jqplot.Theme({_name:'Default'}); 8322 var n, i, nn; 8323 8324 for (n in th.target) { 8325 if (n == "textColor") { 8326 th.target[n] = this.target.css('color'); 8327 } 8328 else { 8329 th.target[n] = this.target.css(n); 8330 } 8331 } 8332 8333 if (this.title.show && this.title._elem) { 8334 for (n in th.title) { 8335 if (n == "textColor") { 8336 th.title[n] = this.title._elem.css('color'); 8337 } 8338 else { 8339 th.title[n] = this.title._elem.css(n); 8340 } 8341 } 8342 } 8343 8344 for (n in th.grid) { 8345 th.grid[n] = this.grid[n]; 8346 } 8347 if (th.grid.backgroundColor == null && this.grid.background != null) { 8348 th.grid.backgroundColor = this.grid.background; 8349 } 8350 if (this.legend.show && this.legend._elem) { 8351 for (n in th.legend) { 8352 if (n == 'textColor') { 8353 th.legend[n] = this.legend._elem.css('color'); 8354 } 8355 else { 8356 th.legend[n] = this.legend._elem.css(n); 8357 } 8358 } 8359 } 8360 var s; 8361 8362 for (i=0; i<this.series.length; i++) { 8363 s = this.series[i]; 8364 if (s.renderer.constructor == $.jqplot.LineRenderer) { 8365 th.series.push(new LineSeriesProperties()); 8366 } 8367 else if (s.renderer.constructor == $.jqplot.BarRenderer) { 8368 th.series.push(new BarSeriesProperties()); 8369 } 8370 else if (s.renderer.constructor == $.jqplot.PieRenderer) { 8371 th.series.push(new PieSeriesProperties()); 8372 }
8373 else if (s.renderer.constructor == $.jqplot.DonutRenderer) { 8374 th.series.push(new DonutSeriesProperties()); 8375 } 8376 else if (s.renderer.constructor == $.jqplot.FunnelRenderer) { 8377 th.series.push(new FunnelSeriesProperties()); 8378 } 8379 else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) { 8380 th.series.push(new MeterSeriesProperties()); 8381 } 8382 else { 8383 th.series.push({}); 8384 } 8385 for (n in th.series[i]) { 8386 th.series[i][n] = s[n]; 8387 } 8388 } 8389 var a, ax; 8390 for (n in this.axes) { 8391 ax = this.axes[n]; 8392 a = th.axes[n] = new AxisProperties(); 8393 a.borderColor = ax.borderColor; 8394 a.borderWidth = ax.borderWidth; 8395 if (ax._ticks && ax._ticks[0]) { 8396 for (nn in a.ticks) { 8397 if (ax._ticks[0].hasOwnProperty(nn)) { 8398 a.ticks[nn] = ax._ticks[0][nn]; 8399 } 8400 else if (ax._ticks[0]._elem){ 8401 a.ticks[nn] = ax._ticks[0]._elem.css(nn); 8402 } 8403 } 8404 } 8405 if (ax._label && ax._label.show) { 8406 for (nn in a.label) { 8407 // a.label[nn] = ax._label._elem.css(nn); 8408 if (ax._label[nn]) { 8409 a.label[nn] = ax._label[nn]; 8410 } 8411 else if (ax._label._elem){ 8412 if (nn == 'textColor') { 8413 a.label[nn] = ax._label._elem.css('color'); 8414 } 8415 else { 8416 a.label[nn] = ax._label._elem.css(nn); 8417 } 8418 } 8419 } 8420 } 8421 } 8422 this.themeEngine._add(th); 8423 this.themeEngine.activeTheme = this.themeEngine.themes[th._name]; 8424 }; 8425 /** 8426 * Group: methods 8427 * 8428 * method: get 8429 * 8430 * Get and return the named theme or the active theme if no name given. 8431 * 8432 * parameter: 8433 * 8434 * name - name of theme to get. 8435 * 8436 * returns: 8437 * 8438 * Theme instance of given name. 8439 */ 8440 $.jqplot.ThemeEngine.prototype.get = function(name) { 8441 if (!name) { 8442 // return the active theme 8443 return this.activeTheme; 8444 } 8445 else { 8446 return this.themes[name]; 8447 } 8448 }; 8449 8450 function numericalOrder(a,b) { return a-b; } 8451 8452 /** 8453 * method: getThemeNames 8454 * 8455 * Return the list of theme names in this manager in alpha-numerical order. 8456 * 8457 * parameter: 8458 * 8459 * None 8460 * 8461 * returns: 8462 * 8463 * A the list of theme names in this manager in alpha-numerical order. 8464 */ 8465 $.jqplot.ThemeEngine.prototype.getThemeNames = function() { 8466 var tn = []; 8467 for (var n in this.themes) { 8468 tn.push(n); 8469 } 8470 return tn.sort(numericalOrder); 8471 }; 8472 8473 /** 8474 * method: getThemes 8475 * 8476 * Return a list of themes in alpha-numerical order by name. 8477 * 8478 * parameter: 8479 * 8480 * None 8481 * 8482 * returns: 8483 * 8484 * A list of themes in alpha-numerical order by name. 8485 */ 8486 $.jqplot.ThemeEngine.prototype.getThemes = function() { 8487 var tn = []; 8488 var themes = []; 8489 for (var n in this.themes) { 8490 tn.push(n); 8491 } 8492 tn.sort(numericalOrder); 8493 for (var i=0; i<tn.length; i++) { 8494 themes.push(this.themes[tn[i]]); 8495 } 8496 return themes; 8497 }; 8498 8499 $.jqplot.ThemeEngine.prototype.activate = function(plot, name) { 8500 // sometimes need to redraw whole plot. 8501 var redrawPlot = false; 8502 if (!name && this.activeTheme && this.activeTheme._name) { 8503 name = this.activeTheme._name; 8504 } 8505 if (!this.themes.hasOwnProperty(name)) { 8506 throw new Error("No theme of that name"); 8507 } 8508 else { 8509 var th = this.themes[name]; 8510 this.activeTheme = th; 8511 var val, checkBorderColor = false, checkBorderWidth = false;
8512 var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis']; 8513 8514 for (i=0; i<arr.length; i++) { 8515 var ax = arr[i]; 8516 if (th.axesStyles.borderColor != null) { 8517 plot.axes[ax].borderColor = th.axesStyles.borderColor; 8518 } 8519 if (th.axesStyles.borderWidth != null) { 8520 plot.axes[ax].borderWidth = th.axesStyles.borderWidth; 8521 } 8522 } 8523 8524 for (var axname in plot.axes) { 8525 var axis = plot.axes[axname]; 8526 if (axis.show) { 8527 var thaxis = th.axes[axname] || {}; 8528 var thaxstyle = th.axesStyles; 8529 var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle); 8530 val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor; 8531 if (thax.borderColor != null) { 8532 axis.borderColor = thax.borderColor; 8533 redrawPlot = true; 8534 } 8535 val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth; 8536 if (thax.borderWidth != null) { 8537 axis.borderWidth = thax.borderWidth; 8538 redrawPlot = true; 8539 } 8540 if (axis._ticks && axis._ticks[0]) { 8541 for (var nn in thax.ticks) { 8542 // val = null; 8543 // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) { 8544 // val = th.axesStyles.ticks[nn]; 8545 // } 8546 // else if (thax.ticks[nn] != null){ 8547 // val = thax.ticks[nn] 8548 // } 8549 val = thax.ticks[nn]; 8550 if (val != null) { 8551 axis.tickOptions[nn] = val; 8552 axis._ticks = []; 8553 redrawPlot = true; 8554 } 8555 } 8556 } 8557 if (axis._label && axis._label.show) { 8558 for (var nn in thax.label) { 8559 // val = null; 8560 // if (th.axesStyles.label && th.axesStyles.label[nn] != null) { 8561 // val = th.axesStyles.label[nn]; 8562 // } 8563 // else if (thax.label && thax.label[nn] != null){ 8564 // val = thax.label[nn] 8565 // } 8566 val = thax.label[nn]; 8567 if (val != null) { 8568 axis.labelOptions[nn] = val; 8569 redrawPlot = true; 8570 } 8571 } 8572 } 8573 8574 } 8575 } 8576 8577 for (var n in th.grid) { 8578 if (th.grid[n] != null) { 8579 plot.grid[n] = th.grid[n]; 8580 } 8581 } 8582 if (!redrawPlot) { 8583 plot.grid.draw(); 8584 } 8585 8586 if (plot.legend.show) { 8587 for (n in th.legend) { 8588 if (th.legend[n] != null) { 8589 plot.legend[n] = th.legend[n]; 8590 } 8591 } 8592 } 8593 if (plot.title.show) { 8594 for (n in th.title) { 8595 if (th.title[n] != null) { 8596 plot.title[n] = th.title[n]; 8597 } 8598 } 8599 } 8600 8601 var i; 8602 for (i=0; i<th.series.length; i++) { 8603 var opts = {}; 8604 var redrawSeries = false; 8605 for (n in th.series[i]) { 8606 val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n]; 8607 if (val != null) { 8608 opts[n] = val; 8609 if (n == 'color') { 8610 plot.series[i].renderer.shapeRenderer.f
8610illStyle = val; 8611 plot.series[i].renderer.shapeRenderer.strokeStyle = val; 8612 plot.series[i][n] = val; 8613 } 8614 else if ((n == 'lineWidth') || (n == 'linePattern')) { 8615 plot.series[i].renderer.shapeRenderer[n] = val; 8616 plot.series[i][n] = val; 8617 } 8618 else if (n == 'markerOptions') { 8619 merge (plot.series[i].markerOptions, val); 8620 merge (plot.series[i].markerRenderer, val); 8621 } 8622 else { 8623 plot.series[i][n] = val; 8624 } 8625 redrawPlot = true; 8626 } 8627 } 8628 } 8629 8630 if (redrawPlot) { 8631 plot.target.empty(); 8632 plot.draw(); 8633 } 8634 8635 for (n in th.target) { 8636 if (th.target[n] != null) { 8637 plot.target.css(n, th.target[n]); 8638 } 8639 } 8640 } 8641 8642 }; 8643 8644 $.jqplot.ThemeEngine.prototype._add = function(theme, name) { 8645 if (name) { 8646 theme._name = name; 8647 } 8648 if (!theme._name) { 8649 theme._name = Date.parse(new Date()); 8650 } 8651 if (!this.themes.hasOwnProperty(theme._name)) { 8652 this.themes[theme._name] = theme; 8653 } 8654 else { 8655 throw new Error("jqplot.ThemeEngine Error: Theme already in use"); 8656 } 8657 }; 8658 8659 // method remove 8660 // Delete the named theme, return true on success, false on failure. 8661 8662 8663 /** 8664 * method: remove 8665 * 8666 * Remove the given theme from the themeEngine. 8667 * 8668 * parameters: 8669 * 8670 * name - name of the theme to remove. 8671 * 8672 * returns: 8673 * 8674 * true on success, false on failure. 8675 */ 8676 $.jqplot.ThemeEngine.prototype.remove = function(name) { 8677 if (name == 'Default') { 8678 return false; 8679 } 8680 return delete this.themes[name]; 8681 }; 8682 8683 /** 8684 * method: newTheme 8685 * 8686 * Create a new theme based on the default theme, adding it the themeEngine. 8687 * 8688 * parameters: 8689 * 8690 * name - name of the new theme. 8691 * obj - optional object of styles to be applied to this new theme. 8692 * 8693 * returns: 8694 * 8695 * new Theme object. 8696 */ 8697 $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) { 8698 if (typeof(name) == 'object') { 8699 obj = obj || name; 8700 name = null; 8701 } 8702 if (obj && obj._name) { 8703 name = obj._name; 8704 } 8705 else { 8706 name = name || Date.parse(new Date()); 8707 } 8708 // var th = new $.jqplot.Theme(name); 8709 var th = this.copy(this.themes['Default']._name, name); 8710 $.jqplot.extend(th, obj); 8711 return th; 8712 }; 8713 8714 // function clone(obj) { 8715 // return eval(obj.toSource()); 8716 // } 8717 8718 function clone(obj){ 8719 if(obj == null || typeof(obj) != 'object'){ 8720 return obj; 8721 } 8722 8723 var temp = new obj.constructor(); 8724 for(var key in obj){ 8725 temp[key] = clone(obj[key]); 8726 } 8727 return temp; 8728 } 8729 8730 $.jqplot.clone = clone; 8731 8732 function merge(obj1, obj2) { 8733 if (obj2 == null || typeof(obj2) != 'object') { 8734 return; 8735 } 8736 for (var key in obj2) { 8737 if (key == 'highlightColors') { 8738 obj1[key] = clone(obj2[key]); 8739 } 8740 if (obj2[key] != null && typeof(obj2[key]) == 'object') { 8741 if (!obj1.hasOwnProperty(key)) { 8742 obj1[key] = {}; 8743 } 8744 merge(obj1[key], obj2[key]); 8745 } 8746 else { 8747 obj1[key] = obj2[key]; 8748 } 8749 } 8750 } 8751 8752 $.jqplot.merge = merge; 8753 8754 // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic 8755 $.jqplot.extend = function() { 8756 // copy reference to target object 8757 var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; 8758 8759 // Handle a deep copy situation 8760 if ( typeof target === "boolean" ) { 8761 deep = target; 8762 target = arguments[1] || {}; 8763 // skip the boolean and the target 8764 i = 2; 8765 } 8766 8767 // Handle case when target is a string or something (possible in deep copy) 8768 if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) { 8769 target = {}; 8770 } 8771 8772 for ( ; i < length; i++ ){ 8773 // Only deal with non-null/undefined values 8774 if ( (options = arguments[ i ]) != null ) { 8775 // Extend the base object 8776 for ( var name in options ) { 8777 var src = target[ name ], copy = options[ name ]; 8778 8779 // Prevent never-ending loop 8780 if ( target === copy ) { 8781 continue; 8782 } 8783 8784 // Recurse if we're merging object values 8785 if ( deep && copy && typeof copy === "object" && !copy.nodeType ) { 8786 target[ name ] = $.jqplot.extend( deep, 8787 // Never move original objects, clone them 8788 src || ( copy.length != null ? [ ] : { } ) 8789 , copy ); 8790 } 8791 // Don't bring in undefined values 8792 else if ( copy !== undefined ) { 8793 target[ name ] = copy; 8794 } 8795 } 8796 } 8797 } 8798 // Return the modified object 8799 return target; 8800 }; 8801 8802 /** 8803 * method: rename 8804 *
8805 * Rename a theme. 8806 * 8807 * parameters: 8808 * 8809 * oldName - current name of the theme. 8810 * newName - desired name of the theme. 8811 * 8812 * returns: 8813 * 8814 * new Theme object. 8815 */ 8816 $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) { 8817 if (oldName == 'Default' || newName == 'Default') { 8818 throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default"); 8819 } 8820 if (this.themes.hasOwnProperty(newName)) { 8821 throw new Error ("jqplot.ThemeEngine Error: New name already in use."); 8822 } 8823 else if (this.themes.hasOwnProperty(oldName)) { 8824 var th = this.copy (oldName, newName); 8825 this.remove(oldName); 8826 return th; 8827 } 8828 throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid"); 8829 }; 8830 8831 /** 8832 * method: copy 8833 * 8834 * Create a copy of an existing theme in the themeEngine, adding it the themeEngine. 8835 * 8836 * parameters: 8837 * 8838 * sourceName - name of the existing theme. 8839 * targetName - name of the copy. 8840 * obj - optional object of style parameter to apply to the new theme. 8841 * 8842 * returns: 8843 * 8844 * new Theme object. 8845 */ 8846 $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) { 8847 if (targetName == 'Default') { 8848 throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme"); 8849 } 8850 if (!this.themes.hasOwnProperty(sourceName)) { 8851 var s = "jqplot.ThemeEngine Error: Source name invalid"; 8852 throw new Error(s); 8853 } 8854 if (this.themes.hasOwnProperty(targetName)) { 8855 var s = "jqplot.ThemeEngine Error: Target name invalid"; 8856 throw new Error(s); 8857 } 8858 else { 8859 var th = clone(this.themes[sourceName]); 8860 th._name = targetName; 8861 $.jqplot.extend(true, th, obj); 8862 this._add(th); 8863 return th; 8864 } 8865 }; 8866 8867 8868 $.jqplot.Theme = function(name, obj) { 8869 if (typeof(name) == 'object') { 8870 obj = obj || name; 8871 name = null; 8872 } 8873 name = name || Date.parse(new Date()); 8874 this._name = name; 8875 this.target = { 8876 backgroundColor: null 8877 }; 8878 this.legend = { 8879 textColor: null, 8880 fontFamily: null, 8881 fontSize: null, 8882 border: null, 8883 background: null 8884 }; 8885 this.title = { 8886 textColor: null, 8887 fontFamily: null, 8888 fontSize: null, 8889 textAlign: null 8890 }; 8891 this.seriesStyles = {}; 8892 this.series = []; 8893 this.grid = { 8894 drawGridlines: null, 8895 gridLineColor: null, 8896 gridLineWidth: null, 8897 backgroundColor: null, 8898 borderColor: null, 8899 borderWidth: null, 8900 shadow: null 8901 }; 8902 this.axesStyles = {label:{}, ticks:{}}; 8903 this.axes = {}; 8904 if (typeof(obj) == 'string') { 8905 this._name = obj; 8906 } 8907 else if(typeof(obj) == 'object') { 8908 $.jqplot.extend(true, this, obj); 8909 } 8910 }; 8911 8912 var AxisProperties = function() { 8913 this.borderColor = null; 8914 this.borderWidth = null; 8915 this.ticks = new AxisTicks(); 8916 this.label = new AxisLabel(); 8917 }; 8918 8919 var AxisTicks = function() { 8920 this.show = null; 8921 this.showGridline = null; 8922 this.showLabel = null; 8923 this.showMark = null; 8924 this.size = null; 8925 this.textColor = null; 8926 this.whiteSpace = null; 8927 this.fontSize = null; 8928 this.fontFamily = null; 8929 }; 8930 8931 var AxisLabel = function() { 8932 this.textColor = null; 8933 this.whiteSpace = null; 8934 this.fontSize = null; 8935 this.fontFamily = null; 8936 this.fontWeight = null; 8937 }; 8938 8939 var LineSeriesProperties = function() { 8940 this.color=null; 8941 this.lineWidth=null; 8942 this.linePattern=null; 8943 this.shadow=null; 8944 this.fillColor=null; 8945 this.showMarker=null; 8946 this.markerOptions = new MarkerOptions(); 8947 }; 8948 8949 var MarkerOptions = function() { 8950 this.show = null; 8951 this.style = null; 8952 this.lineWidth = null; 8953 this.size = null; 8954 this.color = null; 8955 this.shadow = null; 8956 }; 8957 8958 var BarSeriesProperties = function() { 8959 this.color=null; 8960 this.seriesColors=null; 8961 this.lineWidth=null; 8962 this.shadow=null; 8963 this.barPadding=null; 8964 this.barMargin=null; 8965 this.barWidth=null; 8966 this.highlightColors=null; 8967 }; 8968 8969 var PieSeriesProperties = function() { 8970 this.seriesColors=null; 8971 this.padding=null; 8972 this.sliceMargin=null; 8973 this.fill=null; 8974 this.shadow=null; 8975 this.startAngle=null; 8976 this.lineWidth=null; 8977 this.highlightColors=null; 8978 }; 8979 8980 var DonutSeriesProperties = function() { 8981 this.seriesColors=null; 8982 this.padding=null; 8983 this.sliceMargin=null; 8984 this.fill=null; 8985 this.shadow=null; 8986 this.startAngle=null; 8987 this.lineWidth=null; 8988 this.innerDiameter=null; 8989 this.thickness=null; 8990 this.ringMargin=null; 8991 this.highlightColors=null; 8992 }; 8993 8994 var FunnelSeriesProperties = function() { 8995 this.color=null; 8996 this.lineWidth=null; 8997 this.shadow=null; 8998 this.padding=null; 8999 this.sectionMargin=null; 9000 this.seriesColors=null; 9001 this.highlightColors=null; 9002 }; 9003 9004 var MeterSeriesProperties = function() { 9005 this.padding=null; 9006 this.backgroundColor=null; 9007 this.ringColor=null; 9008 this.tickColor=null; 9009 this.ringWidth=null; 9010 this.intervalColors=null; 9011 this.intervalInnerRadius=null; 9012 this.intervalOuterRadius=null; 9013 this.hubRadius=null; 9014 this.needleThickness=null; 9015 this.needlePad=null; 9016 }; 9017 9018 9019 9020 9021 $.fn.jqplotChildText = function() { 9022 return $(this).contents().filter(function() { 9023 return this.nodeType == 3; // Node.TEXT_NODE not defined in I7 9024 }).text(); 9025 }; 9026 9027 // Returns font style as abbreviation for "font" property. 9028 $.fn.jqplotGetComputedFontStyle = function() { 9029 var css = window.getComputedStyle ? window.getComputedStyle(this[0], "") : this[0].currentStyle; 9030 var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily']; 9031 var style = []; 9032 9033 for (var i=0 ; i < attrs.length; ++i) { 9034 var attr = String(css[attrs[i]]); 9035 9036 if (attr && attr != 'normal') { 9037 style.push(attr); 9038 } 9039 } 9040 return style.join(' '); 9041 }; 9042 9043 /** 9044 * Namespace: $.fn 9045 * jQuery namespace to attach functions to jQuery elements. 9046 * 9047 */ 9048 9049 $.fn.jqplotToImageCanvas = function(options) { 9050 9051 options = options || {}; 9052 var x_offset = (options.x_offset == null) ? 0 : options.x_offset; 9053 var y_offset = (options.y_offset == null) ? 0 : options.y_offset; 9054 var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor; 9055 9056 if ($(this).width() == 0 || $(this).height() == 0) { 9057 return null; 9058 } 9059 9060 // excanvas and hence IE < 9 do not support toDataURL and cannot export images. 9061 if ($.jqplot.use_excanvas) { 9062 return null; 9063 } 9064 9065 var newCanvas = document.createElement("canvas"); 9066 var h = $(this).outerHeight(true); 9067 var w = $(this).outerWidth(true); 9068 var offs = $(this).offset(); 9069 var plotleft = offs.left; 9070 var plottop = offs.top; 9071 var transx = 0, transy = 0; 9072 9073 // have to check if any elements are hanging outside of plot area before rendering, 9074 // since changing width of canvas will erase canvas. 9075 9076 var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick', 9077 'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick', 9078 'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label', 9079 'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ]; 9080 9081 var temptop, templeft, tempbottom, tempright; 9082 9083 for (var i = 0; i < clses.length; i++) { 9084 $(this).find('.'+clses[i]).each(function() { 9085 temptop = $(this).offset().top - plottop; 9086 templeft = $(this).offset().left - plotleft; 9087 tempright = templeft + $(this).outerWidth(true) + transx; 9088 tempbottom = temptop + $(this).outerHeight(true) + transy; 9089 if (templeft < -transx) { 9090 w = w - transx - templeft; 9091 transx = -templeft; 9092 } 9093 if (temptop < -transy) { 9094 h = h - transy - temptop; 9095 transy = - temptop; 9096 } 9097 if (tempright > w) { 9098 w = tempright; 9099 } 9100 if (tempbottom > h) { 9101 h = tempbottom; 9102 } 9103 }); 9104 } 9105 9106 newCanvas.width = w + Number(x_offset); 9107 newCanvas.height = h + Number(y_offset); 9108 9109 var newContext = newCanvas.getContext("2d"); 9110 9111 newContext.save(); 9112 newContext.fillStyle = backgroundColor; 9113 newContext.fillRect(0,0, newCanvas.width, newCanvas.height);
9114 newContext.restore(); 9115 9116 newContext.translate(transx, transy); 9117 newContext.textAlign = 'left'; 9118 newContext.textBaseline = 'top'; 9119 9120 function getLineheight(el) { 9121 var lineheight = parseInt($(el).css('line-height'), 10); 9122 9123 if (isNaN(lineheight)) { 9124 lineheight = parseInt($(el).css('font-size'), 10) * 1.2; 9125 } 9126 return lineheight; 9127 } 9128 9129 function writeWrappedText (el, context, text, left, top, canvasWidth) { 9130 var lineheight = getLineheight(el); 9131 var tagwidth = $(el).innerWidth(); 9132 var tagheight = $(el).innerHeight(); 9133 var words = text.split(/\s+/); 9134 var wl = words.length; 9135 var w = ''; 9136 var breaks = []; 9137 var temptop = top; 9138 var templeft = left; 9139 9140 for (var i=0; i<wl; i++) { 9141 w += words[i]; 9142 if (context.measureText(w).width > tagwidth) { 9143 breaks.push(i); 9144 w = ''; 9145 i--; 9146 } 9147 } 9148 if (breaks.length === 0) { 9149 // center text if necessary 9150 if ($(el).css('textAlign') === 'center') { 9151 templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; 9152 } 9153 context.fillText(text, templeft, top); 9154 } 9155 else { 9156 w = words.slice(0, breaks[0]).join(' '); 9157 // center text if necessary 9158 if ($(el).css('textAlign') === 'center') { 9159 templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; 9160 } 9161 context.fillText(w, templeft, temptop); 9162 temptop += lineheight; 9163 for (var i=1, l=breaks.length; i<l; i++) { 9164 w = words.slice(breaks[i-1], breaks[i]).join(' '); 9165 // center text if necessary 9166 if ($(el).css('textAlign') === 'center') { 9167 templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; 9168 } 9169 context.fillText(w, templeft, temptop); 9170 temptop += lineheight; 9171 } 9172 w = words.slice(breaks[i-1], words.length).join(' '); 9173 // center text if necessary 9174 if ($(el).css('textAlign') === 'center') { 9175 templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; 9176 } 9177 context.fillText(w, templeft, temptop); 9178 } 9179 9180 } 9181 9182 function _jqpToImage(el, x_offset, y_offset) { 9183 var tagname = el.tagName.toLowerCase(); 9184 var p = $(el).position(); 9185 var css = window.getComputedStyle ? window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9 9186 var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10); 9187 var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10); 9188 var w = newCanvas.width; 9189 // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth') 9190 9191 // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas. 9192 9193 if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) { 9194 $(el).children().each(function() { 9195 _jqpToImage(this, left, top); 9196 }); 9197 var text = $(el).jqplotChildText(); 9198 9199 if (text) { 9200 newContext.font = $(el).jqplotGetComputedFontStyle(); 9201 newContext.fillStyle = $(el).css('color'); 9202 9203 writeWrappedText(el, newContext, text, left, top, w); 9204 } 9205 } 9206 9207 // handle the standard table legend 9208 9209 else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) { 9210 newContext.strokeStyle = $(el).css('border-top-color'); 9211 newContext.fillStyle = $(el).css('background-color'); 9212 newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight()); 9213 if (parseInt($(el).css('border-top-width'), 10) > 0) { 9214 newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight()); 9215 } 9216 9217 // find all the swatches 9218 $(el).find('div.jqplot-table-legend-swatch-outline').each(function() { 9219 // get the first div and stroke it 9220 var elem = $(this); 9221 newContext.strokeStyle = elem.css('border-top-color'); 9222 var l = left + elem.position().left; 9223 var t = top + elem.position().top; 9224 newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHe
9224ight()); 9225 9226 // now fill the swatch 9227 9228 l += parseInt(elem.css('padding-left'), 10); 9229 t += parseInt(elem.css('padding-top'), 10); 9230 var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10); 9231 var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10); 9232 9233 var swatch = elem.children('div.jqplot-table-legend-swatch'); 9234 newContext.fillStyle = swatch.css('background-color'); 9235 newContext.fillRect(l, t, w, h); 9236 }); 9237 9238 // now add text 9239 9240 $(el).find('td.jqplot-table-legend-label').each(function(){ 9241 var elem = $(this); 9242 var l = left + elem.position().left; 9243 var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10); 9244 newContext.font = elem.jqplotGetComputedFontStyle(); 9245 newContext.fillStyle = elem.css('color'); 9246 writeWrappedText(elem, newContext, elem.text(), l, t, w); 9247 }); 9248 9249 var elem = null; 9250 } 9251 9252 else if (tagname == 'canvas') { 9253 newContext.drawImage(el, left, top); 9254 } 9255 } 9256 $(this).children().each(function() { 9257 _jqpToImage(this, x_offset, y_offset); 9258 }); 9259 return newCanvas; 9260 }; 9261 9262 // return the raw image data string. 9263 // Should work on canvas supporting browsers. 9264 $.fn.jqplotToImageStr = function(options) { 9265 var imgCanvas = $(this).jqplotToImageCanvas(options); 9266 if (imgCanvas) { 9267 return imgCanvas.toDataURL("image/png"); 9268 } 9269 else { 9270 return null; 9271 } 9272 }; 9273 9274 // return a DOM <img> element and return it. 9275 // Should work on canvas supporting browsers. 9276 $.fn.jqplotToImageElem = function(options) { 9277 var elem = document.createElement("img"); 9278 var str = $(this).jqplotToImageStr(options); 9279 elem.src = str; 9280 return elem; 9281 }; 9282 9283 // return a string for an <img> element and return it. 9284 // Should work on canvas supporting browsers. 9285 $.fn.jqplotToImageElemStr = function(options) { 9286 var str = '<img src='+$(this).jqplotToImageStr(options)+' />'; 9287 return str; 9288 }; 9289 9290 // Not guaranteed to work, even on canvas supporting browsers due to 9291 // limitations with location.href and browser support. 9292 $.fn.jqplotSaveImage = function() { 9293 var imgData = $(this).jqplotToImageStr({}); 9294 if (imgData) { 9295 window.location.href = imgData.replace("image/png", "image/octet-stream"); 9296 } 9297 9298 }; 9299 9300 // Not guaranteed to work, even on canvas supporting browsers due to 9301 // limitations with window.open and arbitrary data. 9302 $.fn.jqplotViewImage = function() { 9303 var imgStr = $(this).jqplotToImageElemStr({}); 9304 var imgData = $(this).jqplotToImageStr({}); 9305 if (imgStr) { 9306 var w = window.open(''); 9307 w.document.open("image/png"); 9308 w.document.write(imgStr); 9309 w.document.close(); 9310 w = null; 9311 } 9312 }; 9313 9314 9315 9316 9317 /** 9318 * @description 9319 * <p>Object with extended date parsing and formatting capabilities. 9320 * This library borrows many concepts and ideas from the Date Instance 9321 * Methods by Ken Snyder along with some parts of Ken's actual code.</p> 9322 * 9323 * <p>jsDate takes a different approach by not extending the built-in 9324 * Date Object, improving date parsing, allowing for multiple formatting 9325 * syntaxes and multiple and more easily expandable localization.</p> 9326 * 9327 * @author Chris Leonello 9328 * @date #date# 9329 * @version #VERSION# 9330 * @copyright (c) 2010-2013 Chris Leonello 9331 * jsDate is currently available for use in all personal or commercial projects 9332 * under both the MIT and GPL version 2.0 licenses. This means that you can 9333 * choose the license that best suits your project and use it accordingly. 9334 * 9335 * <p>Ken's original Date Instance Methods and copyright notice:</p> 9336 * <pre> 9337 * Ken Snyder (ken d snyder at gmail dot com) 9338 * 2008-09-10 9339 * version 2.0.2 (http://kendsnyder.com/sandbox/date/)
9340 * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/) 9341 * </pre> 9342 * 9343 * @class 9344 * @name jsDate 9345 * @param {String | Number | Array | Date Object | Options Object} arguments Optional arguments, either a parsable date/time string, 9346 * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], 9347 * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional. 9348 */ 9349 9350 var jsDate = function () { 9351 9352 this.syntax = jsDate.config.syntax; 9353 this._type = "jsDate"; 9354 this.proxy = new Date(); 9355 this.options = {}; 9356 this.locale = jsDate.regional.getLocale(); 9357 this.formatString = ''; 9358 this.defaultCentury = jsDate.config.defaultCentury; 9359 9360 switch ( arguments.length ) { 9361 case 0: 9362 break; 9363 case 1: 9364 // other objects either won't have a _type property or, 9365 // if they do, it shouldn't be set to "jsDate", so 9366 // assume it is an options argument. 9367 if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { 9368 var opts = this.options = arguments[0]; 9369 this.syntax = opts.syntax || this.syntax; 9370 this.defaultCentury = opts.defaultCentury || this.defaultCentury; 9371 this.proxy = jsDate.createDate(opts.date); 9372 } 9373 else { 9374 this.proxy = jsDate.createDate(arguments[0]); 9375 } 9376 break; 9377 default: 9378 var a = []; 9379 for ( var i=0; i<arguments.length; i++ ) { 9380 a.push(arguments[i]); 9381 } 9382 // this should be the current date/time? 9383 this.proxy = new Date(); 9384 this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); 9385 if ( a.slice(3).length ) { 9386 this.proxy.setHours.apply( this.proxy, a.slice(3) ); 9387 } 9388 break; 9389 } 9390 }; 9391 9392 /** 9393 * @namespace Configuration options that will be used as defaults for all instances on the page. 9394 * @property {String} defaultLocale The default locale to use [en]. 9395 * @property {String} syntax The default syntax to use [perl]. 9396 * @property {Number} defaultCentury The default centry for 2 digit dates. 9397 */ 9398 jsDate.config = { 9399 defaultLocale: 'en', 9400 syntax: 'perl', 9401 defaultCentury: 1900 9402 }; 9403 9404 /** 9405 * Add an arbitrary amount to the currently stored date 9406 * 9407 * @param {Number} number 9408 * @param {String} unit 9409 * @returns {jsDate} 9410 */ 9411 9412 jsDate.prototype.add = function(number, unit) { 9413 var factor = multipliers[unit] || multipliers.day; 9414 if (typeof factor == 'number') { 9415 this.proxy.setTime(this.proxy.getTime() + (factor * number)); 9416 } else { 9417 factor.add(this, number); 9418 } 9419 return this; 9420 }; 9421 9422 /** 9423 * Create a new jqplot.date object with the same date 9424 * 9425 * @returns {jsDate} 9426 */ 9427 9428 jsDate.prototype.clone = function() { 9429 return new jsDate(this.proxy.getTime()); 9430 }; 9431 9432 /** 9433 * Get the UTC TimeZone Offset of this date in milliseconds. 9434 * 9435 * @returns {Number} 9436 */ 9437 9438 jsDate.prototype.getUtcOffset = function() { 9439 return this.proxy.getTimezoneOffset() * 60000; 9440 }; 9441 9442 /** 9443 * Find the difference between this jsDate and another date. 9444 * 9445 * @param {String| Number| Array| jsDate Object| Date Object} dateObj 9446 * @param {String} unit 9447 * @param {Boolean} allowDecimal 9448 * @returns {Number} Number of units difference between dates. 9449 */ 9450 9451 jsDate.prototype.diff = function(dateObj, unit, allowDecimal) { 9452 // ensure we have a Date object 9453 dateObj = new jsDate(dateObj); 9454 if (dateObj === null) { 9455 return null; 9456 } 9457 // get the multiplying factor integer or factor function 9458 var factor = multipliers[unit] || multipliers.day; 9459 if (typeof factor == 'number') { 9460 // multiply 9461 var unitDiff = (this.proxy.getTime() - dateObj.proxy.getTime()) / factor; 9462 } else { 9463 // run function 9464 var unitDiff = factor.diff(this.proxy, dateObj.proxy); 9465 }
9466 // if decimals are not allowed, round toward zero 9467 return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff)); 9468 }; 9469 9470 /** 9471 * Get the abbreviated name of the current week day 9472 * 9473 * @returns {String} 9474 */ 9475 9476 jsDate.prototype.getAbbrDayName = function() { 9477 return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()]; 9478 }; 9479 9480 /** 9481 * Get the abbreviated name of the current month 9482 * 9483 * @returns {String} 9484 */ 9485 9486 jsDate.prototype.getAbbrMonthName = function() { 9487 return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()]; 9488 }; 9489 9490 /** 9491 * Get UPPER CASE AM or PM for the current time 9492 * 9493 * @returns {String} 9494 */ 9495 9496 jsDate.prototype.getAMPM = function() { 9497 return this.proxy.getHours() >= 12 ? 'PM' : 'AM'; 9498 }; 9499 9500 /** 9501 * Get lower case am or pm for the current time 9502 * 9503 * @returns {String} 9504 */ 9505 9506 jsDate.prototype.getAmPm = function() { 9507 return this.proxy.getHours() >= 12 ? 'pm' : 'am'; 9508 }; 9509 9510 /** 9511 * Get the century (19 for 20th Century) 9512 * 9513 * @returns {Integer} Century (19 for 20th century). 9514 */ 9515 jsDate.prototype.getCentury = function() { 9516 return parseInt(this.proxy.getFullYear()/100, 10); 9517 }; 9518 9519 /** 9520 * Implements Date functionality 9521 */ 9522 jsDate.prototype.getDate = function() { 9523 return this.proxy.getDate(); 9524 }; 9525 9526 /** 9527 * Implements Date functionality 9528 */ 9529 jsDate.prototype.getDay = function() { 9530 return this.proxy.getDay(); 9531 }; 9532 9533 /** 9534 * Get the Day of week 1 (Monday) thru 7 (Sunday) 9535 * 9536 * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday) 9537 */ 9538 jsDate.prototype.getDayOfWeek = function() { 9539 var dow = this.proxy.getDay(); 9540 return dow===0?7:dow; 9541 }; 9542 9543 /** 9544 * Get the day of the year 9545 * 9546 * @returns {Integer} 1 - 366, day of the year 9547 */ 9548 jsDate.prototype.getDayOfYear = function() { 9549 var d = this.proxy; 9550 var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT'); 9551 ms += d.getTimezoneOffset()*60000; 9552 d = null; 9553 return parseInt(ms/60000/60/24, 10)+1; 9554 }; 9555 9556 /** 9557 * Get the name of the current week day 9558 * 9559 * @returns {String} 9560 */ 9561 9562 jsDate.prototype.getDayName = function() { 9563 return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()]; 9564 }; 9565 9566 /** 9567 * Get the week number of the given year, starting with the first Sunday as the first week 9568 * @returns {Integer} Week number (13 for the 13th full week of the year). 9569 */ 9570 jsDate.prototype.getFullWeekOfYear = function() { 9571 var d = this.proxy; 9572 var doy = this.getDayOfYear(); 9573 var rdow = 6-d.getDay(); 9574 var woy = parseInt((doy+rdow)/7, 10); 9575 return woy; 9576 }; 9577 9578 /** 9579 * Implements Date functionality 9580 */ 9581 jsDate.prototype.getFullYear = function() { 9582 return this.proxy.getFullYear(); 9583 }; 9584 9585 /** 9586 * Get the GMT offset in hours and minutes (e.g. +06:30) 9587 * 9588 * @returns {String} 9589 */ 9590 9591 jsDate.prototype.getGmtOffset = function() { 9592 // divide the minutes offset by 60 9593 var hours = this.proxy.getTimezoneOffset() / 60; 9594 // decide if we are ahead of or behind GMT 9595 var prefix = hours < 0 ? '+' : '-'; 9596 // remove the negative sign if any 9597 hours = Math.abs(hours); 9598 // add the +/- to the padded number of hours to : to the padded minutes 9599 return prefix + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2); 9600 }; 9601 9602 /** 9603 * Implements Date functionality 9604 */ 9605 jsDate.prototype.getHours = function() { 9606 return this.proxy.getHours(); 9607 }; 9608 9609 /** 9610 * Get the current hour on a 12-hour scheme 9611 * 9612 * @returns {Integer} 9613 */ 9614 9615 jsDate.prototype.getHours12 = function() { 9616 var hours = this.proxy.getHours(); 9617 return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours); 9618 }; 9619 9620 9621 jsDate.prototype.getIsoWeek = function() { 9622 var d = this.proxy; 9623 var woy = this.getWeekOfYear(); 9624 var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay(); 9625 // First week is 01 and not 00 as in the case of %U and %W, 9626 // so we add 1 to the final result except if day 1 of the year 9627 // is a Monday (then %W returns 01). 9628 // We also need to subtract 1 if the day 1 of the year is 9629 // Friday-Sunday, so the resulting equation becomes: 9630 var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1); 9631 if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4) 9632 { 9633 idow = 1; 9634 } 9635 else if(idow === 0) 9636 { 9637 d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31')); 9638 idow = d.getIsoWeek(); 9639 } 9640 d = null; 9641 return idow; 9642 }; 9643 9644 /** 9645 * Implements Date functionality 9646 */ 9647 jsDate.prototype.getMilliseconds = function() { 9648 return this.proxy.getMilliseconds(); 9649 }; 9650 9651 /** 9652 * Implements Date functionality 9653 */ 9654 jsDate.prototype.getMinutes = function() { 9655 return this.proxy.getMinutes(); 9656 }; 9657 9658 /** 9659 * Implements Date functionality 9660 */ 9661 jsDate.prototype.getMonth = function() { 9662 return this.proxy.getMonth(); 9663 }; 9664 9665 /** 9666 * Get the name of the current month 9667 * 9668 * @returns {String} 9669 */ 9670 9671 jsDate.prototype.getMonthName = function() { 9672 return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()]; 9673 }; 9674 9675 /** 9676 * Get the number of the current month, 1-12 9677 * 9678 * @returns {Integer} 9679 */ 9680 9681 jsDate.prototype.getMonthNumber = function() { 9682 return this.proxy.getMonth() + 1; 9683 }; 9684 9685 /** 9686 * Implements Date functionality 9687 */ 9688 jsDate.prototype.getSeconds = function() { 9689 return this.proxy.getSeconds(); 9690 }; 9691 9692 /** 9693 * Return a proper two-digit year integer 9694 * 9695 * @returns {Integer} 9696 */ 9697 9698 jsDate.prototype.getShortYear = function() { 9699 return this.proxy.getYear() % 100; 9700 }; 9701 9702 /** 9703 * Implements Date functionality 9704 */ 9705 jsDate.prototype.getTime = function() { 9706 return this.proxy.getTime(); 9707 }; 9708 9709 /** 9710 * Get the timezone abbreviation 9711 * 9712 * @returns {String} Abbreviation for the timezone 9713 */ 9714 jsDate.prototype.getTimezoneAbbr = function() { 9715 return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1'); 9716 }; 9717 9718 /** 9719 * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time) 9720 * 9721 * @returns {String} 9722 */ 9723 jsDate.prototype.getTimezoneName = function() { 9724 var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString()); 9725 return match[1] || match[2] || 'GMT' + this.getGmtOffset(); 9726 }; 9727 9728 /** 9729 * Implements Date functionality 9730 */ 9731 jsDate.prototype.getTimezoneOffset = function() { 9732 return this.proxy.getTimezoneOffset(); 9733 }; 9734 9735 9736 /** 9737 * Get the week number of the given year, starting with the first Monday as the first week 9738 * @returns {Integer} Week number (13 for the 13th week of the year). 9739 */ 9740 jsDate.prototype.getWeekOfYear = function() { 9741 var doy = this.getDayOfYear(); 9742 var rdow = 7 - this.getDayOfWeek(); 9743 var woy = parseInt((doy+rdow)/7, 10); 9744 return woy; 9745 }; 9746 9747 /** 9748 * Get the current date as a Unix timestamp 9749 * 9750 * @returns {Integer} 9751 */ 9752 9753 jsDate.prototype.getUnix = function() { 9754 return Math.round(this.proxy.getTime() / 1000, 0); 9755 }; 9756 9757 /** 9758 * Implements Date functionality 9759 */ 9760 jsDate.prototype.getYear = function() { 9761 return this.proxy.getYear(); 9762 }; 9763 9764 /** 9765 * Return a date one day ahead (or any other unit) 9766 * 9767 * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond 9768 * @returns {jsDate} 9769 */ 9770 9771 jsDate.prototype.next = function(unit) { 9772 unit = unit || 'day'; 9773 return this.clone().add(1, unit); 9774 }; 9775 9776 /** 9777 * Set the jsDate instance to a new date. 9778 * 9779 * @param {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments, 9780 * either a parsable date/time string, 9781 * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], 9782 * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional. 9783 */ 9784 jsDate.prototype.set = function() { 9785 switch ( arguments.length ) { 9786 case 0: 9787 this.proxy = new Date(); 9788 break; 9789 case 1: 9790 // other objects either won't have a _type property or, 9791 // if they do, it shouldn't be set to "jsDate", so 9792 // assume it is an options argument. 9793 if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { 9794 var opts = this.options = arguments[0]; 9795 this.syntax = opts.syntax || this.syntax; 9796 this.defaultCentury = opts.defaultCentury || this.defaultCentury; 9797 this.proxy = jsDate.createDate(opts.date); 9798 } 9799 else { 9800 this.proxy = jsDate.createDate(arguments[0]); 9801 } 9802 break; 9803 default: 9804 var a = []; 9805 for ( var i=0; i<arguments.length; i++ ) { 9806 a.push(arguments[i]); 9807 } 9808 // this should be the current date/time 9809 this.proxy = new Date(); 9810 this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); 9811 if ( a.slice(3).length ) { 9812 this.proxy.setHours.apply( this.proxy, a.slice(3) ); 9813 } 9814 break; 9815 } 9816 return this; 9817 }; 9818 9819 /** 9820 * Sets the day of the month for a specified date according to local time. 9821 * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month. 9822 */ 9823 jsDate.prototype.setDate = function(n) { 9824 this.proxy.setDate(n); 9825 return this; 9826 }; 9827 9828 /** 9829 * Sets the full year for a specified date according to local time. 9830 * @param {Integer} yearValue The numeric value of the year, for example, 1995. 9831 * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December. 9832 * @param {Integer} dayValue Optional, between 1 and 31 representing the day of the month. If you specify the dayValue parameter, you must also specify the monthValue. 9833 */ 9834 jsDate.prototype.setFullYear = function() { 9835 this.proxy.setFullYear.apply(this.proxy, arguments); 9836 return this; 9837 }; 9838 9839 /** 9840 * Sets the hours for a specified date according to local time. 9841 * 9842 * @param {Integer} hoursValue An integer between 0 and 23, representing the hour. 9843 * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes. 9844 * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds. 9845 * If you specify the secondsValue parameter, you must also specify the minutesValue. 9846 * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds. 9847 * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue. 9848 */ 9849 jsDate.prototype.setHours = function() { 9850 this.proxy.setHours.apply(this.proxy, arguments); 9851 return this; 9852 }; 9853 9854 /** 9855 * Implements Date functionality 9856 */ 9857 jsDate.prototype.setMilliseconds = function(n) { 9858 this.proxy.setMilliseconds(n); 9859 return this; 9860 }; 9861 9862 /** 9863 * Implements Date functionality 9864 */ 9865 jsDate.prototype.setMinutes = function() { 9866 this.proxy.setMinutes.apply(this.proxy, arguments); 9867 return this; 9868 }; 9869 9870 /** 9871 * Implements Date functionality 9872 */ 9873 jsDate.prototype.setMonth = function() { 9874 this.proxy.setMonth.apply(this.proxy, arguments); 9875 return this; 9876 }; 9877 9878 /** 9879 * Implements Date functionality 9880 */ 9881 jsDate.prototype.setSeconds = function() { 9882 this.proxy.setSeconds.apply(this.proxy, arguments); 9883 return this; 9884 }; 9885 9886 /** 9887 * Implements Date functionality 9888 */ 9889 jsDate.prototype.setTime = function(n) { 9890 this.proxy.setTime(n); 9891 return this; 9892 }; 9893 9894 /** 9895 * Implements Date functionality 9896 */ 9897 jsDate.prototype.setYear = function() { 9898 this.proxy.setYear.apply(this.proxy, arguments); 9899 return this; 9900 }; 9901 9902 /** 9903 * Provide a formatted string representation of this date. 9904 * 9905 * @param {String}
9905 formatString A format string. 9906 * See: {@link jsDate.formats}. 9907 * @returns {String} Date String. 9908 */ 9909 9910 jsDate.prototype.strftime = function(formatString) { 9911 formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString']; 9912 return jsDate.strftime(this, formatString, this.syntax); 9913 }; 9914 9915 /** 9916 * Return a String representation of this jsDate object. 9917 * @returns {String} Date string. 9918 */ 9919 9920 jsDate.prototype.toString = function() { 9921 return this.proxy.toString(); 9922 }; 9923 9924 /** 9925 * Convert the current date to an 8-digit integer (%Y%m%d) 9926 * 9927 * @returns {Integer} 9928 */ 9929 9930 jsDate.prototype.toYmdInt = function() { 9931 return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate(); 9932 }; 9933 9934 /** 9935 * @namespace Holds localizations for month/day names. 9936 * <p>jsDate attempts to detect locale when loaded and defaults to 'en'. 9937 * If a localization is detected which is not available, jsDate defaults to 'en'. 9938 * Additional localizations can be added after jsDate loads. After adding a localization, 9939 * call the jsDate.regional.getLocale() method. Currently, en, fr and de are defined.</p> 9940 * 9941 * <p>Localizations must be an object and have the following properties defined: monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p> 9942 * <pre class="code"> 9943 * jsDate.regional['en'] = { 9944 * monthNames : 'January February March April May June July August September October November December'.split(' '), 9945 * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), 9946 * dayNames : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), 9947 * dayNamesShort : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ') 9948 * }; 9949 * </pre> 9950 * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the 9951 * new localizations.</p> 9952 */ 9953 9954 jsDate.regional = { 9955 'en': { 9956 monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], 9957 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 9958 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 9959 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], 9960 formatString: '%Y-%m-%d %H:%M:%S' 9961 }, 9962 9963 'fr': { 9964 monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'], 9965 monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'], 9966 dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], 9967 dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], 9968 formatString: '%Y-%m-%d %H:%M:%S' 9969 }, 9970 9971 'de': { 9972 monthNames: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'], 9973 monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'], 9974 dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], 9975 dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], 9976 formatString: '%Y-%m-%d %H:%M:%S' 9977 }, 9978 9979 'es': { 9980 monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], 9981 monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'], 9982 dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], 9983 dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], 9984 formatString: '%Y-%m-%d %H:%M:%S' 9985 }, 9986 9987 'ru': { 9988 monthNames: ['ЯнваÑÑ','ФевÑалÑ','ÐаÑÑ','ÐпÑелÑ','Ðай','ÐÑнÑ','ÐÑлÑ','ÐвгÑÑÑ','СенÑÑбÑÑ','ÐкÑÑбÑÑ','ÐоÑбÑÑ','ÐекабÑÑ'], 9989 monthNamesShort: ['Янв','Фев','ÐаÑ','ÐпÑ','Ðай','ÐÑн','ÐÑл','Ðвг','Сен','ÐкÑ','ÐоÑ','Ðек'], 9990 dayNames: ['воÑкÑеÑенÑе','понеделÑник','вÑоÑник','ÑÑеда','ÑеÑвеÑг','пÑÑниÑа','ÑÑббоÑа'], 9991 dayNamesShort: ['вÑк','пнд','вÑÑ','ÑÑд','ÑÑв','пÑн','ÑбÑ'], 9992 formatString: '%Y-%m-%d %H:%M:%S' 9993 }, 9994 9995 'ar': { 9996 monthNames: ['ÙØ§ÙÙÙ Ø§ÙØ«Ø§ÙÙ', 'شباط', 'آذار', 'ÙÙØ³Ø§Ù', 'آذار', 'ØØ²ÙراÙ','ØªÙ ÙØ²', 'آب', 'Ø£ÙÙÙÙ', 'تشرÙÙ Ø§ÙØ£ÙÙ', 'تشرÙÙ Ø§ÙØ«Ø§ÙÙ', 'ÙØ§ÙÙÙ Ø§ÙØ£ÙÙ'], 9997 monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'], 9998 dayNames: ['Ø§ÙØ³Ø¨Øª', 'Ø§ÙØ£ØØ¯', 'Ø§ÙØ§Ø«ÙÙÙ', 'Ø§ÙØ«Ùاثاء', 'Ø§ÙØ£Ø±Ø¨Ø¹Ø§Ø¡', 'Ø§ÙØ®Ù ÙØ³', 'Ø§ÙØ¬Ù عة'], 9999 dayNamesShort: ['سبت', 'Ø£ØØ¯', 'اثÙÙÙ', 'Ø«ÙØ§Ø«Ø§Ø¡', 'أربعاء', 'Ø®Ù ÙØ³', 'ج٠عة'], 10000 formatString: '%Y-%m-%d %H:%M:%S' 10001 }, 10002 10003 'pt': { 10004 monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], 10005 monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], 10006 dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], 10007 dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], 10008 formatString: '%Y-%m-%d %H:%M:%S' 10009 }, 10010 10011 'pt-BR': { 10012 monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], 10013 monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], 10014 dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], 10015 dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], 10016 formatString: '%Y-%m-%d %H:%M:%S' 10017 }, 10018 10019 'pl': {
10020 monthNames: ['StyczeÅ','Luty','Marzec','KwiecieÅ','Maj','Czerwiec','Lipiec','SierpieÅ','WrzesieÅ','Październik','Listopad','GrudzieÅ'], 10021 monthNamesShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze','Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'], 10022 dayNames: ['Niedziela', 'PoniedziaÅek', 'Wtorek', 'Åroda', 'Czwartek', 'PiÄ tek', 'Sobota'], 10023 dayNamesShort: ['Ni', 'Pn', 'Wt', 'År', 'Cz', 'Pt', 'Sb'], 10024 formatString: '%Y-%m-%d %H:%M:%S' 10025 }, 10026 10027 'nl': { 10028 monthNames: ['Januari','Februari','Maart','April','Mei','Juni','July','Augustus','September','Oktober','November','December'], 10029 monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Aug','Sep','Okt','Nov','Dec'], 10030 dayNames:','['Zondag','Maandag','Dinsdag','Woensdag','Donderdag','Vrijdag','Zaterdag'], 10031 dayNamesShort: ['Zo','Ma','Di','Wo','Do','Vr','Za'], 10032 formatString: '%Y-%m-%d %H:%M:%S' 10033 }, 10034 10035 'sv': { 10036 monthNames: ['januari','februari','mars','april','maj','juni','juli','augusti','september','oktober','november','december'], 10037 monthNamesShort: ['jan','feb','mar','apr','maj','jun','jul','aug','sep','okt','nov','dec'], 10038 dayNames: ['söndag','mÃ¥ndag','tisdag','onsdag','torsdag','fredag','lördag'], 10039 dayNamesShort: ['sön','mÃ¥n','tis','ons','tor','fre','lör'], 10040 formatString: '%Y-%m-%d %H:%M:%S' 10041 } 10042 10043 }; 10044 10045 // Set english variants to 'en' 10046 jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en']; 10047 10048 /** 10049 * Try to determine the users locale based on the lang attribute of the html page. Defaults to 'en' 10050 * if it cannot figure out a locale of if the locale does not have a localization defined. 10051 * @returns {String} locale 10052 */ 10053 10054 jsDate.regional.getLocale = function () { 10055 var l = jsDate.config.defaultLocale; 10056 10057 if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) { 10058 l = document.getElementsByTagName('html')[0].lang; 10059 if (!jsDate.regional.hasOwnProperty(l)) { 10060 l = jsDate.config.defaultLocale; 10061 } 10062 } 10063 10064 return l; 10065 }; 10066 10067 // ms in day 10068 var day = 24 * 60 * 60 * 1000; 10069 10070 // padd a number with zeros 10071 var addZeros = function(num, digits) { 10072 num = String(num); 10073 var i = digits - num.length; 10074 var s = String(Math.pow(10, i)).slice(1); 10075 return s.concat(num); 10076 }; 10077 10078 // representations used for calculating differences between dates. 10079 // This borrows heavily from Ken Snyder's work. 10080 var multipliers = { 10081 millisecond: 1, 10082 second: 1000, 10083 minute: 60 * 1000, 10084 hour: 60 * 60 * 1000, 10085 day: day, 10086 week: 7 * day, 10087 month: { 10088 // add a number of months 10089 add: function(d, number) { 10090 // add any years needed (increments of 12) 10091 multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12)); 10092 // ensure that we properly wrap betwen December and January 10093 // 11 % 12 = 11 10094 // 12 % 12 = 0 10095 var prevMonth = d.getMonth() + (number % 12); 10096 if (prevMonth == 12) { 10097 prevMonth = 0; 10098 d.setYear(d.getFullYear() + 1); 10099 } else if (prevMonth == -1) { 10100 prevMonth = 11; 10101 d.setYear(d.getFullYear() - 1); 10102 } 10103 d.setMonth(prevMonth); 10104 }, 10105 // get the number of months between two Date objects (decimal to the nearest day) 10106 diff: function(d1, d2) { 10107 // get the number of years 10108 var diffYears = d1.getFullYear() - d2.getFullYear(); 10109 // get the number of remaining months 10110 var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12); 10111 // get the number of remaining days 10112 var diffDays = d1.getDate() - d2.getDate(); 10113 // return the month difference with the days difference as a decimal 10114 return diffMonths + (diffDays / 30); 10115 } 10116 }, 10117 year: { 10118 // add a number of years 10119 add: function(d, number) { 10120 d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number)); 10121 }, 10122 // get the number of years between two Date objects (decimal to the nearest day) 10123 diff: function(d1, d2) { 10124 return multipliers.month.diff(d1, d2) / 12; 10125 } 10126 } 10127 }; 10128 // 10129 // Alias each multiplier with an 's' to allow 'year' and 'years' for example. 10130 // This comes from Ken Snyders work. 10131 // 10132 for (var unit in multipliers) { 10133 if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :| 10134 multipliers[unit + 's'] = multipliers[unit]; 10135 } 10136 } 10137 10138 // 10139 // take a jsDate instance and a format code and return the formatted value. 10140 // This is a somewhat modified version of Ken Snyder's method. 10141 // 10142 var format = function(d, code, syntax) { 10143 // if shorcut codes are used, recursively expand those. 10144 if (jsDate.formats[syntax]["shortcuts"][code]) { 10145 return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax); 10146 } else { 10147 // get the format code function and addZeros() argument 10148 var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.'); 10149 var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : ''; 10150 if (getter[1]) { 10151 nbr = addZeros(nbr, getter[1]); 10152 } 10153 return nbr; 10154 } 10155 }; 10156 10157 /** 10158 * @static 10159 * Static function for convert a date to a string according to a given format. Also acts as namespace for strftime format codes. 10160 * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p> 10161 * <pre class="code"> 10162 * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S'); 10163 * </pre> 10164 * @param {String | Number | Array | jsDate Object | Date Object} date A parsable date string, JavaScript time stamp, Array of form [year, month, day, hours, minutes, seconds, milliseconds], jsDate Object or Date object. 10165 * @param {String} formatString String with embedded date formatting codes. 10166 * See: {@link jsDate.formats}. 10167 * @param {String} syntax Optional syntax to use [default perl]. 10168 * @param {String} locale Optional locale to use. 10169 * @returns {String} Formatted representation of the date. 10170 */ 10171 // 10172 // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods. 10173 // 10174 jsDate.strftime = function(d, formatString, syntax, locale) { 10175 var syn = 'perl'; 10176 var loc = jsDate.regional.getLocale(); 10177 10178 // check if syntax and locale are available or reversed 10179 if (syntax && jsDate.formats.hasOwnProperty(syntax)) { 10180 syn = syntax; 10181 } 10182 else if (syntax && jsDate.regional.hasOwnProperty(syntax)) { 10183 loc = syntax; 10184 } 10185 10186 if (locale && jsDate.formats.hasOwnProperty(locale)) { 10187 syn = locale; 10188 } 10189 else if (locale && jsDate.regional.hasOwnProperty(locale)) { 10190 loc = locale; 10191 } 10192 10193 if (get_type(d) != "[object Object]" || d._type != "jsDate") { 10194 d = new jsDate(d); 10195 d.locale = loc; 10196 } 10197 if (!formatString) { 10198 formatString = d.formatString || jsDate.regional[loc]['formatString']; 10199 }
10200 // default the format string to year-month-day 10201 var source = formatString || '%Y-%m-%d', 10202 result = '', 10203 match; 10204 // replace each format code 10205 while (source.length > 0) { 10206 if (match = source.match(jsDate.formats[syn].codes.matcher)) { 10207 result += source.slice(0, match.index); 10208 result += (match[1] || '') + format(d, match[2], syn); 10209 source = source.slice(match.index + match[0].length); 10210 } else { 10211 result += source; 10212 source = ''; 10213 } 10214 } 10215 return result; 10216 }; 10217 10218 /** 10219 * @namespace 10220 * Namespace to hold format codes and format shortcuts. "perl" and "php" format codes 10221 * and shortcuts are defined by default. Additional codes and shortcuts can be 10222 * added like: 10223 * 10224 * <pre class="code"> 10225 * jsDate.formats["perl"] = { 10226 * "codes": { 10227 * matcher: /someregex/, 10228 * Y: "fullYear", // name of "get" method without the "get", 10229 * ..., // more codes 10230 * }, 10231 * "shortcuts": { 10232 * F: '%Y-%m-%d', 10233 * ..., // more shortcuts 10234 * } 10235 * }; 10236 * </pre> 10237 * 10238 * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via: 10239 * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code> 10240 */ 10241 10242 jsDate.formats = { 10243 ISO:'%Y-%m-%dT%H:%M:%S.%N%G', 10244 SQL:'%Y-%m-%d %H:%M:%S' 10245 }; 10246 10247 /** 10248 * Perl format codes and shortcuts for strftime. 10249 * 10250 * A hash (object) of codes where each code must be an array where the first member is 10251 * the name of a Date.prototype or jsDate.prototype function to call 10252 * and optionally a second member indicating the number to pass to addZeros() 10253 * 10254 * <p>The following format codes are defined:</p> 10255 * 10256 * <pre class="code"> 10257 * Code Result Description 10258 * == Years == 10259 * %Y 2008 Four-digit year 10260 * %y 08 Two-digit year 10261 * 10262 * == Months == 10263 * %m 09 Two-digit month 10264 * %#m 9 One or two-digit month 10265 * %B September Full month name 10266 * %b Sep Abbreviated month name 10267 * 10268 * == Days == 10269 * %d 05 Two-digit day of month 10270 * %#d 5 One or two-digit day of month 10271 * %e 5 One or two-digit day of month 10272 * %A Sunday Full name of the day of the week 10273 * %a Sun Abbreviated name of the day of the week 10274 * %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday) 10275 * 10276 * == Hours == 10277 * %H 23 Hours in 24-hour format (two digits) 10278 * %#H 3 Hours in 24-hour integer format (one or two digits) 10279 * %I 11 Hours in 12-hour format (two digits) 10280 * %#I 3 Hours in 12-hour integer format (one or two digits) 10281 * %p PM AM or PM 10282 * 10283 * == Minutes == 10284 * %M 09 Minutes (two digits) 10285 * %#M 9 Minutes (one or two digits) 10286 * 10287 * == Seconds == 10288 * %S 02 Seconds (two digits) 10289 * %#S 2 Seconds (one or two digits) 10290 * %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00) 10291 * 10292 * == Milliseconds == 10293 * %N 008 Milliseconds (three digits) 10294 * %#N 8 Milliseconds (one to three digits) 10295 * 10296 * == Timezone == 10297 * %O 360 difference in minutes between local time and GMT 10298 * %Z Mountain Standard Time Name of timezone as reported by browser 10299 * %G 06:00 Hours and minutes between GMT 10300 * 10301 * == Shortcuts == 10302 * %F 2008-03-26 %Y-%m-%d 10303 * %T 05:06:30 %H:%M:%S 10304 * %X 05:06:30 %H:%M:%S 10305 * %x 03/26/08 %m/%d/%y 10306 * %D 03/26/08 %m/%d/%y 10307 * %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y 10308 * %v 3-Sep-2008 %e-%b-%Y 10309 * %R 15:31 %H:%M 10310 * %r 03:31:00 PM %I:%M:%S %p 10311 * 10312 * == Characters == 10313 * %n \n Newline 10314 * %t \t Tab 10315 * %% % Percent Symbol 10316 * </pre> 10317 * 10318 * <p>Formatting shortcuts that will be translated into their longer version. 10319 * Be sure that format shortcuts do not refer to themselves: this will cause an infinite loop.</p> 10320 * 10321 * <p>Format codes and format shortcuts can be redefined after the jsDate 10322 * module is imported.</p> 10323 * 10324 * <p>Note that if you redefine the whole hash (object), you must supply a "matcher" 10325 * regex for the parser. The default matcher is:</p> 10326 * 10327 * <code>/()%(#?(%|[a-z]))/i</code> 10328 * 10329 * <p>which corresponds to the Perl syntax used by default.</p> 10330 * 10331 * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p> 10332 */ 10333 10334 jsDate.formats.perl = { 10335 codes: { 10336 // 10337 // 2-part regex matcher for format codes 10338 // 10339 // first match must be the character before the code (to account for escaping) 10340 // second match must be the format code character(s) 10341 // 10342 matcher: /()%(#?(%|[a-z]))/i, 10343 // year 10344 Y: 'FullYear', 10345 y: 'ShortYear.2', 10346 // month 10347 m: 'MonthNumber.2', 10348 '#m': 'MonthNumber', 10349 B: 'MonthName', 10350 b: 'AbbrMonthName', 10351 // day 10352 d: 'Date.2', 10353 '#d': 'Date', 10354 e: 'Date', 10355 A: 'DayName', 10356 a: 'AbbrDayName', 10357 w: 'Day', 10358 // hours 10359 H: 'Hours.2', 10360 '#H': 'Hours', 10361 I: 'Hours12.2', 10362 '#I': 'Hours12', 10363 p: 'AMPM', 10364 // minutes 10365 M: 'Minutes.2', 10366 '#M': 'Minutes', 10367 // seconds 10368 S: 'Seconds.2', 10369 '#S': 'Seconds', 10370 s: 'Unix', 10371 // milliseconds 10372 N: 'Milliseconds.3', 10373 '#N': 'Milliseconds', 10374 // timezone 10375 O: 'TimezoneOffset', 10376 Z: 'TimezoneName', 10377 G: 'GmtOffset' 10378 }, 10379 10380 shortcuts: { 10381 // date 10382 F: '%Y-%m-%d', 10383 // time 10384 T: '%H:%M:%S', 10385 X: '%H:%M:%S', 10386 // local format date 10387 x: '%m/%d/%y', 10388 D: '%m/%d/%y', 10389 // local format extended 10390 '#c': '%a %b %e %H:%M:%S %Y', 10391 // local format short 10392 v: '%e-%b-%Y', 10393 R: '%H:%M', 10394 r: '%I:%M:%S %p', 10395 // tab and newline 10396 t: '\t', 10397 n: '\n', 10398 '%': '%' 10399 } 10400 }; 10401 10402 /** 10403 * PHP format codes and shortcuts for strftime. 10404 * 10405 * A hash (object) of codes where each code must be an array where the first member is 10406 * the name of a Date.prototype or jsDate.prototype function to call 10407 * and optionally a second member indicating the number to pass to addZeros() 10408 * 10409 * <p>The following format codes are defined:</p> 10410 * 10411 * <pre class="code"> 10412 * Code Result Description 10413 * === Days === 10414 * %a Sun through Sat An abbreviated textual representation of the day 10415 * %A Sunday - Saturday A full textual representation of the day 10416 * %d 01 to 31 Two-digit day of the month (with leading zeros) 10417 * %e 1 to 31 Day of the month, with a space preceding single digits. 10418 * %j 001 to 366 Day of the year, 3 digits with leading zeros 10419 * %u 1 - 7 (Mon - Sun) ISO-8601 numeric representation of the day of the week 10420 * %w 0 - 6 (Sun - Sat) Numeric representation of the day of the week 10421 * 10422 * === Week === 10423 * %U 13 Full Week number, starting with the first Sunday as the first week 10424 * %V 01 through 53 ISO-8601:1988 week number, starting with the first week of the year 10425 * with at least 4 weekdays, with Monday being the start of the week 10426 * %W 46 A numeric representation of the week of the year, 10427 * starting with the first Monday as the first week 10428 * === Month === 10429 * %b Jan through Dec Abbreviated month name, based on the locale 10430 * %B January - December Full month name, based on the locale 10431 * %h Jan through Dec Abbreviated month name, based on the locale (an alias of %b) 10432 * %m 01 - 12 (Jan - Dec) Two digit representation of the month 10433 * 10434 * === Year === 10435 * %C 19 Two digit century (year/100, truncated to an integer) 10436 * %y 09 for 2009 Two digit year 10437 * %Y 2038 Four digit year 10438 * 10439 * === Time === 10440 * %H 00 through 23 Two digit representation of the hour in 24-hour format 10441 * %I 01 through 12 Two digit representation of the hour in 12-hour format 10442 * %l 1 through 12 Hour in 12-hour format, with a space preceeding single digits 10443 * %M 00 through 59 Two digit representation of the minute 10444 * %p AM/PM UPPER-CASE 'AM' or 'PM' based on the given time 10445 * %P am/pm lower-case 'am' or 'pm' based on the given time
10446 * %r 09:34:17 PM Same as %I:%M:%S %p 10447 * %R 00:35 Same as %H:%M 10448 * %S 00 through 59 Two digit representation of the second 10449 * %T 21:34:17 Same as %H:%M:%S 10450 * %X 03:59:16 Preferred time representation based on locale, without the date 10451 * %z -0500 or EST Either the time zone offset from UTC or the abbreviation 10452 * %Z -0500 or EST The time zone offset/abbreviation option NOT given by %z 10453 * 10454 * === Time and Date === 10455 * %D 02/05/09 Same as %m/%d/%y 10456 * %F 2009-02-05 Same as %Y-%m-%d (commonly used in database datestamps) 10457 * %s 305815200 Unix Epoch Time timestamp (same as the time() function) 10458 * %x 02/05/09 Preferred date representation, without the time 10459 * 10460 * === Miscellaneous === 10461 * %n --- A newline character (\n) 10462 * %t --- A Tab character (\t) 10463 * %% --- A literal percentage character (%) 10464 * </pre> 10465 */ 10466 10467 jsDate.formats.php = { 10468 codes: { 10469 // 10470 // 2-part regex matcher for format codes 10471 // 10472 // first match must be the character before the code (to account for escaping) 10473 // second match must be the format code character(s) 10474 // 10475 matcher: /()%((%|[a-z]))/i, 10476 // day 10477 a: 'AbbrDayName', 10478 A: 'DayName', 10479 d: 'Date.2', 10480 e: 'Date', 10481 j: 'DayOfYear.3', 10482 u: 'DayOfWeek', 10483 w: 'Day', 10484 // week 10485 U: 'FullWeekOfYear.2', 10486 V: 'IsoWeek.2', 10487 W: 'WeekOfYear.2', 10488 // month 10489 b: 'AbbrMonthName', 10490 B: 'MonthName', 10491 m: 'MonthNumber.2', 10492 h: 'AbbrMonthName', 10493 // year 10494 C: 'Century.2', 10495 y: 'ShortYear.2', 10496 Y: 'FullYear', 10497 // time 10498 H: 'Hours.2', 10499 I: 'Hours12.2', 10500 l: 'Hours12', 10501 p: 'AMPM', 10502 P: 'AmPm', 10503 M: 'Minutes.2', 10504 S: 'Seconds.2', 10505 s: 'Unix', 10506 O: 'TimezoneOffset', 10507 z: 'GmtOffset', 10508 Z: 'TimezoneAbbr' 10509 }, 10510 10511 shortcuts: { 10512 D: '%m/%d/%y', 10513 F: '%Y-%m-%d', 10514 T: '%H:%M:%S', 10515 X: '%H:%M:%S', 10516 x: '%m/%d/%y', 10517 R: '%H:%M', 10518 r: '%I:%M:%S %p', 10519 t: '\t', 10520 n: '\n', 10521 '%': '%' 10522 } 10523 }; 10524 // 10525 // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods. 10526 // I use his idea of a set of parsers which can be regular expressions or functions, 10527 // iterating through those, and then seeing if Date.parse() will create a date. 10528 // The parser expressions and functions are a little different and some bugs have been 10529 // worked out. Also, a lot of "pre-parsing" is done to fix implementation 10530 // variations of Date.parse() between browsers. 10531 // 10532 jsDate.createDate = function(date) { 10533 // if passing in multiple arguments, try Date constructor 10534 if (date == null) { 10535 return new Date(); 10536 } 10537 // If the passed value is already a date object, return it 10538 if (date instanceof Date) { 10539 return date; 10540 } 10541 // if (typeof date == 'number') return new Date(date * 1000); 10542 // If the passed value is an integer, interpret it as a javascript timestamp 10543 if (typeof date == 'number') { 10544 return new Date(date); 10545 } 10546 10547 // Before passing strings into Date.parse(), have to normalize them for certain conditions. 10548 // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent. 10549 // 10550 // For example: 10551 // * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century. 10552 // * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse. 10553 // * Both FF, Chrome and Opera will parse '1984/1/25' into localtime. 10554
10555 // remove leading and trailing spaces 10556 var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1'); 10557 10558 // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn] 10559 parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3"); 10560 10561 ///////// 10562 // Need to check for '15-Dec-09' also. 10563 // FF will not parse, but Chrome will. 10564 // Chrome will set date to 2009 as well. 10565 ///////// 10566 10567 // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010' 10568 parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3"); 10569 10570 // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to default century. 10571 var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i); 10572 if (match && match.length > 3) { 10573 var m3 = parseFloat(match[3]); 10574 var ny = jsDate.config.defaultCentury + m3; 10575 ny = String(ny); 10576 10577 // now replace 2 digit year with 4 digit year 10578 parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny); 10579 10580 } 10581 10582 // Check for '1/19/70 8:14PM' 10583 // where starts with mm/dd/yy or yy/mm/dd and have something after 10584 // Check if 1st postiion is greater than 31, assume it is year. 10585 // Assme all 2 digit years are 1900's. 10586 // Finally, change them into US style mm/dd/yyyy representations. 10587 match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/); 10588 10589 function h1(parsable, match) { 10590 var m1 = parseFloat(match[1]); 10591 var m2 = parseFloat(match[2]); 10592 var m3 = parseFloat(match[3]); 10593 var cent = jsDate.config.defaultCentury; 10594 var ny, nd, nm, str; 10595 10596 if (m1 > 31) { // first number is a year 10597 nd = m3; 10598 nm = m2; 10599 ny = cent + m1; 10600 } 10601 10602 else { // last number is the year 10603 nd = m2; 10604 nm = m1; 10605 ny = cent + m3; 10606 } 10607 10608 str = nm+'/'+nd+'/'+ny; 10609 10610 // now replace 2 digit year with 4 digit year 10611 return parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str); 10612 10613 } 10614 10615 if (match && match.length > 3) { 10616 parsable = h1(parsable, match); 10617 } 10618 10619 // Now check for '1/19/70' with nothing after and do as above 10620 var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/); 10621 10622 if (match && match.length > 3) { 10623 parsable = h1(parsable, match); 10624 } 10625 10626 10627 var i = 0; 10628 var length = jsDate.matchers.length; 10629 var pattern, 10630 ms, 10631 current = parsable, 10632 obj; 10633 while (i < length) { 10634 ms = Date.parse(current); 10635 if (!isNaN(ms)) { 10636 return new Date(ms); 10637 } 10638 pattern = jsDate.matchers[i]; 10639 if (typeof pattern == 'function') { 10640 obj = pattern.call(jsDate, current); 10641 if (obj instanceof Date) { 10642 return obj; 10643 } 10644 } else { 10645 current = parsable.replace(pattern[0], pattern[1]); 10646 } 10647 i++; 10648 } 10649 return NaN; 10650 }; 10651 10652 10653 /** 10654 * @static 10655 * Handy static utility function to return the number of days in a given month. 10656 * @param {Integer} year Year 10657 * @param {Integer} month Month (1-12) 10658 * @returns {Integer} Number of days in the month. 10659 */ 10660 // 10661 // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods. 10662 // 10663 jsDate.daysInMonth = function(year, month) { 10664 if (month == 2) { 10665 return new Date(year, 1, 29).getDate() == 29 ? 29 : 28; 10666 } 10667 return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month]; 10668 }; 10669 10670 10671 // 10672 // An Array of regular expressions or functions that will attempt to match the date string. 10673 // Functions are called with scope of a jsDate instance. 10674 // 10675 jsDate.matchers = [ 10676 // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date). 10677 [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'], 10678 // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date). 10679 [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'], 10680 // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part. 10681 function(str) { 10682 var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i); 10683 // opt. date hour opt. minute opt. second opt. msec opt. am or pm 10684 if (match) { 10685 if (match[1]) { 10686 var d = this.createDate(match[1]); 10687 if (isNaN(d)) { 10688 return; 10689 } 10690 } else { 10691 var d = new Date(); 10692 d.setMilliseconds(0); 10693 } 10694 var hour = parseFloat(match[2]); 10695 if (match[6]) { 10696 hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12); 10697 } 10698 d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000); 10699 return d; 10700 } 10701 else { 10702 return str; 10703 } 10704 }, 10705 // Handle ISO timestamp with time zone. 10706 function(str) {
10707 var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i); 10708 if (match) { 10709 if (match[1]) { 10710 var d = this.createDate(match[1]); 10711 if (isNaN(d)) { 10712 return; 10713 } 10714 } else { 10715 var d = new Date(); 10716 d.setMilliseconds(0); 10717 } 10718 var hour = parseFloat(match[2]); 10719 d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000); 10720 return d; 10721 } 10722 else { 10723 return str; 10724 } 10725 }, 10726 // Try to match ambiguous strings like 12/8/22. 10727 // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's). 10728 // This may be redundant with pre processing of date already performed. 10729 function(str) { 10730 var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/); 10731 if (match) { 10732 var d = new Date(); 10733 var cent = jsDate.config.defaultCentury; 10734 var m1 = parseFloat(match[1]); 10735 var m3 = parseFloat(match[3]); 10736 var ny, nd, nm; 10737 if (m1 > 31) { // first number is a year 10738 nd = m3; 10739 ny = cent + m1; 10740 } 10741 10742 else { // last number is the year 10743 nd = m1; 10744 ny = cent + m3; 10745 } 10746 10747 var nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNamesShort"]); 10748 10749 if (nm == -1) { 10750 nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNames"]); 10751 } 10752 10753 d.setFullYear(ny, nm, nd); 10754 d.setHours(0,0,0,0); 10755 return d; 10756 } 10757 10758 else { 10759 return str; 10760 } 10761 } 10762 ]; 10763 10764 // 10765 // I think John Reisig published this method on his blog, ejohn. 10766 // 10767 function inArray( elem, array ) { 10768 if ( array.indexOf ) { 10769 return array.indexOf( elem ); 10770 } 10771 10772 for ( var i = 0, length = array.length; i < length; i++ ) { 10773 if ( array[ i ] === elem ) { 10774 return i; 10775 } 10776 } 10777 10778 return -1; 10779 } 10780 10781 // 10782 // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method. 10783 // 10784 function get_type(thing){ 10785 if(thing===null) return "[object Null]"; // special case 10786 return Object.prototype.toString.call(thing); 10787 } 10788 10789 $.jsDate = jsDate; 10790 10791 10792 /** 10793 * JavaScript printf/sprintf functions. 10794 * 10795 * This code has been adapted from the publicly available sprintf methods 10796 * by Ash Searle. His original header follows: 10797 * 10798 * This code is unrestricted: you are free to use it however you like. 10799 * 10800 * The functions should work as expected, performing left or right alignment, 10801 * truncating strings, outputting numbers with a required precision etc. 10802 * 10803 * For complex cases, these functions follow the Perl implementations of 10804 * (s)printf, allowing arguments to be passed out-of-order, and to set the 10805 * precision or length of the output based on arguments instead of fixed 10806 * numbers. 10807 * 10808 * See http://perldoc.perl.org/functions/sprintf.html for more information. 10809 * 10810 * Implemented: 10811 * - zero and space-padding 10812 * - right and left-alignment, 10813 * - base X prefix (binary, octal and hex) 10814 * - positive number prefix 10815 * - (minimum) width 10816 * - precision / truncation / maximum width 10817 * - out of order arguments 10818 * 10819 * Not implemented (yet): 10820 * - vector flag 10821 * - size (bytes, words, long-words etc.) 10822 * 10823 * Will not implement: 10824 * - %n or %p (no pass-by-reference in JavaScript) 10825 * 10826 * @version 2007.04.27 10827 * @author Ash Searle 10828 * 10829 * You can see the original work and comments on his blog:
10830 * http://hexmen.com/blog/2007/03/printf-sprintf/ 10831 * http://hexmen.com/js/sprintf.js 10832 */ 10833 10834 /** 10835 * @Modifications 2009.05.26 10836 * @author Chris Leonello 10837 * 10838 * Added %p %P specifier 10839 * Acts like %g or %G but will not add more significant digits to the output than present in the input. 10840 * Example: 10841 * Format: '%.3p', Input: 0.012, Output: 0.012 10842 * Format: '%.3g', Input: 0.012, Output: 0.0120 10843 * Format: '%.4p', Input: 12.0, Output: 12.0 10844 * Format: '%.4g', Input: 12.0, Output: 12.00 10845 * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5 10846 * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5 10847 * 10848 * Example: 10849 * >>> $.jqplot.sprintf('%.2f, %d', 23.3452, 43.23) 10850 * "23.35, 43" 10851 * >>> $.jqplot.sprintf("no value: %n, decimal with thousands separator: %'d", 23.3452, 433524) 10852 * "no value: , decimal with thousands separator: 433,524" 10853 */ 10854 $.jqplot.sprintf = function() { 10855 function pad(str, len, chr, leftJustify) { 10856 var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr); 10857 return leftJustify ? str + padding : padding + str; 10858 10859 } 10860 10861 function thousand_separate(value) { 10862 var value_str = new String(value); 10863 for (var i=10; i>0; i--) { 10864 if (value_str == (value_str = value_str.replace(/^(\d+)(\d{3})/, "$1"+$.jqplot.sprintf.thousandsSeparator+"$2"))) break; 10865 } 10866 return value_str; 10867 } 10868 10869 function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) { 10870 var diff = minWidth - value.length; 10871 if (diff > 0) { 10872 var spchar = ' '; 10873 if (htmlSpace) { spchar = ' '; } 10874 if (leftJustify || !zeroPad) { 10875 value = pad(value, minWidth, spchar, leftJustify); 10876 } else { 10877 value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length); 10878 } 10879 } 10880 return value; 10881 } 10882 10883 function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) { 10884 // Note: casts negative numbers to positive ones 10885 var number = value >>> 0; 10886 prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || ''; 10887 value = prefix + pad(number.toString(base), precision || 0, '0', false); 10888 return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); 10889 } 10890 10891 function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) { 10892 if (precision != null) { 10893 value = value.slice(0, precision); 10894 } 10895 return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace); 10896 } 10897 10898 var a = arguments, i = 0, format = a[i++]; 10899 10900 return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) { 10901 if (substring == '%%') { return '%'; } 10902 10903 // parse flags 10904 var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false, thousandSeparation = false; 10905 for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) { 10906 case ' ': positivePrefix = ' '; break; 10907 case '+': positivePrefix = '+'; break; 10908 case '-': leftJustify = true; break; 10909 case '0': zeroPad = true; break; 10910 case '#': prefixBaseX = true; break; 10911 case '&': htmlSpace = true; break; 10912 case '\'': thousandSeparation = true; break; 10913 } 10914 10915 // parameters may be null, undefined, empty-string or real valued 10916 // we want to ignore null, undefined and empty-string values 10917 10918 if (!minWidth) { 10919 minWidth = 0; 10920 } 10921 else if (minWidth == '*') { 10922 minWidth = +a[i++]; 10923 } 10924 else if (minWidth.charAt(0) == '*') { 10925 minWidth = +a[minWidth.slice(1, -1)]; 10926 } 10927 else { 10928 minWidth = +minWidth; 10929 } 10930 10931 // Note: undocumented perl feature: 10932 if (minWidth < 0) { 10933 minWidth = -minWidth; 10934 leftJustify = true; 10935 } 10936 10937 if (!isFinite(minWidth)) { 10938 throw new Error('$.jqplot.sprintf: (minimum-)width must be finite'); 10939 } 10940 10941 if (!precision) { 10942 precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0); 10943 } 10944 else if (precision == '*') { 10945 precision = +a[i++]; 10946 } 10947 else if (precision.charAt(0) == '*') { 10948 precision = +a[precision.slice(1, -1)]; 10949 } 10950 else { 10951 precision = +precision; 10952 } 10953 10954 // grab value using valueIndex if required? 10955 var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++]; 10956 10957 switch (type) { 10958 case 's': { 10959 if (value == null) { 10960 return ''; 10961 } 10962 return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace); 10963 } 10964 case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace); 10965 case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace); 10966 case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); 10967 case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); 10968 case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase(); 10969 case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); 10970 case 'i': { 10971 var number = parseInt(+value, 10); 10972 if (isNaN(number)) { 10973 return ''; 10974 } 10975 var prefix = number < 0 ? '-' : positivePrefix; 10976 var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number)); 10977 value = prefix + pad(number_str, precision, '0', false); 10978 //value = prefix + pad(String(Math.abs(number)), precision, '0', false); 10979 return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); 10980 } 10981 case 'd': { 10982 var number = Math.round(+value); 10983 if (isNaN(number)) { 10984 return ''; 10985 } 10986 var prefix = number < 0 ? '-' : positivePrefix; 10987 var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number)); 10988 value = prefix + pad(number_str, precision, '0', false); 10989 return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); 10990 } 10991 case 'e': 10992 case 'E': 10993 case 'f': 10994 case 'F': 10995 case 'g': 10996 case 'G': 10997 { 10998 var number = +value; 10999 if (isNaN(number)) { 11000 return ''; 11001 } 11002 var prefix = number < 0 ? '-' : positivePrefix; 11003 var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())]; 11004 var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2]; 11005 var number_str = Math.abs(number)[method](precision); 11006 11007 // Apply the decimal mark properly by splitting the number by the 11008 // decimalMark, applying thousands separator, and then placing it 11009 // back in. 11010 var parts = number_str.toString().split('.'); 11011 parts[0] = thousandSeparation ? thousand_separate(parts[0]) : parts[0]; 11012 number_str = parts.join($.jqplot.sprintf.decimalMark); 11013 11014 value = prefix + number_str; 11015 var justified = justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform](); 11016 11017 return justified; 11018 } 11019 case 'p': 11020 case 'P': 11021 { 11022 // make sure number is a number 11023 var number = +value; 11024 if (isNaN(number)) { 11025 return ''; 11026 } 11027 var prefix = number < 0 ? '-' : positivePrefix; 11028 11029 var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/); 11030 var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : String(number).length; 11031 var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0; 11032 11033 if (Math.abs(number) < 1) { 11034 if (sd + zeros <= precision) { 11035 value = prefix + Math.abs(number).toPrecision(sd); 11036 } 11037 else { 11038 if (sd <= precision - 1) { 11039 value = prefix + Math.abs(number).toExponential(sd-1); 11040 } 11041 else { 11042 value = prefix + Math.abs(number).toExponential(precision-1); 11043 } 11044 } 11045 } 11046 else { 11047 var prec = (sd <= precision) ? sd : precision; 11048 value = prefix + Math.abs(number).toPrecision(prec); 11049 } 11050 var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2]; 11051 return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform](); 11052 } 11053 case 'n': return ''; 11054 default: return substring; 11055 } 11056 }); 11057 }; 11058 11059 $.jqplot.sprintf.thousandsSeparator = ','; 11060 // Specifies the decimal mark for floating point values. By default a period '.' 11061 // is used. If you change this value to for example a comma be sure to also 11062 // change the thousands separator or else this won't work since a simple String 11063 // replace is used (replacing all periods with the mark specified here). 11064 $.jqplot.sprintf.decimalMark = '.'; 11065 11066 $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g; 11067 11068 $.jqplot.getSignificantFigures = function(number) { 11069 var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/); 11070 // total significant digits 11071 var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length; 11072 var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0; 11073 // exponent 11074 var expn = parseInt(parts[1], 10); 11075 // digits to the left of the decimal place 11076 var dleft = (expn + 1 > 0) ? expn + 1 : 0; 11077 // digits to the right of the decimal place 11078 var dright = (sd <= dleft) ? 0 : sd - expn - 1; 11079 return {significantDigits: sd, digitsLeft: dleft, digitsRight: dright, zeros: zeros, exponent: expn} ; 11080 }; 11081 11082 $.jqplot.getPrecision = function(number) { 11083 return $.jqplot.getSignificantFigures(number).digitsRight; 11084 }; 11085 11086 11087 11088 11089 var backCompat = $.uiBackCompat !== false; 11090 11091 $.jqplot.effects = { 11092 effect: {} 11093 }; 11094 11095 // prefix used for storing data on .data() 11096 var dataSpace = "jqplot.storage."; 11097 11098 /******************************************************************************/ 11099 /*********************************** EFFECTS **********************************/ 11100 /******************************************************************************/ 11101 11102 $.extend( $.jqplot.effects, { 11103 version: "1.9pre", 11104 11105 // Saves a set of properties in a data storage 11106 save: function( element, set ) { 11107 for( var i=0; i < set.length; i++ ) { 11108 if ( set[ i ] !== null ) { 11109 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); 11110 } 11111 } 11112 }, 11113 11114 // Restores a set of previously saved properties from a data storage 11115 restore: function( element, set ) { 11116 for( var i=0; i < set.length; i++ ) { 11117 if ( set[ i ] !== null ) { 11118 element.css( set[ i ], element.data( dataSpace + set[ i ] ) ); 11119 } 11120 } 11121 }, 11122 11123 setMode: function( el, mode ) { 11124 if (mode === "toggle") { 11125 mode = el.is( ":hidden" ) ? "show" : "hide"; 11126 } 11127 return mode; 11128 }, 11129
vendor: 4,910 bytes, lines 11130-11274
11130 // Wraps the element around a wrapper that copies position properties 11131 createWrapper: function( element ) { 11132 11133 // if the element is already wrapped, return it 11134 if ( element.parent().is( ".ui-effects-wrapper" )) { 11135 return element.parent(); 11136 } 11137 11138 // wrap the element 11139 var props = { 11140 width: element.outerWidth(true), 11141 height: element.outerHeight(true), 11142 "float": element.css( "float" ) 11143 }, 11144 wrapper = $( "<div></div>" ) 11145 .addClass( "ui-effects-wrapper" ) 11146 .css({ 11147 fontSize: "100%", 11148 background: "transparent", 11149 border: "none", 11150 margin: 0, 11151 padding: 0 11152 }), 11153 // Store the size in case width/height are defined in % - Fixes #5245 11154 size = { 11155 width: element.width(), 11156 height: element.height() 11157 }, 11158 active = document.activeElement; 11159 11160 element.wrap( wrapper ); 11161 11162 // Fixes #7595 - Elements lose focus when wrapped. 11163 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { 11164 $( active ).focus(); 11165 } 11166 11167 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element 11168 11169 // transfer positioning properties to the wrapper 11170 if ( element.css( "position" ) === "static" ) { 11171 wrapper.css({ position: "relative" }); 11172 element.css({ position: "relative" }); 11173 } else { 11174 $.extend( props, { 11175 position: element.css( "position" ), 11176 zIndex: element.css( "z-index" ) 11177 }); 11178 $.each([ "top", "left", "bottom", "right" ], function(i, pos) { 11179 props[ pos ] = element.css( pos ); 11180 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { 11181 props[ pos ] = "auto"; 11182 } 11183 }); 11184 element.css({ 11185 position: "relative", 11186 top: 0, 11187 left: 0, 11188 right: "auto", 11189 bottom: "auto" 11190 }); 11191 } 11192 element.css(size); 11193 11194 return wrapper.css( props ).show(); 11195 }, 11196 11197 removeWrapper: function( element ) { 11198 var active = document.activeElement; 11199 11200 if ( element.parent().is( ".ui-effects-wrapper" ) ) { 11201 element.parent().replaceWith( element ); 11202 11203 // Fixes #7595 - Elements lose focus when wrapped. 11204 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { 11205 $( active ).focus(); 11206 } 11207 } 11208 11209 11210 return element; 11211 } 11212 }); 11213 11214 // return an effect options object for the given parameters: 11215 function _normalizeArguments( effect, options, speed, callback ) { 11216 11217 // short path for passing an effect options object: 11218 if ( $.isPlainObject( effect ) ) { 11219 return effect; 11220 } 11221 11222 // convert to an object 11223 effect = { effect: effect }; 11224 11225 // catch (effect) 11226 if ( options === undefined ) { 11227 options = {}; 11228 } 11229 11230 // catch (effect, callback) 11231 if ( $.isFunction( options ) ) { 11232 callback = options; 11233 speed = null; 11234 options = {}; 11235 } 11236 11237 // catch (effect, speed, ?) 11238 if ( $.type( options ) === "number" || $.fx.speeds[ options ]) { 11239 callback = speed; 11240 speed = options; 11241 options = {}; 11242 } 11243 11244 // catch (effect, options, callback) 11245 if ( $.isFunction( speed ) ) { 11246 callback = speed; 11247 speed = null; 11248 } 11249 11250 // add options to effect 11251 if ( options ) { 11252 $.extend( effect, options ); 11253 } 11254 11255 speed = speed || options.duration; 11256 effect.duration = $.fx.off ? 0 : typeof speed === "number" 11257 ? speed : speed in $.fx.speeds ? $.fx.speeds[ speed ] : $.fx.speeds._default; 11258 11259 effect.complete = callback || options.complete; 11260 11261 return effect; 11262 } 11263 11264 function standardSpeed( speed ) { 11265 // valid standard speeds 11266 if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) { 11267 return true; 11268 } 11269 11270 // invalid strings - treat as "normal" speed 11271 if ( typeof speed === "string" && !$.jqplot.effects.effect[ speed ] ) { 11272 // TODO: remove in 2.0 (#7115) 11273 if ( backCompat && $.jqplot.effects[ speed ] ) { 11274 return false;
11275 } 11276 return true; 11277 } 11278 11279 return false; 11280 } 11281 11282 $.fn.extend({ 11283 jqplotEffect: function( effect, options, speed, callback ) { 11284 var args = _normalizeArguments.apply( this, arguments ), 11285 mode = args.mode, 11286 queue = args.queue, 11287 effectMethod = $.jqplot.effects.effect[ args.effect ], 11288 11289 // DEPRECATED: remove in 2.0 (#7115) 11290 oldEffectMethod = !effectMethod && backCompat && $.jqplot.effects[ args.effect ]; 11291 11292 if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) { 11293 // delegate to the original method (e.g., .show()) if possible 11294 if ( mode ) { 11295 return this[ mode ]( args.duration, args.complete ); 11296 } else { 11297 return this.each( function() { 11298 if ( args.complete ) { 11299 args.complete.call( this ); 11300 } 11301 }); 11302 } 11303 } 11304 11305 function run( next ) { 11306 var elem = $( this ), 11307 complete = args.complete, 11308 mode = args.mode; 11309 11310 function done() { 11311 if ( $.isFunction( complete ) ) { 11312 complete.call( elem[0] ); 11313 } 11314 if ( $.isFunction( next ) ) { 11315 next(); 11316 } 11317 } 11318 11319 // if the element is hiddden and mode is hide, 11320 // or element is visible and mode is show 11321 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { 11322 done(); 11323 } else { 11324 effectMethod.call( elem[0], args, done ); 11325 } 11326 } 11327 11328 // TODO: remove this check in 2.0, effectMethod will always be true 11329 if ( effectMethod ) { 11330 return queue === false ? this.each( run ) : this.queue( queue || "fx", run ); 11331 } else { 11332 // DEPRECATED: remove in 2.0 (#7115) 11333 return oldEffectMethod.call(this, { 11334 options: args, 11335 duration: args.duration, 11336 callback: args.complete, 11337 mode: args.mode 11338 }); 11339 } 11340 } 11341 }); 11342 11343 11344 11345 11346 var rvertical = /up|down|vertical/, 11347 rpositivemotion = /up|left|vertical|horizontal/; 11348 11349 $.jqplot.effects.effect.blind = function( o, done ) { 11350 // Create element 11351 var el = $( this ), 11352 props = [ "position", "top", "bottom", "left", "right", "height", "width" ], 11353 mode = $.jqplot.effects.setMode( el, o.mode || "hide" ), 11354 direction = o.direction || "up", 11355 vertical = rvertical.test( direction ), 11356 ref = vertical ? "height" : "width", 11357 ref2 = vertical ? "top" : "left", 11358 motion = rpositivemotion.test( direction ), 11359 animation = {}, 11360 show = mode === "show", 11361 wrapper, distance, top; 11362 11363 // // if already wrapped, the wrapper's properties are my property. #6245 11364 if ( el.parent().is( ".ui-effects-wrapper" ) ) { 11365 $.jqplot.effects.save( el.parent(), props ); 11366 } else { 11367 $.jqplot.effects.save( el, props ); 11368 } 11369 el.show(); 11370 top = parseInt(el.css('top'), 10); 11371 wrapper = $.jqplot.effects.createWrapper( el ).css({ 11372 overflow: "hidden" 11373 }); 11374 11375 distance = vertical ? wrapper[ ref ]() + top : wrapper[ ref ](); 11376 11377 animation[ ref ] = show ? String(distance) : '0'; 11378 if ( !motion ) { 11379 el 11380 .css( vertical ? "bottom" : "right", 0 ) 11381 .css( vertical ? "top" : "left", "" ) 11382 .css({ position: "absolute" }); 11383 animation[ ref2 ] = show ? '0' : String(distance); 11384 } 11385 11386 // // start at 0 if we are showing 11387 if ( show ) { 11388 wrapper.css( ref, 0 ); 11389 if ( ! motion ) { 11390 wrapper.css( ref2, distance ); 11391 } 11392 } 11393 11394 // // Animate 11395 wrapper.animate( animation, { 11396 duration: o.duration, 11397 easing: o.easing, 11398 queue: false, 11399 complete: function() { 11400 if ( mode === "hide" ) { 11401 el.hide(); 11402 } 11403 $.jqplot.effects.restore( el, props ); 11404 $.jqplot.effects.removeWrapper( el ); 11405 done(); 11406 } 11407 }); 11408 11409 }; 11410 11411})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.