1/** 2 * Title: jqPlot Charts 3 * 4 * Pure JavaScript plotting plugin for jQuery. 5 * 6 * About: Version 7 * 8 * version: 1.0.9 9 * revision: d96a669 10 * 11 * About: Copyright & License 12 * 13 * Copyright (c) 2009-2016 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.9"; 248 $.jqplot.revision = "d96a669"; 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 299 var cctx = canvas.getContext('2d'); 300 301 var canvasBackingScale = 1; 302 if (window.devicePixelRatio > 1 && (cctx.webkitBackingStorePixelRatio === undefined || 303 cctx.webkitBackingStorePixelRatio < 2)) { 304 canvasBackingScale = window.devicePixelRatio; 305 } 306 var oldWidth = canvas.width; 307 var oldHeight = canvas.height; 308 309 canvas.width = canvasBackingScale * canvas.width; 310 canvas.height = canvasBackingScale * canvas.height; 311 canvas.style.width = oldWidth + 'px'; 312 canvas.style.height = oldHeight + 'px'; 313 cctx.save(); 314 315 cctx.scale(canvasBackingScale, canvasBackingScale); 316 317 return canvas; 318 }; 319 320 this.freeAllCanvases = function() { 321 for (var i = 0, l=myCanvases.length; i < l; i++) { 322 this.freeCanvas(myCanvases[i]); 323 } 324 myCanvases = []; 325 }; 326 327 this.freeCanvas = function(idx) { 328 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 329 // excanvas can't be reused, but properly unset 330 window.G_vmlCanvasManager.uninitElement($.jqplot.CanvasManager.canvases[idx]); 331 $.jqplot.CanvasManager.canvases[idx] = null; 332 } 333 else { 334 var canvas = $.jqplot.CanvasManager.canvases[idx]; 335 canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); 336 $(canvas).unbind().removeAttr('class').removeAttr('style'); 337 // Style attributes seemed to be still hanging around. wierd. Some ticks 338 // still retained a left: 0px attribute after reusing a canvas. 339 $(canvas).css({left: '', top: '', position: ''}); 340 // setting size to 0 may save memory of unused canvases? 341 canvas.width = 0; 342 canvas.height = 0; 343 $.jqplot.CanvasManager.free[idx] = true; 344 } 345 }; 346 347 }; 348 349 350 // Convienence function that won't hang IE or FF without FireBug. 351 $.jqplot.log = function() { 352 if (window.console) { 353 window.console.log.apply(window.console, arguments); 354 } 355 }; 356 357 $.jqplot.config = { 358 addDomReference: false, 359 enablePlugins:false, 360 defaultHeight:300, 361 defaultWidth:400, 362 UTCAdjust:false, 363 timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000), 364 errorMessage: '',
365 errorBackground: '', 366 errorBorder: '', 367 errorFontFamily: '', 368 errorFontSize: '', 369 errorFontStyle: '', 370 errorFontWeight: '', 371 catchErrors: false, 372 defaultTickFormatString: "%.1f", 373 defaultColors: [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], 374 defaultNegativeColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"], 375 dashLength: 4, 376 gapLength: 4, 377 dotGapLength: 2.5, 378 srcLocation: 'jqplot/src/', 379 pluginLocation: 'jqplot/src/plugins/' 380 }; 381 382 383 $.jqplot.arrayMax = function( array ){ 384 return Math.max.apply( Math, array ); 385 }; 386 387 $.jqplot.arrayMin = function( array ){ 388 return Math.min.apply( Math, array ); 389 }; 390 391 $.jqplot.enablePlugins = $.jqplot.config.enablePlugins; 392 393 // canvas related tests taken from modernizer: 394 // Copyright (c) 2009 - 2010 Faruk Ates. 395 // http://www.modernizr.com 396 397 $.jqplot.support_canvas = function() { 398 if (typeof $.jqplot.support_canvas.result == 'undefined') { 399 $.jqplot.support_canvas.result = !!document.createElement('canvas').getContext; 400 } 401 return $.jqplot.support_canvas.result; 402 }; 403 404 $.jqplot.support_canvas_text = function() { 405 if (typeof $.jqplot.support_canvas_text.result == 'undefined') { 406 if (window.G_vmlCanvasManager !== undefined && window.G_vmlCanvasManager._version > 887) { 407 $.jqplot.support_canvas_text.result = true; 408 } 409 else { 410 $.jqplot.support_canvas_text.result = !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 411 } 412 413 } 414 return $.jqplot.support_canvas_text.result; 415 }; 416 417 $.jqplot.use_excanvas = ((!$.support.boxModel || !$.support.objectAll || !$support.leadingWhitespace) && !$.jqplot.support_canvas()) ? true : false; 418 419 /** 420 * 421 * Hooks: jqPlot Pugin Hooks 422 * 423 * $.jqplot.preInitHooks - called before initialization. 424 * $.jqplot.postInitHooks - called after initialization. 425 * $.jqplot.preParseOptionsHooks - called before user options are parsed. 426 * $.jqplot.postParseOptionsHooks - called after user options are parsed. 427 * $.jqplot.preDrawHooks - called before plot draw. 428 * $.jqplot.postDrawHooks - called after plot draw. 429 * $.jqplot.preDrawSeriesHooks - called before each series is drawn. 430 * $.jqplot.postDrawSeriesHooks - called after each series is drawn. 431 * $.jqplot.preDrawLegendHooks - called before the legend is drawn. 432 * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins 433 * can add rows to the legend table. 434 * $.jqplot.preSeriesInitHooks - called before series is initialized. 435 * $.jqplot.postSeriesInitHooks - called after series is initialized. 436 * $.jqplot.preParseSeriesOptionsHooks - called before series related options 437 * are parsed. 438 * $.jqplot.postParseSeriesOptionsHooks - called after series related options 439 * are parsed. 440 * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds 441 * listeners to the event canvas which lays on top of the grid area. 442 * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn. 443 * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn. 444 * 445 */ 446 447 $.jqplot.preInitHooks = []; 448 $.jqplot.postInitHooks = []; 449 $.jqplot.preParseOptionsHooks = []; 450 $.jqplot.postParseOptionsHooks = []; 451 $.jqplot.preDrawHooks = []; 452 $.jqplot.postDrawHooks = []; 453 $.jqplot.preDrawSeriesHooks = []; 454 $.jqplot.postDrawSeriesHooks = []; 455 $.jqplot.preDrawLegendHooks = []; 456 $.jqplot.addLegendRowHooks = []; 457 $.jqplot.preSeriesInitHooks = []; 458 $.jqplot.postSeriesInitHooks = []; 459 $.jqplot.preParseSeriesOptionsHooks = []; 460 $.jqplot.postParseSeriesOptionsHooks = [];
461 $.jqplot.eventListenerHooks = []; 462 $.jqplot.preDrawSeriesShadowHooks = []; 463 $.jqplot.postDrawSeriesShadowHooks = []; 464 465 // A superclass holding some common properties and methods. 466 $.jqplot.ElemContainer = function() { 467 this._elem; 468 this._plotWidth; 469 this._plotHeight; 470 this._plotDimensions = {height:null, width:null}; 471 }; 472 473 $.jqplot.ElemContainer.prototype.createElement = function(el, offsets, clss, cssopts, attrib) { 474 this._offsets = offsets; 475 var klass = clss || 'jqplot'; 476 var elem = document.createElement(el); 477 this._elem = $(elem); 478 this._elem.addClass(klass); 479 this._elem.css(cssopts); 480 this._elem.attr(attrib); 481 // avoid memory leak; 482 elem = null; 483 return this._elem; 484 }; 485 486 $.jqplot.ElemContainer.prototype.getWidth = function() { 487 if (this._elem) { 488 return this._elem.outerWidth(true); 489 } 490 else { 491 return null; 492 } 493 }; 494 495 $.jqplot.ElemContainer.prototype.getHeight = function() { 496 if (this._elem) { 497 return this._elem.outerHeight(true); 498 } 499 else { 500 return null; 501 } 502 }; 503 504 $.jqplot.ElemContainer.prototype.getPosition = function() { 505 if (this._elem) { 506 return this._elem.position(); 507 } 508 else { 509 return {top:null, left:null, bottom:null, right:null}; 510 } 511 }; 512 513 $.jqplot.ElemContainer.prototype.getTop = function() { 514 return this.getPosition().top; 515 }; 516 517 $.jqplot.ElemContainer.prototype.getLeft = function() { 518 return this.getPosition().left; 519 }; 520 521 $.jqplot.ElemContainer.prototype.getBottom = function() { 522 return this._elem.css('bottom'); 523 }; 524 525 $.jqplot.ElemContainer.prototype.getRight = function() { 526 return this._elem.css('right'); 527 }; 528 529 530 /** 531 * Class: Axis 532 * An individual axis object. Cannot be instantiated directly, but created 533 * by the Plot object. Axis properties can be set or overridden by the 534 * options passed in from the user. 535 * 536 */ 537 function Axis(name) { 538 $.jqplot.ElemContainer.call(this); 539 // Group: Properties 540 // 541 // Axes options are specified within an axes object at the top level of the 542 // plot options like so: 543 // > { 544 // > axes: { 545 // > xaxis: {min: 5}, 546 // > yaxis: {min: 2, max: 8, numberTicks:4}, 547 // > x2axis: {pad: 1.5}, 548 // > y2axis: {ticks:[22, 44, 66, 88]} 549 // > } 550 // > } 551 // There are 2 x axes, 'xaxis' and 'x2axis', and 552 // 9 yaxes, 'yaxis', 'y2axis'. 'y3axis', ... Any or all of which may be specified. 553 this.name = name; 554 this._series = []; 555 // prop: show 556 // Wether to display the axis on the graph. 557 this.show = false; 558 // prop: tickRenderer 559 // A class of a rendering engine for creating the ticks labels displayed on the plot, 560 // See <$.jqplot.AxisTickRenderer>. 561 this.tickRenderer = $.jqplot.AxisTickRenderer; 562 // prop: tickOptions 563 // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options. 564 this.tickOptions = {}; 565 // prop: labelRenderer 566 // A class of a rendering engine for creating an axis label. 567 this.labelRenderer = $.jqplot.AxisLabelRenderer; 568 // prop: labelOptions 569 // Options passed to the label renderer. 570 this.labelOptions = {}; 571 // prop: label 572 // Label for the axis 573 this.label = null; 574 // prop: showLabel 575 // true to show the axis label. 576 this.showLabel = true; 577 // prop: min 578 // minimum value of the axis (in data units, not pixels). 579 this.min = null; 580 // prop: max 581 // maximum value of the axis (in data units, not pixels). 582 this.max = null; 583 // prop: autoscale 584 // DEPRECATED 585 // the default scaling algorithm produces superior results. 586 this.autoscale = false; 587 // prop: pad 588 // Padding to extend the range above and below the data bounds. 589 // The data range is multiplied by this factor to determine minimum and maximum axis boun
589ds. 590 // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0. 591 this.pad = 1.2; 592 // prop: padMax 593 // Padding to extend the range above data bounds. 594 // The top of the data range is multiplied by this factor to determine maximum axis bounds. 595 // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0. 596 this.padMax = null; 597 // prop: padMin 598 // Padding to extend the range below data bounds. 599 // The bottom of the data range is multiplied by this factor to determine minimum axis bounds. 600 // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0. 601 this.padMin = null; 602 // prop: ticks 603 // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis. 604 // If no label is specified, the value is formatted into an appropriate label. 605 this.ticks = []; 606 // prop: numberTicks 607 // Desired number of ticks. Default is to compute automatically. 608 this.numberTicks; 609 // prop: tickInterval 610 // number of units between ticks. Mutually exclusive with numberTicks. 611 this.tickInterval; 612 // prop: renderer 613 // A class of a rendering engine that handles tick generation, 614 // scaling input data to pixel grid units and drawing the axis element. 615 this.renderer = $.jqplot.LinearAxisRenderer; 616 // prop: rendererOptions 617 // renderer specific options. See <$.jqplot.LinearAxisRenderer> for options. 618 this.rendererOptions = {}; 619 // prop: showTicks 620 // Wether to show the ticks (both marks and labels) or not. 621 // Will not override showMark and showLabel options if specified on the ticks themselves. 622 this.showTicks = true; 623 // prop: showTickMarks 624 // Wether to show the tick marks (line crossing grid) or not. 625 // Overridden by showTicks and showMark option of tick itself. 626 this.showTickMarks = true; 627 // prop: showMinorTicks 628 // Wether or not to show minor ticks. This is renderer dependent. 629 this.showMinorTicks = true; 630 // prop: drawMajorGridlines 631 // True to draw gridlines for major axis ticks. 632 this.drawMajorGridlines = true; 633 // prop: drawMinorGridlines 634 // True to draw gridlines for minor ticks. 635 this.drawMinorGridlines = false; 636 // prop: drawMajorTickMarks 637 // True to draw tick marks for major axis ticks. 638 this.drawMajorTickMarks = true; 639 // prop: drawMinorTickMarks 640 // True to draw tick marks for minor ticks. This is renderer dependent. 641 this.drawMinorTickMarks = true; 642 // prop: useSeriesColor 643 // Use the color of the first series associated with this axis for the 644 // tick marks and line bordering this axis. 645 this.useSeriesColor = false; 646 // prop: borderWidth 647 // width of line stroked at the border of the axis. Defaults 648 // to the width of the grid boarder. 649 this.borderWidth = null; 650 // prop: borderColor 651 // color of the border adjacent to the axis. Defaults to grid border color. 652 this.borderColor = null; 653 // prop: scaleToHiddenSeries 654 // True to include hidden series when computing axes bounds and scaling. 655 this.scaleToHiddenSeries = false; 656 // minimum and maximum values on the axis. 657 this._dataBounds = {min:null, max:null}; 658 // statistics (min, max, mean) as well as actual data intervals for each series attached to axis. 659 // holds collection of {intervals:[], min:, max:, mean: } objects for each series on axis. 660 this._intervalStats = []; 661 // pixel position from the top left of the min value and max value on the axis. 662 this._offsets = {min:null, max:null}; 663 this._ticks=[]; 664 this._label = null; 665 // prop: syncTicks 666 // true to try and synchronize tick spacing across multiple axes so that ticks and 667 // grid lines line up. This has an impact on autoscaling algorithm, however. 668 // In general, autoscaling an individual axis will work better if it does not 669 // have to sync ticks. 670 this.syncTicks = null; 671 // prop: tickSpacing 672 // Approximate pixel spacing between ticks on graph. Used during autoscaling. 673 // This number will be an upper bound, actual spacing will be less. 674 this.tickSpacing = 75; 675 // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks 676 // so they can be restored if altered by plugins. 677 this._min = null; 678 this._max = null; 679 this._tickInterval = null; 680 this._numberTicks = null; 681 this.__ticks = null; 682 // hold original user options. 683 this._options = {}; 684 } 685 686 Axis.prototype = new $.jqplot.ElemContainer(); 687 Axis.prototype.constructor = Axis; 688 689 Axis.prototype.init = function() { 690 if ($.isFunction(this.renderer)) { 691 this.renderer = new this.renderer(); 692 } 693 // set the axis name 694 this.tickOptions.axis = this.name; 695 // if showMark or showLabel tick options not specified, use value of axis option. 696 // showTicks overrides showTickMarks. 697 if (this.tickOptions.showMark == null) { 698 this.tickOptions.showMark = this.showTicks; 699 } 700 if (this.tickOptions.showMark == null) { 701 this.tickOptions.showMark = this.showTickMarks; 702 } 703 if (this.tickOptions.showLabel == null) { 704 this.tickOptions.showLabel = this.showTicks; 705 } 706 707 if (this.label == null || this.label == '') { 708 this.showLabel = false; 709 } 710 else { 711 this.labelOptions.label = this.label; 712 } 713 if (this.showLabel == false) { 714 this.labelOptions.show = false; 715 } 716 // set the default padMax, padMin if not specified 717 // special check, if no padding desired, padding 718 // should be set to 1.0 719 if (this.pad == 0) { 720 this.pad = 1.0; 721 } 722 if (this.padMax == 0) { 723 this.padMax = 1.0; 724 } 725 if (this.padMin == 0) { 726 this.padMin = 1.0; 727 } 728 if (this.padMax == null) { 729 this.padMax = (this.pad-1)/2 + 1; 730 } 731 if (this.padMin == null) { 732 this.padMin = (this.pad-1)/2 + 1; 733 } 734 // now that padMin and padMax are correctly set, reset pad in case user has supplied 735 // padMin and/or padMax 736 this.pad = this.padMax + this.padMin - 1; 737 if (this.min != null || this.max != null) { 738 this.autoscale = false; 739 } 740 // if not set, sync ticks for y axes but not x by default. 741 if (this.syncTicks == null && this.name.indexOf('y') > -1) { 742 this.syncTicks = true; 743 } 744 else if (this.syncTicks == null){ 745 this.syncTicks = false; 746 } 747 this.renderer.init.call(this, this.rendererOptions); 748 749 }; 750 751 Axis.prototype.draw = function(ctx, plot) { 752 // Memory Leaks patch 753 if (this.__ticks) { 754 this.__ticks = null; 755 } 756 757 return this.renderer.draw.call(this, ctx, plot); 758 759 }; 760 761 Axis.prototype.set = function() { 762 this.renderer.set.call(this); 763 }; 764 765 Axis.prototype.pack = function(pos, offsets) { 766 if (this.show) { 767 this.renderer.pack.call(this, pos, offsets); 768 } 769 // these properties should all be available now. 770 if (this._min == null) { 771 this._min = this.min; 772 this._max = this.max; 773 this._tickInterval = this.tickInterval; 774 this._numberTicks = this.numberTicks; 775 this.__ticks = this._ticks; 776 } 777 }; 778 779 // reset the axis back to original values if it has been scaled, zoomed, etc. 780 Axis.prototype.reset = function() { 781 this.renderer.reset.call(this); 782 }; 783 784 Axis.prototype.resetScale = function(opts) { 785 $.extend(true, this, {min: null, max: null, numberTicks: null, tickInterval: null, _ticks: [], ticks: []}, opts); 786 this.resetDataBounds(); 787 }; 788 789 Axis.prototype.resetDataBounds = function() { 790 // Go through all the series attached to this axis and find 791 // the min/max bounds for this axis. 792 var db = this._dataBounds; 793 db.min = null; 794 db.max = null; 795 var l, s, d; 796 // check for when to force min 0 on bar series plots. 797 var doforce = (this.show) ? true : false; 798 for (var i=0; i<this._series.length; i++) { 799 s = this._series[i];
800 if (s.show || this.scaleToHiddenSeries) { 801 d = s._plotData; 802 if (s._type === 'line' && s.renderer.bands.show && this.name.charAt(0) !== 'x') { 803 d = [[0, s.renderer.bands._min], [1, s.renderer.bands._max]]; 804 } 805 806 var minyidx = 1, maxyidx = 1; 807 808 if (s._type != null && s._type == 'ohlc') { 809 minyidx = 3; 810 maxyidx = 2; 811 } 812 813 for (var j=0, l=d.length; j<l; j++) { 814 if (this.name == 'xaxis' || this.name == 'x2axis') { 815 if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) { 816 db.min = d[j][0]; 817 } 818 if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) { 819 db.max = d[j][0]; 820 } 821 } 822 else { 823 if ((d[j][minyidx] != null && d[j][minyidx] < db.min) || db.min == null) { 824 db.min = d[j][minyidx]; 825 } 826 if ((d[j][maxyidx] != null && d[j][maxyidx] > db.max) || db.max == null) { 827 db.max = d[j][maxyidx]; 828 } 829 } 830 } 831 832 // Hack to not pad out bottom of bar plots unless user has specified a padding. 833 // every series will have a chance to set doforce to false. once it is set to 834 // false, it cannot be reset to true. 835 // If any series attached to axis is not a bar, wont force 0. 836 if (doforce && s.renderer.constructor !== $.jqplot.BarRenderer) { 837 doforce = false; 838 } 839 840 else if (doforce && this._options.hasOwnProperty('forceTickAt0') && this._options.forceTickAt0 == false) { 841 doforce = false; 842 } 843 844 else if (doforce && s.renderer.constructor === $.jqplot.BarRenderer) { 845 if (s.barDirection == 'vertical' && this.name != 'xaxis' && this.name != 'x2axis') { 846 if (this._options.pad != null || this._options.padMin != null) { 847 doforce = false; 848 } 849 } 850 851 else if (s.barDirection == 'horizontal' && (this.name == 'xaxis' || this.name == 'x2axis')) { 852 if (this._options.pad != null || this._options.padMin != null) { 853 doforce = false; 854 } 855 } 856 857 } 858 } 859 } 860 861 if (doforce && this.renderer.constructor === $.jqplot.LinearAxisRenderer && db.min >= 0) { 862 this.padMin = 1.0; 863 this.forceTickAt0 = true; 864 } 865 }; 866 867 /** 868 * Class: Legend 869 * Legend object. Cannot be instantiated directly, but created 870 * by the Plot object. Legend properties can be set or overridden by the 871 * options passed in from the user. 872 */ 873 function Legend(options) { 874 $.jqplot.ElemContainer.call(this); 875 // Group: Properties 876 877 // prop: show 878 // Wether to display the legend on the graph. 879 this.show = false; 880 // prop: location 881 // Placement of the legend. one of the compass directions: nw, n, ne, e, se, s, sw, w 882 this.location = 'ne'; 883 // prop: labels 884 // Array of labels to use. By default the renderer will look for labels on the series. 885 // Labels specified in this array will override labels specified on the series. 886 this.labels = []; 887 // prop: showLabels 888 // true to show the label text on the legend. 889 this.showLabels = true; 890 // prop: showSwatch 891 // true to show the color swatches on the legend. 892 this.showSwatches = true; 893 // prop: placement 894 // "insideGrid" places legend inside the grid area of the plot. 895 // "outsideGrid" places the legend outside the grid but inside the plot container, 896 // shrinking the grid to accomodate the legend. 897 // "inside" synonym for "insideGrid", 898 // "outside" places the legend ouside the grid area, but does not shrink the grid which 899 // can cause the legend to overflow the plot container. 900 this.placement = "insideGrid"; 901 // prop: xoffset 902 // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc. 903 // properties or via CSS margin styling of the .jqplot-table-legend class. 904 this.xoffset = 0; 905 // prop: yoffset 906 // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc. 907 // properties or via CSS margin styling of the .jqplot-table-legend class. 908 this.yoffset = 0; 909 // prop: border 910 // css spec for the border around the legend box. 911 this.border; 912 // prop: background 913 // css spec for the background of the legend box. 914 this.background; 915 // prop: textColor 916 // css color spec for the legend text. 917 this.textColor; 918 // prop: fontFamily 919 // css font-family spec for the legend text. 920 this.fontFamily; 921 // prop: fontSize 922 // css font-size spec for the legend text. 923 this.fontSize ; 924 // prop: rowSpacing 925 // css padding-top spec for the rows in the legend. 926 this.rowSpacing = '0.5em'; 927 // renderer 928 // A class that will create a DOM object for the legend, 929 // see <$.jqplot.TableLegendRenderer>. 930 this.renderer = $.jqplot.TableLegendRenderer; 931 // prop: rendererOptions 932 // renderer specific options passed to the renderer. 933 this.rendererOptions = {}; 934 // prop: predraw 935 // Wether to draw the legend before the series or not. 936 // Used with series specific legend renderers for pie, donut, mekko charts, etc. 937 this.preDraw = false; 938 // prop: marginTop 939 // CSS margin for the legend DOM element. This will set an element 940 // CSS style for the margin which will override any style sheet setting. 941 // The default will be taken from the stylesheet. 942 this.marginTop = null; 943 // prop: marginRight 944 // CSS margin for the legend DOM element. This will set an element 945 // CSS style for the margin which will override any style sheet setting. 946 // The default will be taken from the stylesheet. 947 this.marginRight = null; 948 // prop: marginBottom 949 // CSS margin for the legend DOM element. This will set an element 950 // CSS style for the margin which will override any style sheet setting. 951 // The default will be taken from the stylesheet. 952 this.marginBottom = null; 953 // prop: marginLeft 954 // CSS margin for the legend DOM element. This will set an element 955 // CSS style for the margin which will override any style sheet setting. 956 // The default will be taken from the stylesheet. 957 this.marginLeft = null; 958 // prop: escapeHtml 959 // True to escape special characters with their html entity equivalents 960 // in legend text. "<" becomes < and so on, so html tags are not rendered. 961 this.escapeHtml = false; 962 this._series = []; 963 964 $.extend(true, this, options); 965 } 966 967 Legend.prototype = new $.jqplot.ElemContainer(); 968 Legend.prototype.constructor = Legend; 969 970 Legend.prototype.setOptions = function(options) { 971 $.extend(true, this, options); 972 973 // Try to emulate deprecated behaviour 974 // if user has specified xoffset or yoffset, copy these to 975 // the margin properties. 976 977 if (this.placement == 'inside') { 978 this.placement = 'insideGrid'; 979 } 980 981 if (this.xoffset >0) { 982 if (this.placement == 'insideGrid') { 983 switch (this.location) { 984 case 'nw': 985 case 'w': 986 case 'sw': 987 if (this.marginLeft == null) { 988 this.marginLeft = this.xoffset + 'px'; 989 } 990 this.marginRight = '0px'; 991 break;
992 case 'ne': 993 case 'e': 994 case 'se': 995 default: 996 if (this.marginRight == null) { 997 this.marginRight = this.xoffset + 'px'; 998 } 999 this.marginLeft = '0px'; 1000 break; 1001 } 1002 } 1003 else if (this.placement == 'outside') { 1004 switch (this.location) { 1005 case 'nw': 1006 case 'w': 1007 case 'sw': 1008 if (this.marginRight == null) { 1009 this.marginRight = this.xoffset + 'px'; 1010 } 1011 this.marginLeft = '0px'; 1012 break; 1013 case 'ne': 1014 case 'e': 1015 case 'se': 1016 default: 1017 if (this.marginLeft == null) { 1018 this.marginLeft = this.xoffset + 'px'; 1019 } 1020 this.marginRight = '0px'; 1021 break; 1022 } 1023 } 1024 this.xoffset = 0; 1025 } 1026 1027 if (this.yoffset >0) { 1028 if (this.placement == 'outside') { 1029 switch (this.location) { 1030 case 'sw': 1031 case 's': 1032 case 'se': 1033 if (this.marginTop == null) { 1034 this.marginTop = this.yoffset + 'px'; 1035 } 1036 this.marginBottom = '0px'; 1037 break; 1038 case 'ne': 1039 case 'n': 1040 case 'nw': 1041 default: 1042 if (this.marginBottom == null) { 1043 this.marginBottom = this.yoffset + 'px'; 1044 } 1045 this.marginTop = '0px'; 1046 break; 1047 } 1048 } 1049 else if (this.placement == 'insideGrid') { 1050 switch (this.location) { 1051 case 'sw': 1052 case 's': 1053 case 'se': 1054 if (this.marginBottom == null) { 1055 this.marginBottom = this.yoffset + 'px'; 1056 } 1057 this.marginTop = '0px'; 1058 break; 1059 case 'ne': 1060 case 'n': 1061 case 'nw': 1062 default: 1063 if (this.marginTop == null) { 1064 this.marginTop = this.yoffset + 'px'; 1065 } 1066 this.marginBottom = '0px'; 1067 break; 1068 } 1069 } 1070 this.yoffset = 0; 1071 } 1072 1073 // TO-DO: 1074 // Handle case where offsets are < 0. 1075 // 1076 }; 1077 1078 Legend.prototype.init = function() { 1079 if ($.isFunction(this.renderer)) { 1080 this.renderer = new this.renderer(); 1081 } 1082 this.renderer.init.call(this, this.rendererOptions); 1083 }; 1084 1085 Legend.prototype.draw = function(offsets, plot) { 1086 for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){ 1087 $.jqplot.preDrawLegendHooks[i].call(this, offsets); 1088 } 1089 return this.renderer.draw.call(this, offsets, plot); 1090 }; 1091 1092 Legend.prototype.pack = function(offsets) { 1093 this.renderer.pack.call(this, offsets); 1094 }; 1095 1096 /** 1097 * Class: Title 1098 * Plot Title object. Cannot be instantiated directly, but created 1099 * by the Plot object. Title properties can be set or overridden by the 1100 * options passed in from the user. 1101 * 1102 * Parameters: 1103 * text - text of the title. 1104 */ 1105 function Title(text) { 1106 $.jqplot.ElemContainer.call(this); 1107 // Group: Properties 1108 1109 // prop: text 1110 // text of the title; 1111 this.text = text; 1112 // prop: show 1113 // whether or not to show the title 1114 this.show = true; 1115 // prop: fontFamily 1116 // css font-family spec for the text. 1117 this.fontFamily; 1118 // prop: fontSize 1119 // css font-size spec for the text. 1120 this.fontSize ; 1121 // prop: textAlign 1122 // css text-align spec for the text. 1123 this.textAlign; 1124 // prop: textColor 1125 // css color spec for the text. 1126 this.textColor; 1127 // prop: renderer 1128 // A class for creating a DOM element for the title, 1129 // see <$.jqplot.DivTitleRenderer>. 1130 this.renderer = $.jqplot.DivTitleRenderer; 1131 // prop: rendererOptions 1132 // renderer specific options passed to the renderer. 1133 this.rendererOptions = {}; 1134 // prop: escapeHtml 1135 // True to escape special characters with their html entity equivalents 1136 // in title text. "<" becomes < and so on, so html tags are not rendered. 1137 this.escapeHtml = false; 1138 } 1139 1140 Title.prototype = new $.jqplot.ElemContainer(); 1141 Title.prototype.constructor = Title; 1142 1143 Title.prototype.init = function() { 1144 if ($.isFunction(this.renderer)) { 1145 this.renderer = new this.renderer(); 1146 } 1147 this.renderer.init.call(this, this.rendererOptions); 1148 }; 1149 1150 Title.prototype.draw = function(width) { 1151 return this.renderer.draw.call(this, width); 1152 }; 1153 1154 Title.prototype.pack = function() { 1155 this.renderer.pack.call(this); 1156 }; 1157 1158 1159 /** 1160 * Class: Series 1161 * An individual data series object. Cannot be instantiated directly, but created 1162 * by the Plot object. Series properties can be set or overridden by the 1163 * options passed in from the user. 1164 */ 1165 function Series(options) { 1166 options = options || {}; 1167 $.jqplot.ElemContainer.call(this); 1168 // Group: Properties 1169 // Properties will be assigned from a series array at the top level of the 1170 // options. If you had two series and wanted to change the color and line 1171 // width of the first and set the second to use the secondary y axis with 1172 // no shadow and supply custom labels for each: 1173 // > { 1174 // > series:[ 1175 // > {color: '#ff4466', lineWidth: 5, label:'good line'}, 1176 // > {yaxis: 'y2axis', shadow: false, label:'bad line'} 1177 // > ] 1178 // > } 1179 1180 // prop: show 1181 // whether or not to draw the series. 1182 this.show = true; 1183 // prop: xaxis 1184 // which x axis to use with this series, either 'xaxis' or 'x2axis'. 1185 this.xaxis = 'xaxis'; 1186 this._xaxis; 1187 // prop: yaxis 1188 // which y axis to use with this series, either 'yaxis' or 'y2axis'. 1189 this.yaxis = 'yaxis'; 1190 this._yaxis; 1191 this.gridBorderWidth = 2.0; 1192 // prop: renderer 1193 // A class of a renderer which will draw the series, 1194 // see <$.jqplot.LineRenderer>. 1195 this.renderer = $.jqplot.LineRenderer; 1196 // prop: rendererOptions 1197 // Options to pass on to the renderer. 1198 this.rendererOptions = {}; 1199 this.data = []; 1200 this.gridData = []; 1201 // prop: label 1202 // Line label to use in the legend. 1203 this.label = ''; 1204 // prop: showLabel 1205 // true to show label for this series in the legend. 1206 this.showLabel = true; 1207 // prop: color 1208 // css color spec for the series 1209 this.color; 1210 // prop: negativeColor 1211 // css color spec used for filled (area) plots that are filled to zero and 1212 // the "useNegativeColors" option is true. 1213 this.negativeColor; 1214 // prop: lineWidth 1215 // width of the line in pixels. May have different meanings depending on renderer. 1216 this.lineWidth = 2.5; 1217 // prop: lineJoin 1218 // Canvas lineJoin style between segments of series. 1219 this.lineJoin = 'round'; 1220 // prop: lineCap 1221 // Canvas lineCap style at ends of line. 1222 this.lineCap = 'round'; 1223 // prop: linePattern 1224 // line pattern 'dashed', 'dotted', 'solid', some combination 1225 // of '-' and '.' characters such as '.-.' or a numerical array like 1226 // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, 1227 // [1, 10, 20, 10] to draw a dot-dash line, and so on. 1228 this.linePattern = 'solid'; 1229 this.shadow = true; 1230 // prop: shadowAngle 1231 // Shadow angle in degrees 1232 this.shadowAngle = 45; 1233 // prop: shadowOffset 1234 // Shadow offset from line in pixels 1235 this.shadowOffset = 1.25; 1236 // prop: shadowDepth 1237 // Number of times shadow is stroked, each stroke offset shadowOffset from the last. 1238 this.shadowDepth = 3; 1239 // prop: shadowAlpha 1240 // Alpha channel transparency of shadow. 0 = transparent. 1241 this.shadowAlpha = '0.1'; 1242 // prop: breakOnNull 1243 // Wether line segments should be be broken at null value. 1244 // False will join point on either side of line. 1245 this.breakOnNull = false; 1246 // prop: markerRenderer 1247 // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points, 1248 // see <$.jqplot.MarkerRenderer>. 1249 this.markerRenderer = $.jqplot.MarkerRenderer; 1250 // prop: markerOptions 1251 // renderer specific options to pass to the markerRenderer, 1252 // see <$.jqplot.MarkerRenderer>. 1253 this.markerOptions = {}; 1254 // prop: showLine 1255 // whether to actually draw the line or not. Series will still be renderered, even if no line is drawn. 1256 this.showLine = true; 1257 // prop: showMarker 1258 // whether or not to show the markers at the data points. 1259 this.showMarker = true; 1260 // prop: index 1261 // 0 based index of this series in the plot series array. 1262 this.index; 1263 // prop: fill 1264 // true or false, whether to fill under lines or in bars. 1265 // May not be implemented in all renderers. 1266 this.fill = false; 1267 // prop: fillColor 1268 // CSS color spec to use for fill under line. Defaults to line color. 1269 this.fillColor; 1270 // prop: fillAlpha 1271 // Alpha transparency to apply to the fill under the line. 1272 // Use this to adjust alpha separate from fill color. 1273 this.fillAlpha; 1274 // prop: fillAndStroke 1275 // If true will stroke the line (with color this.color) as well as fill under it. 1276 // Applies only when fill is true. 1277 this.fillAndStroke = false; 1278 // prop: disableStack 1279 // true to not stack this series with other series in the plot. 1280 // To render properly, non-stacked series must come after any stacked series 1281 // in the plot's data series array. So, the plot's data series array would look like: 1282 // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...] 1283 // disableStack will put a gap in the stacking order of series, and subsequent 1284 // stacked series will not fill down through the non-stacked series and will 1285 // most likely not stack properly on top of the non-stacked series. 1286 this.disableStack = false; 1287 // _stack is set by the Plot if the plot is a stacked chart. 1288 // will stack lines or bars on top of one another to build a "mountain" style chart. 1289 // May not be implemented in all renderers. 1290 this._stack = false; 1291 // prop: neighborThreshold 1292 // how close or far (in pixels) the cursor must be from a point marker to detect the point. 1293 this.neighborThreshold = 4; 1294 // prop: fillToZero 1295 // true will force bar and filled series to fill toward zero on the fill Axis. 1296 this.fillToZero = false; 1297 // prop: fillToValue 1298 // fill a filled series to this value on the fill axis. 1299 // Works in conjunction with fillToZero, so that must be true. 1300 this.fillToValue = 0; 1301 // prop: fillAxis 1302 // Either 'x' or 'y'. Which axis to fill the line toward if fillToZero is true. 1303 // 'y' means fill up/down to 0 on the y axis for this series.
1304 this.fillAxis = 'y'; 1305 // prop: useNegativeColors 1306 // true to color negative values differently in filled and bar charts. 1307 this.useNegativeColors = true; 1308 this._stackData = []; 1309 // _plotData accounts for stacking. If plots not stacked, _plotData and data are same. If 1310 // stacked, _plotData is accumulation of stacking data. 1311 this._plotData = []; 1312 // _plotValues hold the individual x and y values that will be plotted for this series. 1313 this._plotValues = {x:[], y:[]}; 1314 // statistics about the intervals between data points. Used for auto scaling. 1315 this._intervals = {x:{}, y:{}}; 1316 // data from the previous series, for stacked charts. 1317 this._prevPlotData = []; 1318 this._prevGridData = []; 1319 this._stackAxis = 'y'; 1320 this._primaryAxis = '_xaxis'; 1321 // give each series a canvas to draw on. This should allow for redrawing speedups. 1322 this.canvas = new $.jqplot.GenericCanvas(); 1323 this.shadowCanvas = new $.jqplot.GenericCanvas(); 1324 this.plugins = {}; 1325 // sum of y values in this series. 1326 this._sumy = 0; 1327 this._sumx = 0; 1328 this._type = ''; 1329 this.step = false; 1330 } 1331 1332 Series.prototype = new $.jqplot.ElemContainer(); 1333 Series.prototype.constructor = Series; 1334 1335 Series.prototype.init = function(index, gridbw, plot) { 1336 // weed out any null values in the data. 1337 this.index = index; 1338 this.gridBorderWidth = gridbw; 1339 var d = this.data; 1340 var temp = [], i, l; 1341 for (i=0, l=d.length; i<l; i++) { 1342 if (! this.breakOnNull) { 1343 if (d[i] == null || d[i][0] == null || d[i][1] == null) { 1344 continue; 1345 } 1346 else { 1347 temp.push(d[i]); 1348 } 1349 } 1350 else {
1351 // TODO: figure out what to do with null values 1352 // probably involve keeping nulls in data array 1353 // and then updating renderers to break line 1354 // when it hits null value. 1355 // For now, just keep value. 1356 temp.push(d[i]); 1357 } 1358 } 1359 this.data = temp; 1360 1361 // parse the renderer options and apply default colors if not provided 1362 // Set color even if not shown, so series don't change colors when other 1363 // series on plot shown/hidden. 1364 if (!this.color) { 1365 this.color = plot.colorGenerator.get(this.index); 1366 } 1367 if (!this.negativeColor) { 1368 this.negativeColor = plot.negativeColorGenerator.get(this.index); 1369 } 1370 1371 1372 if (!this.fillColor) { 1373 this.fillColor = this.color; 1374 } 1375 if (this.fillAlpha) { 1376 var comp = $.jqplot.normalize2rgb(this.fillColor); 1377 var comp = $.jqplot.getColorComponents(comp); 1378 this.fillColor = 'rgba('+comp[0]+','+comp[1]+','+comp[2]+','+this.fillAlpha+')'; 1379 } 1380 if ($.isFunction(this.renderer)) { 1381 this.renderer = new this.renderer(); 1382 } 1383 this.renderer.init.call(this, this.rendererOptions, plot); 1384 this.markerRenderer = new this.markerRenderer(); 1385 if (!this.markerOptions.color) { 1386 this.markerOptions.color = this.color; 1387 } 1388 if (this.markerOptions.show == null) { 1389 this.markerOptions.show = this.showMarker; 1390 } 1391 this.showMarker = this.markerOptions.show; 1392 // the markerRenderer is called within its own scope, don't want to overwrite series options!! 1393 this.markerRenderer.init(this.markerOptions); 1394 }; 1395 1396 // data - optional data point array to draw using this series renderer 1397 // gridData - optional grid data point array to draw using this series renderer 1398 // stackData - array of cumulative data for stacked plots. 1399 Series.prototype.draw = function(sctx, opts, plot) { 1400 var options = (opts == undefined) ? {} : opts; 1401 sctx = (sctx == undefined) ? this.canvas._ctx : sctx; 1402 1403 var j, data, gridData; 1404 1405 // hooks get called even if series not shown 1406 // we don't clear canvas here, it would wipe out all other series as well. 1407 for (j=0; j<$.jqplot.preDrawSeriesHooks.length; j++) { 1408 $.jqplot.preDrawSeriesHooks[j].call(this, sctx, options); 1409 } 1410 if (this.show) { 1411 this.renderer.setGridData.call(this, plot); 1412 if (!options.preventJqPlotSeriesDrawTrigger) { 1413 $(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]); 1414 } 1415 data = []; 1416 if (options.data) { 1417 data = options.data; 1418 } 1419 else if (!this._stack) { 1420 data = this.data; 1421 } 1422 else { 1423 data = this._plotData; 1424 } 1425 gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot); 1426 1427 if (this._type === 'line' && this.renderer.smooth && this.renderer._smoothedData.length) { 1428 gridData = this.renderer._smoothedData; 1429 } 1430 1431 this.renderer.draw.call(this, sctx, gridData, options, plot); 1432 } 1433 1434 for (j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) { 1435 $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options, plot); 1436 } 1437 1438 sctx = opts = plot = j = data = gridData = null; 1439 }; 1440 1441 Series.prototype.drawShadow = function(sctx, opts, plot) { 1442 var options = (opts == undefined) ? {} : opts; 1443 sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx; 1444 1445 var j, data, gridData; 1446 1447 // hooks get called even if series not shown 1448 // we don't clear canvas here, it would wipe out all other series as well. 1449 for (j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) { 1450 $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options); 1451 } 1452 if (this.shadow) { 1453 this.renderer.setGridData.call(this, plot); 1454 1455 data = []; 1456 if (options.data) { 1457 data = options.data; 1458 } 1459 else if (!this._stack) { 1460 data = this.data; 1461 } 1462 else { 1463 data = this._plotData; 1464 } 1465 gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot); 1466 1467 this.renderer.drawShadow.call(this, sctx, gridData, options, plot); 1468 } 1469 1470 for (j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) { 1471 $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options); 1472 } 1473 1474 sctx = opts = plot = j = data = gridData = null; 1475 1476 }; 1477 1478 // toggles series display on plot, e.g. show/hide series 1479 Series.prototype.toggleDisplay = function(ev, callback) { 1480 var s, speed; 1481 if (ev.data.series) { 1482 s = ev.data.series; 1483 } 1484 else { 1485 s = this; 1486 } 1487 1488 if (ev.data.speed) { 1489 speed = ev.data.speed; 1490 } 1491 if (speed) { 1492 // this can be tricky because series may not have a canvas element if replotting. 1493 if (s.canvas._elem.is(':hidden') || !s.show) { 1494 s.show = true; 1495 1496 s.canvas._elem.removeClass('jqplot-series-hidden'); 1497 if (s.shadowCanvas._elem) { 1498 s.shadowCanvas._elem.fadeIn(speed); 1499 } 1500 s.canvas._elem.fadeIn(speed, callback); 1501 s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeIn(speed); 1502 } 1503 else { 1504 s.show = false; 1505 1506 s.canvas._elem.addClass('jqplot-series-hidden'); 1507 if (s.shadowCanvas._elem) { 1508 s.shadowCanvas._elem.fadeOut(speed); 1509 } 1510 s.canvas._elem.fadeOut(speed, callback); 1511 s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeOut(speed); 1512 } 1513 } 1514 else { 1515 // this can be tricky because series may not have a canvas element if replotting. 1516 if (s.canvas._elem.is(':hidden') || !s.show) { 1517 s.show = true; 1518 1519 s.canvas._elem.removeClass('jqplot-series-hidden'); 1520 if (s.shadowCanvas._elem) { 1521 s.shadowCanvas._elem.show(); 1522 } 1523 s.canvas._elem.show(0, callback); 1524 s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).show(); 1525 } 1526 else { 1527 s.show = false; 1528 1529 s.canvas._elem.addClass('jqplot-series-hidden'); 1530 if (s.shadowCanvas._elem) { 1531 s.shadowCanvas._elem.hide(); 1532 } 1533 s.canvas._elem.hide(0, callback); 1534 s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide(); 1535 } 1536 } 1537 }; 1538 1539 1540 1541 /** 1542 * Class: Grid 1543 * 1544 * Object representing the grid on which the plot is drawn. The grid in this 1545 * context is the area bounded by the axes, the area which will contain the series. 1546 * Note, the series are drawn on their own canvas. 1547 * The Grid object cannot be instantiated directly, but is created by the Plot object. 1548 * Grid properties can be set or overridden by the options passed in from the user. 1549 */ 1550 function Grid() { 1551 $.jqplot.ElemContainer.call(this); 1552 // Group: Properties 1553 1554 // prop: drawGridlines 1555 // whether to draw the gridlines on the plot.
1556 this.drawGridlines = true; 1557 // prop: gridLineColor 1558 // color of the grid lines. 1559 this.gridLineColor = '#cccccc'; 1560 // prop: gridLineWidth 1561 // width of the grid lines. 1562 this.gridLineWidth = 1.0; 1563 // prop: background 1564 // css spec for the background color. 1565 this.background = '#fffdf6'; 1566 // prop: borderColor 1567 // css spec for the color of the grid border. 1568 this.borderColor = '#999999'; 1569 // prop: borderWidth 1570 // width of the border in pixels. 1571 this.borderWidth = 2.0; 1572 // prop: drawBorder 1573 // True to draw border around grid. 1574 this.drawBorder = true; 1575 // prop: shadow 1576 // whether to show a shadow behind the grid. 1577 this.shadow = true; 1578 // prop: shadowAngle 1579 // shadow angle in degrees 1580 this.shadowAngle = 45; 1581 // prop: shadowOffset 1582 // Offset of each shadow stroke from the border in pixels 1583 this.shadowOffset = 1.5; 1584 // prop: shadowWidth 1585 // width of the stoke for the shadow 1586 this.shadowWidth = 3; 1587 // prop: shadowDepth 1588 // Number of times shadow is stroked, each stroke offset shadowOffset from the last. 1589 this.shadowDepth = 3; 1590 // prop: shadowColor 1591 // an optional css color spec for the shadow in 'rgba(n, n, n, n)' form 1592 this.shadowColor = null; 1593 // prop: shadowAlpha 1594 // Alpha channel transparency of shadow. 0 = transparent. 1595 this.shadowAlpha = '0.07'; 1596 this._left; 1597 this._top; 1598 this._right; 1599 this._bottom; 1600 this._width; 1601 this._height; 1602 this._axes = []; 1603 // prop: renderer 1604 // Instance of a renderer which will actually render the grid, 1605 // see <$.jqplot.CanvasGridRenderer>. 1606 this.renderer = $.jqplot.CanvasGridRenderer; 1607 // prop: rendererOptions 1608 // Options to pass on to the renderer, 1609 // see <$.jqplot.CanvasGridRenderer>. 1610 this.rendererOptions = {}; 1611 this._offsets = {top:null, bottom:null, left:null, right:null}; 1612 } 1613 1614 Grid.prototype = new $.jqplot.ElemContainer(); 1615 Grid.prototype.constructor = Grid; 1616 1617 Grid.prototype.init = function() { 1618 if ($.isFunction(this.renderer)) { 1619 this.renderer = new this.renderer(); 1620 } 1621 this.renderer.init.call(this, this.rendererOptions); 1622 }; 1623 1624 Grid.prototype.createElement = function(offsets,plot) { 1625 this._offsets = offsets; 1626 return this.renderer.createElement.call(this, plot); 1627 }; 1628 1629 Grid.prototype.draw = function() { 1630 this.renderer.draw.call(this); 1631 }; 1632 1633 $.jqplot.GenericCanvas = function() { 1634 $.jqplot.ElemContainer.call(this); 1635 this._ctx; 1636 }; 1637 1638 $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer(); 1639 $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas; 1640 1641 $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions, plot) { 1642 this._offsets = offsets; 1643 var klass = 'jqplot'; 1644 if (clss != undefined) { 1645 klass = clss; 1646 } 1647 var elem; 1648 1649 elem = plot.canvasManager.getCanvas(); 1650 1651 // if new plotDimensions supplied, use them. 1652 if (plotDimensions != null) { 1653 this._plotDimensions = plotDimensions; 1654 } 1655 1656 elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right; 1657 elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom; 1658 this._elem = $(elem); 1659 this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top }); 1660 1661 this._elem.addClass(klass); 1662 1663 elem = plot.canvasManager.initCanvas(elem); 1664 1665 elem = null; 1666 return this._elem; 1667 }; 1668 1669 $.jqplot.GenericCanvas.prototype.setContext = function() { 1670 this._ctx = this._elem.get(0).getContext("2d"); 1671 return this._ctx; 1672 }; 1673 1674 // Memory Leaks patch 1675 $.jqplot.GenericCanvas.prototype.resetCanvas = function() { 1676 if (this._elem) { 1677 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 1678 window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); 1679 } 1680 1681 //this._elem.remove(); 1682 this._elem.emptyForce(); 1683 } 1684 1685 this._ctx = null; 1686 }; 1687 1688 $.jqplot.HooksManager = function () { 1689 this.hooks =[]; 1690 this.args = []; 1691 }; 1692 1693 $.jqplot.HooksManager.prototype.addOnce = function(fn, args) { 1694 args = args || []; 1695 var havehook = false; 1696 for (var i=0, l=this.hooks.length; i<l; i++) { 1697 if (this.hooks[i] == fn) { 1698 havehook = true; 1699 } 1700 } 1701 if (!havehook) { 1702 this.hooks.push(fn); 1703 this.args.push(args); 1704 } 1705 }; 1706 1707 $.jqplot.HooksManager.prototype.add = function(fn, args) { 1708 args = args || []; 1709 this.hooks.push(fn); 1710 this.args.push(args); 1711 }; 1712 1713 $.jqplot.EventListenerManager = function () { 1714 this.hooks =[]; 1715 }; 1716 1717 $.jqplot.EventListenerManager.prototype.addOnce = function(ev, fn) { 1718 var havehook = false, h, i;
1719 for (var i=0, l=this.hooks.length; i<l; i++) { 1720 h = this.hooks[i]; 1721 if (h[0] == ev && h[1] == fn) { 1722 havehook = true; 1723 } 1724 } 1725 if (!havehook) { 1726 this.hooks.push([ev, fn]); 1727 } 1728 }; 1729 1730 $.jqplot.EventListenerManager.prototype.add = function(ev, fn) { 1731 this.hooks.push([ev, fn]); 1732 }; 1733 1734 1735 var _axisNames = ['yMidAxis', 'xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis']; 1736 1737 /** 1738 * Class: jqPlot 1739 * Plot object returned by call to $.jqplot. Handles parsing user options, 1740 * creating sub objects (Axes, legend, title, series) and rendering the plot. 1741 */ 1742 function jqPlot() { 1743 // Group: Properties 1744 // These properties are specified at the top of the options object 1745 // like so: 1746 // > { 1747 // > axesDefaults:{min:0}, 1748 // > series:[{color:'#6633dd'}], 1749 // > title: 'A Plot' 1750 // > } 1751 // 1752 1753 // prop: animate 1754 // True to animate the series on initial plot draw (renderer dependent). 1755 // Actual animation functionality must be supported in the renderer. 1756 this.animate = false; 1757 // prop: animateReplot 1758 // True to animate series after a call to the replot() method. 1759 // Use with caution! Replots can happen very frequently under 1760 // certain circumstances (e.g. resizing, dragging points) and 1761 // animation in these situations can cause problems. 1762 this.animateReplot = false; 1763 // prop: axes 1764 // up to 4 axes are supported, each with its own options, 1765 // See <Axis> for axis specific options. 1766 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')}; 1767 this.baseCanvas = new $.jqplot.GenericCanvas(); 1768 // true to intercept right click events and fire a 'jqplotRightClick' event. 1769 // this will also block the context menu. 1770 this.captureRightClick = false; 1771 // prop: data 1772 // user's data. Data should *NOT* be specified in the options object, 1773 // but be passed in as the second argument to the $.jqplot() function. 1774 // The data property is described here soley for reference. 1775 // The data should be in the form of an array of 2D or 1D arrays like 1776 // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ]. 1777 this.data = []; 1778 // prop: dataRenderer 1779 // A callable which can be used to preprocess data passed into the plot. 1780 // Will be called with 3 arguments: the plot data, a reference to the plot, 1781 // and the value of dataRendererOptions. 1782 this.dataRenderer; 1783 // prop: dataRendererOptions 1784 // Options that will be passed to the dataRenderer. 1785 // Can be of any type. 1786 this.dataRendererOptions; 1787 this.defaults = { 1788 // prop: axesDefaults 1789 // default options that will be applied to all axes. 1790 // see <Axis> for axes options. 1791 axesDefaults: {}, 1792 axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}, yMidAxis:{}}, 1793 // prop: seriesDefaults 1794 // default options that will be applied to all series. 1795 // see <Series> for series options. 1796 seriesDefaults: {}, 1797 series:[] 1798 }; 1799 // prop: defaultAxisStart 1800 // 1-D data series are internally converted into 2-D [x,y] data point arrays 1801 // by jqPlot. This is the default starting value for the missing x or y value. 1802 // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...]) 1803 // starting at this value. 1804 this.defaultAxisStart = 1; 1805 // this.doCustomEventBinding = true; 1806 // prop: drawIfHidden 1807 // True to execute the draw method even if the plot target is hidden. 1808 // Generally, this should be false. Most plot elements will not be sized/ 1809 // positioned correclty if renderered into a hidden container. To render into 1810 // a hidden container, call the replot method when the container is shown. 1811 this.drawIfHidden = false; 1812 this.eventCanvas = new $.jqplot.GenericCanvas(); 1813 // prop: fillBetween 1814 // Fill between 2 line series in a plot. 1815 // Options object: 1816 // { 1817 // series1: first index (0 based) of series in fill 1818 // series2: second index (0 based) of series in fill 1819 // color: color of fill [default fillColor of series1] 1820 // baseSeries: fill will be drawn below this series (0 based index) 1821 // fill: false to turn off fill [default true]. 1822 // } 1823 this.fillBetween = { 1824 series1: null, 1825 series2: null, 1826 color: null, 1827 baseSeries: 0, 1828 fill: true 1829 }; 1830 // prop; fontFamily 1831 // css spec for the font-family attribute. Default for the entire plot. 1832 this.fontFamily; 1833 // prop: fontSize 1834 // css spec for the font-size attribute. Default for the entire plot. 1835 this.fontSize; 1836 // prop: grid 1837 // See <Grid> for grid specific options. 1838 this.grid = new Grid(); 1839 // prop: legend 1840 // see <$.jqplot.TableLegendRenderer> 1841 this.legend = new Legend(); 1842 // prop: noDataIndicator 1843 // Options to set up a mock plot with a data loading indicator if no data is specified. 1844 this.noDataIndicator = { 1845 show: false, 1846 indicator: 'Loading Data...', 1847 axes: { 1848 xaxis: { 1849 min: 0, 1850 max: 10, 1851 tickInterval: 2, 1852 show: true 1853 }, 1854 yaxis: { 1855 min: 0, 1856 max: 12, 1857 tickInterval: 3, 1858 show: true 1859 } 1860 } 1861 }; 1862 // prop: negativeSeriesColors 1863 // colors to use for portions of the line below zero. 1864 this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors; 1865 // container to hold all of the merged options. Convienence for plugins. 1866 this.options = {}; 1867 this.previousSeriesStack = []; 1868 // Namespace to hold plugins. Generally non-renderer plugins add themselves to here. 1869 this.plugins = {}; 1870 // prop: series 1871 // Array of series object options. 1872 // see <Series> for series specific options. 1873 this.series = []; 1874 // array of series indices. Keep track of order 1875 // which series canvases are displayed, lowest 1876 // to highest, back to front. 1877 this.seriesStack = []; 1878 // prop: seriesColors 1879 // Ann array of CSS color specifications that will be applied, in order, 1880 // to the series in the plot. Colors will wrap around so, if their 1881 // are more series than colors, colors will be reused starting at the 1882 // beginning. For pie charts, this specifies the colors of the slices. 1883 this.seriesColors = $.jqplot.config.defaultColors; 1884 // prop: sortData 1885 // false to not sort the data passed in by the user. 1886 // Many bar, stacked and other graphs as well as many plugins depend on 1887 // having sorted data. 1888 this.sortData = true; 1889 // prop: stackSeries 1890 // true or false, creates a stack or "mountain" plot. 1891 // Not all series renderers may implement this option. 1892 this.stackSeries = false; 1893 // a shortcut for axis syncTicks options. Not implemented yet. 1894 this.syncXTicks = true; 1895 // a shortcut for axis syncTicks options. Not implemented yet. 1896 this.syncYTicks = true; 1897 // the jquery object for the dom target. 1898 this.target = null; 1899 // The id of the dom element to render the plot into 1900 this.targetId = null; 1901 // prop textColor 1902 // css spec for the css color attribute. Default for the entire plot. 1903 this.textColor; 1904 // prop: title 1905 // Title object. See <Title> for specific options. As a shortcut, you 1906 // can specify the title option as just a string like: title: 'My Plot' 1907 // and this will create a new title object with the specified text. 1908 this.title = new Title(); 1909 // Count how many times the draw method has been called while the plot is visible. 1910 // Mostly used to test if plot has never been dran (=0), has been successfully drawn 1911 // into a visible container once (=1) or draw more than once into a visible container. 1912 // Can use this in tests to see if plot has been visibly drawn at least one time. 1913 // After plot has been visibly drawn once, it generally doesn't need redrawing if its 1914 // container is hidden and shown. 1915 this._drawCount = 0; 1916 // sum of y values for all series in plot. 1917 // used in mekko chart. 1918 this._sumy = 0; 1919 this._sumx = 0; 1920 // array to hold the cumulative stacked series data. 1921 // used to ajust the individual series data, which won't have access to other 1922 // series data. 1923 this._stackData = []; 1924 // array that holds the data to be plotted. This will be the series data 1925 // merged with the the appropriate data from _stackData according to the stackAxis. 1926 this._plotData = []; 1927 this._width = null; 1928 this._height = null; 1929 this._plotDimensions = {height:null, width:null}; 1930 this._gridPadding = {top:null, right:null, bottom:null, left:null}; 1931 this._defaultGridPadding = {top:10, right:10, bottom:23, left:10}; 1932 1933 this._addDomReference = $.jqplot.config.addDomReference; 1934 1935 this.preInitHooks = new $.jqplot.HooksManager(); 1936 this.postInitHooks = new $.jqplot.HooksManager(); 1937 this.preParseOptionsHooks = new $.jqplot.HooksManager(); 1938 this.postParseOptionsHooks = new $.jqplot.HooksManager(); 1939 this.preDrawHooks = new $.jqplot.HooksManager(); 1940 this.postDrawHooks = new $.jqplot.HooksManager(); 1941 this.preDrawSeriesHooks = new $.jqplot.HooksManager(); 1942 this.postDrawSeriesHooks = new $.jqplot.HooksManager(); 1943 this.preDrawLegendHooks = new $.jqplot.HooksManager(); 1944 this.addLegendRowHooks = new $.jqplot.HooksManager(); 1945 this.preSeriesInitHooks = new $.jqplot.HooksManager(); 1946 this.postSeriesInitHooks = new $.jqplot.HooksManager(); 1947 this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager(); 1948 this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager();
1949 this.eventListenerHooks = new $.jqplot.EventListenerManager(); 1950 this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager(); 1951 this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager(); 1952 1953 this.colorGenerator = new $.jqplot.ColorGenerator(); 1954 this.negativeColorGenerator = new $.jqplot.ColorGenerator(); 1955 1956 this.canvasManager = new $.jqplot.CanvasManager(); 1957 1958 this.themeEngine = new $.jqplot.ThemeEngine(); 1959 1960 var seriesColorsIndex = 0; 1961 1962 // Group: methods 1963 // 1964 // method: init 1965 // sets the plot target, checks data and applies user 1966 // options to plot. 1967 this.init = function(target, data, options) { 1968 options = options || {}; 1969 for (var i=0; i<$.jqplot.preInitHooks.length; i++) { 1970 $.jqplot.preInitHooks[i].call(this, target, data, options); 1971 } 1972 1973 for (var i=0; i<this.preInitHooks.hooks.length; i++) { 1974 this.preInitHooks.hooks[i].call(this, target, data, options); 1975 } 1976 1977 this.targetId = '#'+target; 1978 this.target = $('#'+target); 1979 1980 ////// 1981 // Add a reference to plot 1982 ////// 1983 if (this._addDomReference) { 1984 this.target.data('jqplot', this); 1985 } 1986 // remove any error class that may be stuck on target. 1987 this.target.removeClass('jqplot-error'); 1988 if (!this.target.get(0)) { 1989 throw new Error("No plot target specified"); 1990 } 1991 1992 // make sure the target is positioned by some means and set css 1993 if (this.target.css('position') == 'static') { 1994 this.target.css('position', 'relative'); 1995 } 1996 if (!this.target.hasClass('jqplot-target')) { 1997 this.target.addClass('jqplot-target'); 1998 } 1999 2000 // if no height or width specified, use a default. 2001 if (!this.target.height()) { 2002 var h; 2003 if (options && options.height) { 2004 h = parseInt(options.height, 10); 2005 } 2006 else if (this.target.attr('data-height')) { 2007 h = parseInt(this.target.attr('data-height'), 10); 2008 } 2009 else { 2010 h = parseInt($.jqplot.config.defaultHeight, 10); 2011 } 2012 this._height = h; 2013 this.target.css('height', h+'px'); 2014 } 2015 else { 2016 this._height = h = this.target.height(); 2017 } 2018 if (!this.target.width()) { 2019 var w; 2020 if (options && options.width) { 2021 w = parseInt(options.width, 10); 2022 } 2023 else if (this.target.attr('data-width')) { 2024 w = parseInt(this.target.attr('data-width'), 10); 2025 } 2026 else { 2027 w = parseInt($.jqplot.config.defaultWidth, 10); 2028 } 2029 this._width = w; 2030 this.target.css('width', w+'px'); 2031 } 2032 else { 2033 this._width = w = this.target.width(); 2034 } 2035 2036 for (var i=0, l=_axisNames.length; i<l; i++) { 2037 this.axes[_axisNames[i]] = new Axis(_axisNames[i]); 2038 } 2039 2040 this._plotDimensions.height = this._height; 2041 this._plotDimensions.width = this._width; 2042 this.grid._plotDimensions = this._plotDimensions; 2043 this.title._plotDimensions = this._plotDimensions; 2044 this.baseCanvas._plotDimensions = this._plotDimensions; 2045 this.eventCanvas._plotDimensions = this._plotDimensions; 2046 this.legend._plotDimensions = this._plotDimensions; 2047 if (this._height <=0 || this._width <=0 || !this._height || !this._width) { 2048 throw new Error("Canvas dimension not set"); 2049 } 2050 2051 if (options.dataRenderer && $.isFunction(options.dataRenderer)) { 2052 if (options.dataRendererOptions) { 2053 this.dataRendererOptions = options.dataRendererOptions; 2054 } 2055 this.dataRenderer = options.dataRenderer; 2056 data = this.dataRenderer(data, this, this.dataRendererOptions); 2057 } 2058 2059 if (options.noDataIndicator && $.isPlainObject(options.noDataIndicator)) { 2060 $.extend(true, this.noDataIndicator, options.noDataIndicator); 2061 } 2062 2063 if (data == null || $.isArray(data) == false || data.length == 0 || $.isArray(data[0]) == false || data[0].length == 0) { 2064 2065 if (this.noDataIndicator.show == false) { 2066 throw new Error("No data specified"); 2067 } 2068 2069 else { 2070 // have to be descructive here in order for plot to not try and render series. 2071 // This means that $.jqplot() will have to be called again when there is data. 2072 //delete options.series; 2073 2074 for (var ax in this.noDataIndicator.axes) { 2075 for (var prop in this.noDataIndicator.axes[ax]) { 2076 this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop]; 2077 } 2078 } 2079 2080 this.postDrawHooks.add(function() { 2081 var eh = this.eventCanvas.getHeight(); 2082 var ew = this.eventCanvas.getWidth(); 2083 var temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>'); 2084 this.target.append(temp); 2085 temp.height(eh); 2086 temp.width(ew); 2087 temp.css('top', this.eventCanvas._offsets.top); 2088 temp.css('left', this.eventCanvas._offsets.left); 2089 2090 var temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>'); 2091 temp.append(temp2); 2092 temp2.html(this.noDataIndicator.indicator); 2093 var th = temp2.height(); 2094 var tw = temp2.width(); 2095 temp2.height(th); 2096 temp2.width(tw); 2097 temp2.css('top', (eh - th)/2 + 'px'); 2098 }); 2099 2100 } 2101 } 2102 2103 // make a copy of the data 2104 this.data = $.extend(true, [], data); 2105 2106 this.parseOptions(options); 2107 2108 if (this.textColor) { 2109 this.target.css('color', this.textColor); 2110 } 2111 if (this.fontFamily) { 2112 this.target.css('font-family', this.fontFamily); 2113 } 2114 if (this.fontSize) { 2115 this.target.css('font-size', this.fontSize); 2116 } 2117
2118 this.title.init(); 2119 this.legend.init(); 2120 this._sumy = 0; 2121 this._sumx = 0; 2122 this.computePlotData(); 2123 for (var i=0; i<this.series.length; i++) { 2124 // set default stacking order for series canvases 2125 this.seriesStack.push(i); 2126 this.previousSeriesStack.push(i); 2127 this.series[i].shadowCanvas._plotDimensions = this._plotDimensions; 2128 this.series[i].canvas._plotDimensions = this._plotDimensions; 2129 for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) { 2130 $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2131 } 2132 for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) { 2133 this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2134 } 2135 // this.populatePlotData(this.series[i], i); 2136 this.series[i]._plotDimensions = this._plotDimensions; 2137 this.series[i].init(i, this.grid.borderWidth, this); 2138 for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) { 2139 $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2140 } 2141 for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) { 2142 this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2143 } 2144 this._sumy += this.series[i]._sumy; 2145 this._sumx += this.series[i]._sumx; 2146 } 2147 2148 var name, 2149 axis; 2150 for (var i=0, l=_axisNames.length; i<l; i++) { 2151 name = _axisNames[i]; 2152 axis = this.axes[name]; 2153 axis._plotDimensions = this._plotDimensions; 2154 axis.init(); 2155 if (this.axes[name].borderColor == null) { 2156 if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) { 2157 axis.borderColor = axis._series[0].color; 2158 } 2159 else { 2160 axis.borderColor = this.grid.borderColor; 2161 } 2162 } 2163 } 2164 2165 if (this.sortData) { 2166 sortData(this.series); 2167 } 2168 this.grid.init(); 2169 this.grid._axes = this.axes; 2170 2171 this.legend._series = this.series; 2172 2173 for (var i=0; i<$.jqplot.postInitHooks.length; i++) { 2174 $.jqplot.postInitHooks[i].call(this, target, this.data, options); 2175 } 2176 2177 for (var i=0; i<this.postInitHooks.hooks.length; i++) { 2178 this.postInitHooks.hooks[i].call(this, target, this.data, options); 2179 } 2180 }; 2181 2182 // method: resetAxesScale 2183 // Reset the specified axes min, max, numberTicks and tickInterval properties to null 2184 // or reset these properties on all axes if no list of axes is provided. 2185 // 2186 // Parameters: 2187 // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset. 2188 this.resetAxesScale = function(axes, options) { 2189 var opts = options || {}; 2190 var ax = axes || this.axes; 2191 if (ax === true) { 2192 ax = this.axes; 2193 } 2194 if ($.isArray(ax)) { 2195 for (var i = 0; i < ax.length; i++) { 2196 this.axes[ax[i]].resetScale(opts[ax[i]]); 2197 } 2198 } 2199 else if (typeof(ax) === 'object') { 2200 for (var name in ax) { 2201 this.axes[name].resetScale(opts[name]); 2202 } 2203 } 2204 }; 2205 // method: reInitialize 2206 // reinitialize plot for replotting. 2207 // not called directly. 2208 this.reInitialize = function (data, opts) { 2209 // Plot should be visible and have a height and width. 2210 // If plot doesn't have height and width for some 2211 // reason, set it by other means. Plot must not have 2212 // a display:none attribute, however. 2213 2214 var options = $.extend(true, {}, this.options, opts); 2215 2216 var target = this.targetId.substr(1); 2217 var tdata = (data == null) ? this.data : data; 2218 2219 for (var i=0; i<$.jqplot.preInitHooks.length; i++) { 2220 $.jqplot.preInitHooks[i].call(this, target, tdata, options); 2221 } 2222 2223 for (var i=0; i<this.preInitHooks.hooks.length; i++) { 2224 this.preInitHooks.hooks[i].call(this, target, tdata, options); 2225 } 2226 2227 this._height = this.target.height(); 2228 this._width = this.target.width(); 2229 2230 if (this._height <=0 || this._width <=0 || !this._height || !this._width) { 2231 throw new Error("Target dimension not set"); 2232 } 2233 2234 this._plotDimensions.height = this._height; 2235 this._plotDimensions.width = this._width; 2236 this.grid._plotDimensions = this._plotDimensions; 2237 this.title._plotDimensions = this._plotDimensions; 2238 this.baseCanvas._plotDimensions = this._plotDimensions; 2239 this.eventCanvas._plotDimensions = this._plotDimensions; 2240 this.legend._plotDimensions = this._plotDimensions; 2241 2242 var name, 2243 t, 2244 j, 2245 axis; 2246 2247 for (var i=0, l=_axisNames.length; i<l; i++) { 2248 name = _axisNames[i]; 2249 axis = this.axes[name]; 2250 2251 // Memory Leaks patch : clear ticks elements 2252 t = axis._ticks; 2253 for (var j = 0, tlen = t.length; j < tlen; j++) { 2254 var el = t[j]._elem; 2255 if (el) { 2256 // if canvas renderer 2257 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 2258 window.G_vmlCanvasManager.uninitElement(el.get(0)); 2259 } 2260 el.emptyForce(); 2261 el = null; 2262 t._elem = null; 2263 } 2264 } 2265 t = null; 2266 2267 delete axis.ticks; 2268 delete axis._ticks; 2269 this.axes[name] = new Axis(name); 2270 this.axes[name]._plotWidth = this._width; 2271 this.axes[name]._plotHeight = this._height; 2272 } 2273 2274 if (data) { 2275 if (options.dataRenderer && $.isFunction(options.dataRenderer)) { 2276 if (options.dataRendererOptions) { 2277 this.dataRendererOptions = options.dataRendererOptions; 2278 } 2279 this.dataRenderer = options.dataRenderer; 2280 data = this.dataRenderer(data, this, this.dataRendererOptions); 2281 } 2282 2283 // make a copy of the data 2284 this.data = $.extend(true, [], data); 2285 } 2286 2287 if (opts) { 2288 this.parseOptions(options); 2289 } 2290 2291 this.title._plotWidth = this._width; 2292 2293 if (this.textColor) { 2294 this.target.css('color', this.textColor); 2295 } 2296 if (this.fontFamily) { 2297 this.target.css('font-family', this.fontFamily); 2298 } 2299 if (this.fontSize) { 2300 this.target.css('font-size', this.fontSize); 2301 } 2302
2303 this.title.init(); 2304 this.legend.init(); 2305 this._sumy = 0; 2306 this._sumx = 0; 2307 2308 this.seriesStack = []; 2309 this.previousSeriesStack = []; 2310 2311 this.computePlotData(); 2312 for (var i=0, l=this.series.length; i<l; i++) { 2313 // set default stacking order for series canvases 2314 this.seriesStack.push(i); 2315 this.previousSeriesStack.push(i); 2316 this.series[i].shadowCanvas._plotDimensions = this._plotDimensions; 2317 this.series[i].canvas._plotDimensions = this._plotDimensions; 2318 for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) { 2319 $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2320 } 2321 for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) { 2322 this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2323 } 2324 // this.populatePlotData(this.series[i], i); 2325 this.series[i]._plotDimensions = this._plotDimensions; 2326 this.series[i].init(i, this.grid.borderWidth, this); 2327 for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) { 2328 $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2329 } 2330 for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) { 2331 this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); 2332 } 2333 this._sumy += this.series[i]._sumy; 2334 this._sumx += this.series[i]._sumx; 2335 } 2336 2337 for (var i=0, l=_axisNames.length; i<l; i++) { 2338 name = _axisNames[i]; 2339 axis = this.axes[name]; 2340 2341 axis._plotDimensions = this._plotDimensions; 2342 axis.init(); 2343 if (axis.borderColor == null) { 2344 if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) { 2345 axis.borderColor = axis._series[0].color; 2346 } 2347 else { 2348 axis.borderColor = this.grid.borderColor; 2349 } 2350 } 2351 } 2352 2353 if (this.sortData) { 2354 sortData(this.series); 2355 } 2356 this.grid.init(); 2357 this.grid._axes = this.axes; 2358 2359 this.legend._series = this.series; 2360 2361 for (var i=0, l=$.jqplot.postInitHooks.length; i<l; i++) { 2362 $.jqplot.postInitHooks[i].call(this, target, this.data, options); 2363 } 2364 2365 for (var i=0, l=this.postInitHooks.hooks.length; i<l; i++) { 2366 this.postInitHooks.hooks[i].call(this, target, this.data, options); 2367 } 2368 }; 2369 2370 2371 2372 // method: quickInit 2373 // 2374 // Quick reinitialization plot for replotting. 2375 // Does not parse options ore recreate axes and series. 2376 // not called directly. 2377 this.quickInit = function () { 2378 // Plot should be visible and have a height and width. 2379 // If plot doesn't have height and width for some 2380 // reason, set it by other means. Plot must not have 2381 // a display:none attribute, however. 2382 2383 this._height = this.target.height(); 2384 this._width = this.target.width(); 2385 2386 if (this._height <=0 || this._width <=0 || !this._height || !this._width) { 2387 throw new Error("Target dimension not set"); 2388 } 2389 2390 this._plotDimensions.height = this._height; 2391 this._plotDimensions.width = this._width; 2392 this.grid._plotDimensions = this._plotDimensions; 2393 this.title._plotDimensions = this._plotDimensions; 2394 this.baseCanvas._plotDimensions = this._plotDimensions; 2395 this.eventCanvas._plotDimensions = this._plotDimensions; 2396 this.legend._plotDimensions = this._plotDimensions; 2397 2398 for (var n in this.axes) { 2399 this.axes[n]._plotWidth = this._width; 2400 this.axes[n]._plotHeight = this._height; 2401 } 2402 2403 this.title._plotWidth = this._width; 2404 2405 if (this.textColor) { 2406 this.target.css('color', this.textColor); 2407 } 2408 if (this.fontFamily) { 2409 this.target.css('font-family', this.fontFamily); 2410 } 2411 if (this.fontSize) { 2412 this.target.css('font-size', this.fontSize); 2413 } 2414 2415 this._sumy = 0; 2416 this._sumx = 0; 2417 this.computePlotData(); 2418 for (var i=0; i<this.series.length; i++) { 2419 // this.populatePlotData(this.series[i], i); 2420 if (this.series[i]._type === 'line' && this.series[i].renderer.bands.show) { 2421 this.series[i].renderer.initBands.call(this.series[i], this.series[i].renderer.options, this); 2422 } 2423 this.series[i]._plotDimensions = this._plotDimensions; 2424 this.series[i].canvas._plotDimensions = this._plotDimensions; 2425 //this.series[i].init(i, this.grid.borderWidth);
2426 this._sumy += this.series[i]._sumy; 2427 this._sumx += this.series[i]._sumx; 2428 } 2429 2430 var name; 2431 2432 for (var j=0; j<12; j++) { 2433 name = _axisNames[j]; 2434 // Memory Leaks patch : clear ticks elements 2435 var t = this.axes[name]._ticks; 2436 for (var i = 0; i < t.length; i++) { 2437 var el = t[i]._elem; 2438 if (el) { 2439 // if canvas renderer 2440 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 2441 window.G_vmlCanvasManager.uninitElement(el.get(0)); 2442 } 2443 el.emptyForce(); 2444 el = null; 2445 t._elem = null; 2446 } 2447 } 2448 t = null; 2449 2450 this.axes[name]._plotDimensions = this._plotDimensions; 2451 this.axes[name]._ticks = []; 2452 // this.axes[name].renderer.init.call(this.axes[name], {}); 2453 } 2454 2455 if (this.sortData) { 2456 sortData(this.series); 2457 } 2458 2459 this.grid._axes = this.axes; 2460 2461 this.legend._series = this.series; 2462 }; 2463 2464 // sort the series data in increasing order. 2465 function sortData(series) { 2466 var d, sd, pd, ppd, ret; 2467 for (var i=0; i<series.length; i++) { 2468 var check; 2469 var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData]; 2470 for (var n=0; n<4; n++) { 2471 check = true; 2472 d = bat[n]; 2473 if (series[i]._stackAxis == 'x') { 2474 for (var j = 0; j < d.length; j++) { 2475 if (typeof(d[j][1]) != "number") { 2476 check = false; 2477 break; 2478 } 2479 } 2480 if (check) { 2481 d.sort(function(a,b) { return a[1] - b[1]; }); 2482 } 2483 } 2484 else { 2485 for (var j = 0; j < d.length; j++) { 2486 if (typeof(d[j][0]) != "number") { 2487 check = false; 2488 break; 2489 } 2490 } 2491 if (check) { 2492 d.sort(function(a,b) { return a[0] - b[0]; }); 2493 } 2494 } 2495 } 2496 2497 } 2498 } 2499 2500 this.computePlotData = function() { 2501 this._plotData = []; 2502 this._stackData = []; 2503 var series, 2504 index, 2505 l; 2506 2507 2508 for (index=0, l=this.series.length; index<l; index++) { 2509 series = this.series[index]; 2510 this._plotData.push([]); 2511 this._stackData.push([]); 2512 var cd = series.data; 2513 this._plotData[index] = $.extend(true, [], cd); 2514 this._stackData[index] = $.extend(true, [], cd); 2515 series._plotData = this._plotData[index]; 2516 series._stackData = this._stackData[index]; 2517 var plotValues = {x:[], y:[]}; 2518 2519 if (this.stackSeries && !series.disableStack) { 2520 series._stack = true;
2521 /////////////////////////// 2522 // have to check for nulls 2523 /////////////////////////// 2524 var sidx = (series._stackAxis === 'x') ? 0 : 1; 2525 2526 for (var k=0, cdl=cd.length; k<cdl; k++) { 2527 var temp = cd[k][sidx]; 2528 if (temp == null) { 2529 temp = 0; 2530 } 2531 this._plotData[index][k][sidx] = temp; 2532 this._stackData[index][k][sidx] = temp; 2533 2534 if (index > 0) { 2535 for (var j=index; j--;) { 2536 var prevval = this._plotData[j][k][sidx]; 2537 // only need to sum up the stack axis column of data 2538 // and only sum if it is of same sign. 2539 // if previous series isn't same sign, keep looking 2540 // at earlier series untill we find one of same sign. 2541 if (temp * prevval >= 0) { 2542 this._plotData[index][k][sidx] += prevval; 2543 this._stackData[index][k][sidx] += prevval; 2544 break; 2545 } 2546 } 2547 } 2548 } 2549 2550 } 2551 else { 2552 for (var i=0; i<series.data.length; i++) { 2553 plotValues.x.push(series.data[i][0]); 2554 plotValues.y.push(series.data[i][1]); 2555 } 2556 this._stackData.push(series.data); 2557 this.series[index]._stackData = series.data; 2558 this._plotData.push(series.data); 2559 series._plotData = series.data; 2560 series._plotValues = plotValues; 2561 } 2562 if (index>0) { 2563 series._prevPlotData = this.series[index-1]._plotData; 2564 } 2565 series._sumy = 0; 2566 series._sumx = 0; 2567 for (i=series.data.length-1; i>-1; i--) { 2568 series._sumy += series.data[i][1]; 2569 series._sumx += series.data[i][0]; 2570 } 2571 } 2572 2573 }; 2574 2575 // populate the _stackData and _plotData arrays for the plot and the series. 2576 this.populatePlotData = function(series, index) { 2577 // if a stacked chart, compute the stacked data 2578 this._plotData = []; 2579 this._stackData = []; 2580 series._stackData = []; 2581 series._plotData = []; 2582 var plotValues = {x:[], y:[]}; 2583 if (this.stackSeries && !series.disableStack) { 2584 series._stack = true; 2585 var sidx = (series._stackAxis === 'x') ? 0 : 1; 2586 // var idx = sidx ? 0 : 1; 2587 // push the current data into stackData 2588 //this._stackData.push(this.series[i].data); 2589 var temp = $.extend(true, [], series.data); 2590 // create the data that will be plotted for this series 2591 var plotdata = $.extend(true, [], series.data); 2592 var tempx, tempy, dval, stackval, comparator; 2593 // for first series, nothing to add to stackData. 2594 for (var j=0; j<index; j++) { 2595 var cd = this.series[j].data; 2596 for (var k=0; k<cd.length; k++) { 2597 dval = cd[k]; 2598 tempx = (dval[0] != null) ? dval[0] : 0; 2599 tempy = (dval[1] != null) ? dval[1] : 0; 2600 temp[k][0] += tempx; 2601 temp[k][1] += tempy; 2602 stackval = (sidx) ? tempy : tempx; 2603 // only need to sum up the stack axis column of data 2604 // and only sum if it is of same sign. 2605 if (series.data[k][sidx] * stackval >= 0) { 2606 plotdata[k][sidx] += stackval; 2607 } 2608 } 2609 } 2610 for (var i=0; i<plotdata.length; i++) { 2611 plotValues.x.push(plotdata[i][0]); 2612 plotValues.y.push(plotdata[i][1]); 2613 } 2614 this._plotData.push(plotdata); 2615 this._stackData.push(temp); 2616 series._stackData = temp; 2617 series._plotData = plotdata; 2618 series._plotValues = plotValues; 2619 } 2620 else { 2621 for (var i=0; i<series.data.length; i++) { 2622 plotValues.x.push(series.data[i][0]); 2623 plotValues.y.push(series.data[i][1]); 2624 } 2625 this._stackData.push(series.data); 2626 this.series[index]._stackData = series.data; 2627 this._plotData.push(series.data); 2628 series._plotData = series.data; 2629 series._plotValues = plotValues; 2630 } 2631 if (index>0) { 2632 series._prevPlotData = this.series[index-1]._plotData; 2633 } 2634 series._sumy = 0; 2635 series._sumx = 0; 2636 for (i=series.data.length-1; i>-1; i--) { 2637 series._sumy += series.data[i][1]; 2638 series._sumx += series.data[i][0]; 2639 } 2640 }; 2641 2642 // function to safely return colors from the color array and wrap around at the end. 2643 this.getNextSeriesColor = (function(t) { 2644 var idx = 0; 2645 var sc = t.seriesColors; 2646 2647 return function () { 2648 if (idx < sc.length) { 2649 return sc[idx++]; 2650 } 2651 else { 2652 idx = 0; 2653 return sc[idx++]; 2654 } 2655 }; 2656 })(this); 2657 2658 this.parseOptions = function(options){ 2659 for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) { 2660 this.preParseOptionsHooks.hooks[i].call(this, options); 2661 } 2662 for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) { 2663 $.jqplot.preParseOptionsHooks[i].call(this, options); 2664 } 2665 this.options = $.extend(true, {}, this.defaults, options); 2666 var opts = this.options; 2667 this.animate = opts.animate; 2668 this.animateReplot = opts.animateReplot; 2669 this.stackSeries = opts.stackSeries; 2670 if ($.isPlainObject(opts.fillBetween)) { 2671 2672 var temp = ['series1', 'series2', 'color', 'baseSeries', 'fill'], 2673 tempi; 2674 2675 for (var i=0, l=temp.length; i<l; i++) { 2676 tempi = temp[i]; 2677 if (opts.fillBetween[tempi] != null) { 2678 this.fillBetween[tempi] = opts.fillBetween[tempi]; 2679 } 2680 } 2681 } 2682 2683 if (opts.seriesColors) { 2684 this.seriesColors = opts.seriesColors; 2685 } 2686 if (opts.negativeSeriesColors) { 2687 this.negativeSeriesColors = opts.negativeSeriesColors; 2688 } 2689 if (opts.captureRightClick) { 2690 this.captureRightClick = opts.captureRightClick; 2691 } 2692 this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart; 2693 this.colorGenerator.setColors(this.seriesColors); 2694 this.negativeColorGenerator.setColors(this.negativeSeriesColors); 2695 // var cg = new this.colorGenerator(this.seriesColors); 2696 // var ncg = new this.colorGenerator(this.negativeSeriesColors); 2697 // this._gridPadding = this.options.gridPadding; 2698 $.extend(true, this._gridPadding, opts.gridPadding); 2699 this.sortData = (opts.sortData != null) ? opts.sortData : this.sortData; 2700 for (var i=0; i<12; i++) { 2701 var n = _axisNames[i]; 2702 var axis = this.axes[n]; 2703 axis._options = $.extend(true, {}, opts.axesDefaults, opts.axes[n]); 2704 $.extend(true, axis, opts.axesDefaults, opts.axes[n]); 2705 axis._plotWidth = this._width; 2706 axis._plotHeight = this._height; 2707 } 2708 // if (this.data.length == 0) { 2709 // this.data = []; 2710 // for (var i=0; i<this.options.series.length; i++) { 2711 // this.data.push(this.options.series.data); 2712 // } 2713 // } 2714 2715 var normalizeData = function(data, dir, start) { 2716 // return data as an array of point arrays, 2717 // in form [[x1,y1...], [x2,y2...], ...] 2718 var temp = []; 2719 var i, l; 2720 dir = dir || 'vertical'; 2721 if (!$.isArray(data[0])) { 2722 // we have a series of scalars. One line with just y values. 2723 // turn the scalar list of data into a data array of form: 2724 // [[1, data[0]], [2, data[1]], ...] 2725 for (i=0, l=data.length; i<l; i++) { 2726 if (dir == 'vertical') { 2727 temp.push([start + i, data[i]]); 2728 } 2729 else { 2730 temp.push([data[i], start+i]); 2731 } 2732 } 2733 } 2734 else { 2735 // we have a properly formatted data series, copy it. 2736 $.extend(true, temp, data); 2737 } 2738 return temp; 2739 }; 2740 2741 var colorIndex = 0; 2742 this.series = []; 2743 for (var i=0; i<this.data.length; i++) { 2744 var sopts = $.extend(true, {index: i}, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i], {rendererOptions:{animation:{show: this.animate}}}); 2745 // pass in options in case something needs set prior to initialization. 2746 var temp = new Series(sopts); 2747 for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) { 2748 $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]); 2749 } 2750 for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) { 2751 this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]); 2752 } 2753 // Now go back and apply the options to the series. Really should just do this during initializaiton, but don't want to 2754 // mess up preParseSeriesOptionsHooks at this point. 2755 $.extend(true, temp, sopts); 2756 var dir = 'vertical'; 2757 if (temp.renderer === $.jqplot.BarRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') { 2758 dir = 'horizontal'; 2759 temp._stackAxis = 'x'; 2760 temp._primaryAxis = '_yaxis'; 2761 } 2762 temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart); 2763 switch (temp.xaxis) { 2764 case 'xaxis': 2765 temp._xaxis = this.axes.xaxis; 2766 break; 2767 case 'x2axis': 2768 temp._xaxis = this.axes.x2axis; 2769 break; 2770 default: 2771 break; 2772 } 2773 temp._yaxis = this.axes[temp.yaxis]; 2774 temp._xaxis._series.push(temp); 2775 temp._yaxis._series.push(temp); 2776 if (temp.show) { 2777 temp._xaxis.show = true; 2778 temp._yaxis.show = true; 2779 } 2780 else { 2781 if (temp._xaxis.scaleToHiddenSeries) { 2782 temp._xaxis.show = true; 2783 } 2784 if (temp._yaxis.scaleToHiddenSeries) { 2785 temp._yaxis.show = true; 2786 } 2787 } 2788 2789 // // parse the renderer options and apply default colors if not provided 2790 // if (!temp.color && temp.show != false) { 2791 // temp.color = cg.next(); 2792 // colorIndex = cg.getIndex() - 1;; 2793 // } 2794 // if (!temp.negativeColor && temp.show != false) { 2795 // temp.negativeColor = ncg.get(colorIndex); 2796 // ncg.setIndex(colorIndex); 2797 // } 2798 if (!temp.label) { 2799 temp.label = 'Series '+ (i+1).toString(); 2800 } 2801 // temp.rendererOptions.show = temp.show; 2802 // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions); 2803 this.series.push(temp); 2804 for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) { 2805 $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]); 2806 } 2807 for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) { 2808 this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]); 2809 } 2810 } 2811 2812 // copy the grid and title options into this object. 2813 $.extend(true, this.grid, this.options.grid); 2814 // if axis border properties aren't set, set default. 2815 for (var i=0, l=_axisNames.length; i<l; i++) { 2816 var n = _axisNames[i]; 2817 var axis = this.axes[n]; 2818 if (axis.borderWidth == null) { 2819 axis.borderWidth =this.grid.borderWidth; 2820 } 2821 } 2822 2823 if (typeof this.options.title == 'string') { 2824 this.title.text = this.options.title; 2825 } 2826 else if (typeof this.options.title == 'object') { 2827 $.extend(true, this.title, this.options.title); 2828 } 2829 this.title._plotWidth = this._width; 2830 this.legend.setOptions(this.options.legend); 2831 2832 for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) { 2833 $.jqplot.postParseOptionsHooks[i].call(this, options); 2834 } 2835 for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) { 2836 this.postParseOptionsHooks.hooks[i].call(this, options); 2837 } 2838 }; 2839 2840 // method: destroy 2841 // Releases all resources occupied by the plot 2842 this.destroy = function() {
2843 this.canvasManager.freeAllCanvases(); 2844 if (this.eventCanvas && this.eventCanvas._elem) { 2845 this.eventCanvas._elem.unbind(); 2846 } 2847 // Couple of posts on Stack Overflow indicate that empty() doesn't 2848 // always cear up the dom and release memory. Sometimes setting 2849 // innerHTML property to null is needed. Particularly on IE, may 2850 // have to directly set it to null, bypassing $. 2851 this.target.empty(); 2852 2853 this.target[0].innerHTML = ''; 2854 }; 2855 2856 // method: replot 2857 // Does a reinitialization of the plot followed by 2858 // a redraw. Method could be used to interactively 2859 // change plot characteristics and then replot. 2860 // 2861 // Parameters: 2862 // options - Options used for replotting. 2863 // 2864 // Properties: 2865 // clear - false to not clear (empty) the plot container before replotting (default: true). 2866 // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves. 2867 // optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false). 2868 this.replot = function(options) { 2869 var opts = options || {}; 2870 var data = opts.data || null; 2871 var clear = (opts.clear === false) ? false : true; 2872 var resetAxes = opts.resetAxes || false; 2873 delete opts.data; 2874 delete opts.clear; 2875 delete opts.resetAxes; 2876 2877 this.target.trigger('jqplotPreReplot'); 2878 2879 if (clear) { 2880 this.destroy(); 2881 } 2882 // if have data or other options, full reinit. 2883 // otherwise, quickinit. 2884 if (data || !$.isEmptyObject(opts)) { 2885 this.reInitialize(data, opts); 2886 } 2887 else { 2888 this.quickInit(); 2889 } 2890 2891 if (resetAxes) { 2892 this.resetAxesScale(resetAxes, opts.axes); 2893 } 2894 this.draw(); 2895 this.target.trigger('jqplotPostReplot'); 2896 }; 2897 2898 // method: redraw 2899 // Empties the plot target div and redraws the plot. 2900 // This enables plot data and properties to be changed 2901 // and then to comletely clear the plot and redraw. 2902 // redraw *will not* reinitialize any plot elements. 2903 // That is, axes will not be autoscaled and defaults 2904 // will not be reapplied to any plot elements. redraw 2905 // is used primarily with zooming. 2906 // 2907 // Parameters: 2908 // clear - false to not clear (empty) the plot container before redrawing (default: true). 2909 this.redraw = function(clear) { 2910 clear = (clear != null) ? clear : true; 2911 this.target.trigger('jqplotPreRedraw'); 2912 if (clear) { 2913 this.canvasManager.freeAllCanvases(); 2914 this.eventCanvas._elem.unbind(); 2915 // Dont think I bind any events to the target, this shouldn't be necessary. 2916 // It will remove user's events. 2917 // this.target.unbind(); 2918 this.target.empty(); 2919 } 2920 for (var ax in this.axes) { 2921 this.axes[ax]._ticks = []; 2922 } 2923 this.computePlotData(); 2924 // for (var i=0; i<this.series.length; i++) { 2925 // this.populatePlotData(this.series[i], i); 2926 // } 2927 this._sumy = 0; 2928 this._sumx = 0; 2929 for (var i=0, tsl = this.series.length; i<tsl; i++) { 2930 this._sumy += this.series[i]._sumy; 2931 this._sumx += this.series[i]._sumx; 2932 } 2933 this.draw(); 2934 this.target.trigger('jqplotPostRedraw'); 2935 }; 2936 2937 // method: draw 2938 // Draws all elements of the plot into the container. 2939 // Does not clear the container before drawing. 2940 this.draw = function(){ 2941 if (this.drawIfHidden || this.target.is(':visible')) { 2942 this.target.trigger('jqplotPreDraw'); 2943 var i, 2944 j, 2945 l, 2946 tempseries; 2947 for (i=0, l=$.jqplot.preDrawHooks.length; i<l; i++) { 2948 $.jqplot.preDrawHooks[i].call(this); 2949 } 2950 for (i=0, l=this.preDrawHooks.hooks.length; i<l; i++) { 2951 this.preDrawHooks.hooks[i].apply(this, this.preDrawSeriesHooks.args[i]); 2952 } 2953 // create an underlying canvas to be used for special features. 2954 this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas', null, this)); 2955 this.baseCanvas.setContext(); 2956 this.target.append(this.title.draw());
2957 this.title.pack({top:0, left:0}); 2958 2959 // make room for the legend between the grid and the edge. 2960 // pass a dummy offsets object and a reference to the plot. 2961 var legendElem = this.legend.draw({}, this); 2962 2963 var gridPadding = {top:0, left:0, bottom:0, right:0}; 2964 2965 if (this.legend.placement == "outsideGrid") { 2966 // temporarily append the legend to get dimensions 2967 this.target.append(legendElem); 2968 switch (this.legend.location) { 2969 case 'n': 2970 gridPadding.top += this.legend.getHeight(); 2971 break; 2972 case 's': 2973 gridPadding.bottom += this.legend.getHeight(); 2974 break; 2975 case 'ne': 2976 case 'e': 2977 case 'se': 2978 gridPadding.right += this.legend.getWidth(); 2979 break; 2980 case 'nw': 2981 case 'w': 2982 case 'sw': 2983 gridPadding.left += this.legend.getWidth(); 2984 break; 2985 default: // same as 'ne' 2986 gridPadding.right += this.legend.getWidth(); 2987 break; 2988 } 2989 legendElem = legendElem.detach(); 2990 } 2991 2992 var ax = this.axes; 2993 var name; 2994 // draw the yMidAxis first, so xaxis of pyramid chart can adjust itself if needed. 2995 for (i=0; i<12; i++) { 2996 name = _axisNames[i]; 2997 this.target.append(ax[name].draw(this.baseCanvas._ctx, this)); 2998 ax[name].set(); 2999 } 3000 if (ax.yaxis.show) { 3001 gridPadding.left += ax.yaxis.getWidth(); 3002 } 3003 var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis']; 3004 var rapad = [0, 0, 0, 0, 0, 0, 0, 0]; 3005 var gpr = 0; 3006 var n; 3007 for (n=0; n<8; n++) { 3008 if (ax[ra[n]].show) { 3009 gpr += ax[ra[n]].getWidth(); 3010 rapad[n] = gpr; 3011 } 3012 } 3013 gridPadding.right += gpr; 3014 if (ax.x2axis.show) { 3015 gridPadding.top += ax.x2axis.getHeight(); 3016 } 3017 if (this.title.show) { 3018 gridPadding.top += this.title.getHeight(); 3019 } 3020 if (ax.xaxis.show) { 3021 gridPadding.bottom += ax.xaxis.getHeight(); 3022 } 3023 3024 // end of gridPadding adjustments. 3025 3026 // if user passed in gridDimensions option, check against calculated gridPadding 3027 if (this.options.gridDimensions && $.isPlainObject(this.options.gridDimensions)) { 3028 var gdw = parseInt(this.options.gridDimensions.width, 10) || 0; 3029 var gdh = parseInt(this.options.gridDimensions.height, 10) || 0; 3030 var widthAdj = (this._width - gridPadding.left - gridPadding.right - gdw)/2; 3031 var heightAdj = (this._height - gridPadding.top - gridPadding.bottom - gdh)/2; 3032 3033 if (heightAdj >= 0 && widthAdj >= 0) { 3034 gridPadding.top += heightAdj; 3035 gridPadding.bottom += heightAdj; 3036 gridPadding.left += widthAdj; 3037 gridPadding.right += widthAdj; 3038 } 3039 } 3040 var arr = ['top', 'bottom', 'left', 'right']; 3041 for (var n in arr) { 3042 if (this._gridPadding[arr[n]] == null && gridPadding[arr[n]] > 0) { 3043 this._gridPadding[arr[n]] = gridPadding[arr[n]]; 3044 } 3045 else if (this._gridPadding[arr[n]] == null) { 3046 this._gridPadding[arr[n]] = this._defaultGridPadding[arr[n]]; 3047 } 3048 } 3049 3050 var legendPadding = this._gridPadding; 3051 3052 if (this.legend.placement === 'outsideGrid') { 3053 legendPadding = {top:this.title.getHeight(), left: 0, right: 0, bottom: 0}; 3054 if (this.legend.location === 's') { 3055 legendPadding.left = this._gridPadding.left; 3056 legendPadding.right = this._gridPadding.right; 3057 } 3058 } 3059 3060 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});
3061 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}); 3062 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}); 3063 for (i=8; i>0; i--) { 3064 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}); 3065 } 3066 var ltemp = (this._width - this._gridPadding.left - this._gridPadding.right)/2.0 + this._gridPadding.left - ax.yMidAxis.getWidth()/2.0; 3067 ax.yMidAxis.pack({position:'absolute', top:0, left:ltemp, zIndex:9, textAlign: 'center'}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); 3068 3069 this.target.append(this.grid.createElement(this._gridPadding, this)); 3070 this.grid.draw(); 3071 3072 var series = this.series; 3073 var seriesLength = series.length; 3074 // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars. 3075 for (i=0, l=seriesLength; i<l; i++) { 3076 // draw series in order of stacking. This affects only 3077 // order in which canvases are added to dom. 3078 j = this.seriesStack[i]; 3079 this.target.append(series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas', null, this)); 3080 series[j].shadowCanvas.setContext(); 3081 series[j].shadowCanvas._elem.data('seriesIndex', j); 3082 } 3083 3084 for (i=0, l=seriesLength; i<l; i++) { 3085 // draw series in order of stacking. This affects only 3086 // order in which canvases are added to dom. 3087 j = this.seriesStack[i]; 3088 this.target.append(series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas', null, this)); 3089 series[j].canvas.setContext(); 3090 series[j].canvas._elem.data('seriesIndex', j); 3091 } 3092 // Need to use filled canvas to capture events in IE. 3093 // Also, canvas seems to block selection of other elements in document on FF. 3094 this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas', null, this)); 3095 this.eventCanvas.setContext(); 3096 this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)'; 3097 this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height); 3098 3099 // bind custom event handlers to regular events. 3100 this.bindCustomEvents(); 3101 3102 // draw legend before series if the series needs to know the legend dimensions. 3103 if (this.legend.preDraw) { 3104 this.eventCanvas._elem.before(legendElem); 3105 this.legend.pack(legendPadding); 3106 if (this.legend._elem) { 3107 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}}); 3108 } 3109 else { 3110 this.drawSeries(); 3111 } 3112 } 3113 else { // draw series before legend 3114 this.drawSeries(); 3115 if (seriesLength) { 3116 $(series[seriesLength-1].canvas._elem).after(legendElem); 3117 } 3118 this.legend.pack(legendPadding); 3119 } 3120 3121 // register event listeners on the overlay canvas 3122 for (var i=0, l=$.jqplot.eventListenerHooks.length; i<l; i++) { 3123 // in the handler, this will refer to the eventCanvas dom element. 3124 // make sure there are references back into plot objects. 3125 this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]); 3126 } 3127 3128 // register event listeners on the overlay canvas 3129 for (var i=0, l=this.eventListenerHooks.hooks.length; i<l; i++) { 3130 // in the handler, this will refer to the eventCanvas dom element. 3131 // make sure there are references back into plot objects. 3132 this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]); 3133 } 3134 3135 var fb = this.fillBetween; 3136 if(typeof fb.series1 == 'number'){ 3137 if(fb.fill&&fb.series1!==fb.series2&&fb.series1<seriesLength&&fb.series2<seriesLength&&series[fb.series1]._type==="line"&&series[fb.series2]._type==="line") 3138 this.doFillBetweenLines(); 3139 } 3140 else{ 3141 if(fb.series1 != null && fb.series2 != null){ 3142 var doFb = false; 3143 if(fb.series1.length === fb.series2.length){ 3144 var tempSeries1 = 0; 3145 var tempSeries2 = 0; 3146 3147 for(var cnt = 0; cnt < fb.series1.length; cnt++){ 3148 tempSeries1 = fb.series1[cnt]; 3149 tempSeries2 = fb.series2[cnt]; 3150 if(tempSeries1!==tempSeries2&&tempSeries1<seriesLength&&tempSeries2<seriesLength&&series[tempSeries1]._type==="line"&&series[tempSeries2]._type==="line"){ 3151 doFb = true; 3152 } 3153 else{ 3154 doFb = false; 3155 break; 3156 } 3157 } 3158 } 3159 if(fb.fill && doFb){ 3160 this.doFillBetweenLines(); 3161 } 3162 } 3163 } 3164 3165 for (var i=0, l=$.jqplot.postDrawHooks.length; i<l; i++) { 3166 $.jqplot.postDrawHooks[i].call(this); 3167 } 3168 3169 for (var i=0, l=this.postDrawHooks.hooks.length; i<l; i++) { 3170 this.postDrawHooks.hooks[i].apply(this, this.postDrawHooks.args[i]); 3171 } 3172 3173 if (this.target.is(':visible')) { 3174 this._drawCount += 1; 3175 } 3176 3177 var temps, 3178 tempr, 3179 sel, 3180 _els; 3181 // ughh. ideally would hide all series then show them. 3182 for (i=0, l=seriesLength; i<l; i++) { 3183 temps = series[i];
3184 tempr = temps.renderer; 3185 sel = '.jqplot-point-label.jqplot-series-'+i; 3186 if (tempr.animation && tempr.animation._supported && tempr.animation.show && (this._drawCount < 2 || this.animateReplot)) { 3187 _els = this.target.find(sel); 3188 _els.stop(true, true).hide(); 3189 temps.canvas._elem.stop(true, true).hide(); 3190 temps.shadowCanvas._elem.stop(true, true).hide(); 3191 temps.canvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed); 3192 temps.shadowCanvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed); 3193 _els.fadeIn(tempr.animation.speed*0.8); 3194 } 3195 } 3196 _els = null; 3197 3198 this.target.trigger('jqplotPostDraw', [this]); 3199 } 3200 }; 3201 3202 jqPlot.prototype.doFillBetweenLines = function () { 3203 var fb = this.fillBetween; 3204 var series = this.series; 3205 var sid1 = fb.series1; 3206 var sid2 = fb.series2; 3207 var id1 = 0, id2 = 0; 3208 3209 function fill(id1, id2){ 3210 var series1 = series[id1]; 3211 var series2 = series[id2]; 3212 if (series2.renderer.smooth) 3213 var tempgd = series2.renderer._smoothedData.slice(0).reverse(); 3214 else 3215 var tempgd = series2.gridData.slice(0).reverse(); 3216 if (series1.renderer.smooth) 3217 var gd = series1.renderer._smoothedData.concat(tempgd); 3218 else 3219 var gd = series1.gridData.concat(tempgd); 3220 var color = fb.color !== null ? fb.color : series[sid1].fillColor; 3221 var baseSeries = fb.baseSeries !== null ? fb.baseSeries : id1; 3222 var sr = 3223 series[baseSeries].renderer.shapeRenderer; 3224 var opts = 3225 { 3226 fillStyle : color, 3227 fill : true, 3228 closePath : true 3229 }; 3230 sr.draw(series1.shadowCanvas._ctx, gd, opts) 3231 } 3232 3233 if(typeof sid1 == 'number' && typeof sid2 == 'number'){ 3234 id1 = sid1 < sid2 ? sid1 : sid2; 3235 id2 = sid2 > sid1 ? sid2 : sid1; 3236 fill(id1, id2); 3237 } 3238 else{ 3239 for(var cnt = 0; cnt < sid1.length ; cnt++){ 3240 id1 = sid1[cnt] < sid2[cnt] ? sid1[cnt] : sid2[cnt]; 3241 id2 = sid2[cnt] > sid1[cnt] ? sid2[cnt] : sid1[cnt]; 3242 fill(id1, id2); 3243 } 3244 } 3245 }; 3246 3247 this.bindCustomEvents = function() { 3248 this.eventCanvas._elem.bind('click', {plot:this}, this.onClick); 3249 this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick); 3250 this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown); 3251 this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove); 3252 this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter); 3253 this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave); 3254 if (this.captureRightClick) { 3255 this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick); 3256 this.eventCanvas._elem.get(0).oncontextmenu = function() { 3257 return false; 3258 }; 3259 } 3260 else { 3261 this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp); 3262 } 3263 }; 3264 3265 function getEventPosition(ev) { 3266 var plot = ev.data.plot; 3267 var go = plot.eventCanvas._elem.offset(); 3268 var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top}; 3269 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}; 3270 var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; 3271 var ax = plot.axes; 3272 var n, axis; 3273 for (n=11; n>0; n--) { 3274 axis = an[n-1]; 3275 if (ax[axis].show) { 3276 dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]); 3277 } 3278 } 3279 3280 return {offsets:go, gridPos:gridPos, dataPos:dataPos}; 3281 } 3282 3283 3284 // function to check if event location is over a area area 3285 function checkIntersection(gridpos, plot) { 3286 var series = plot.series; 3287 var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang; 3288 var d0, d, p, pp, points, bw, hp; 3289 var threshold, t; 3290 for (k=plot.seriesStack.length-1; k>=0; k--) { 3291 i = plot.seriesStack[k]; 3292 s = series[i];
3293 hp = s._highlightThreshold; 3294 switch (s.renderer.constructor) { 3295 case $.jqplot.BarRenderer: 3296 x = gridpos.x; 3297 y = gridpos.y; 3298 for (j=0; j<s._barPoints.length; j++) { 3299 points = s._barPoints[j]; 3300 p = s.gridData[j]; 3301 if (x>points[0][0] && x<points[2][0] && (y>points[2][1] && y<points[0][1] || y<points[2][1] && y>points[0][1])) { 3302 return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]}; 3303 } 3304 } 3305 break; 3306 case $.jqplot.PyramidRenderer: 3307 x = gridpos.x; 3308 y = gridpos.y; 3309 for (j=0; j<s._barPoints.length; j++) { 3310 points = s._barPoints[j]; 3311 p = s.gridData[j]; 3312 if (x > points[0][0] + hp[0][0] && x < points[2][0] + hp[2][0] && y > points[2][1] && y < points[0][1]) { 3313 return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]}; 3314 } 3315 } 3316 break; 3317 3318 case $.jqplot.DonutRenderer: 3319 sa = s.startAngle/180*Math.PI; 3320 x = gridpos.x - s._center[0]; 3321 y = gridpos.y - s._center[1]; 3322 r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); 3323 if (x > 0 && -y >= 0) { 3324 theta = 2*Math.PI - Math.atan(-y/x); 3325 } 3326 else if (x > 0 && -y < 0) { 3327 theta = -Math.atan(-y/x); 3328 } 3329 else if (x < 0) { 3330 theta = Math.PI - Math.atan(-y/x); 3331 } 3332 else if (x == 0 && -y > 0) { 3333 theta = 3*Math.PI/2; 3334 } 3335 else if (x == 0 && -y < 0) { 3336 theta = Math.PI/2; 3337 } 3338 else if (x == 0 && y == 0) { 3339 theta = 0; 3340 } 3341 if (sa) { 3342 theta -= sa; 3343 if (theta < 0) { 3344 theta += 2*Math.PI; 3345 } 3346 else if (theta > 2*Math.PI) { 3347 theta -= 2*Math.PI; 3348 } 3349 } 3350 3351 sm = s.sliceMargin/180*Math.PI; 3352 if (r < s._radius && r > s._innerRadius) { 3353 for (j=0; j<s.gridData.length; j++) { 3354 minang = (j>0) ? s.gridData[j-1][1]+sm : sm; 3355 maxang = s.gridData[j][1]; 3356 if (theta > minang && theta < maxang) { 3357 return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]}; 3358 } 3359 } 3360 } 3361 break; 3362 3363 case $.jqplot.PieRenderer: 3364 sa = s.startAngle/180*Math.PI; 3365 x = gridpos.x - s._center[0]; 3366 y = gridpos.y - s._center[1]; 3367 r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); 3368 if (x > 0 && -y >= 0) { 3369 theta = 2*Math.PI - Math.atan(-y/x); 3370 } 3371 else if (x > 0 && -y < 0) { 3372 theta = -Math.atan(-y/x); 3373 } 3374 else if (x < 0) { 3375 theta = Math.PI - Math.atan(-y/x); 3376 } 3377 else if (x == 0 && -y > 0) { 3378 theta = 3*Math.PI/2; 3379 } 3380 else if (x == 0 && -y < 0) { 3381 theta = Math.PI/2; 3382 } 3383 else if (x == 0 && y == 0) { 3384 theta = 0; 3385 } 3386 if (sa) { 3387 theta -= sa; 3388 if (theta < 0) { 3389 theta += 2*Math.PI; 3390 } 3391 else if (theta > 2*Math.PI) { 3392 theta -= 2*Math.PI; 3393 } 3394 } 3395 3396 sm = s.sliceMargin/180*Math.PI; 3397 if (r < s._radius) { 3398 for (j=0; j<s.gridData.length; j++) { 3399 minang = (j>0) ? s.gridData[j-1][1]+sm : sm; 3400 maxang = s.gridData[j][1]; 3401 if (theta > minang && theta < maxang) { 3402 return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]}; 3403 } 3404 } 3405 } 3406 break; 3407 3408 case $.jqplot.BubbleRenderer: 3409 x = gridpos.x; 3410 y = gridpos.y; 3411 var ret = null; 3412 3413 if (s.show) { 3414 for (var j=0; j<s.gridData.length; j++) { 3415 p = s.gridData[j]; 3416 d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-
3416p[1]) * (y-p[1]) ); 3417 if (d <= p[2] && (d <= d0 || d0 == null)) { 3418 d0 = d; 3419 ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3420 } 3421 } 3422 if (ret != null) { 3423 return ret; 3424 } 3425 } 3426 break; 3427 3428 case $.jqplot.FunnelRenderer: 3429 x = gridpos.x; 3430 y = gridpos.y; 3431 var v = s._vertices, 3432 vfirst = v[0], 3433 vlast = v[v.length-1], 3434 lex, 3435 rex, 3436 cv; 3437 3438 // equations of right and left sides, returns x, y values given height of section (y value and 2 points) 3439 3440 function findedge (l, p1 , p2) { 3441 var m = (p1[1] - p2[1])/(p1[0] - p2[0]); 3442 var b = p1[1] - m*p1[0]; 3443 var y = l + p1[1]; 3444 3445 return [(y - b)/m, y]; 3446 } 3447 3448 // check each section 3449 lex = findedge(y, vfirst[0], vlast[3]); 3450 rex = findedge(y, vfirst[1], vlast[2]); 3451 for (j=0; j<v.length; j++) { 3452 cv = v[j]; 3453 if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) { 3454 return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]}; 3455 } 3456 } 3457 break; 3458 3459 case $.jqplot.LineRenderer: 3460 x = gridpos.x; 3461 y = gridpos.y; 3462 r = s.renderer; 3463 if (s.show) { 3464 if ((s.fill || (s.renderer.bands.show && s.renderer.bands.fill)) && (!plot.plugins.highlighter || !plot.plugins.highlighter.show)) { 3465 // first check if it is in bounding box 3466 var inside = false; 3467 if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) { 3468 // now check the crossing number 3469 3470 var numPoints = s._areaPoints.length; 3471 var ii; 3472 var j = numPoints-1; 3473 3474 for(var ii=0; ii < numPoints; ii++) { 3475 var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]]; 3476 var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]]; 3477 3478 if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y) { 3479 if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) { 3480 inside = !inside; 3481 } 3482 } 3483 3484 j = ii; 3485 } 3486 } 3487 if (inside) { 3488 return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints}; 3489 } 3490 break; 3491 3492 } 3493 3494 else { 3495 t = s.markerRenderer.size/2+s.neighborThreshold; 3496 threshold = (t > 0) ? t : 0; 3497 for (var j=0; j<s.gridData.length; j++) { 3498 p = s.gridData[j]; 3499 // neighbor looks different to OHLC chart. 3500 if (r.constructor == $.jqplot.OHLCRenderer) { 3501 if (r.candleStick) { 3502 var yp = s._yaxis.series_u2p; 3503 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])) { 3504 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3505 } 3506 } 3507 // if an open hi low close chart 3508 else if (!r.hlc){ 3509 var yp = s._yaxis.series_u2p; 3510 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { 3511 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3512 } 3513 } 3514 // a hi low close chart 3515 else { 3516 var yp = s._yaxis.series_u2p; 3517 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) { 3518 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3519 } 3520 } 3521 3522 } 3523 else if (p[0] != null && p[1] != null){ 3524 d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-
3524p[1]) * (y-p[1]) ); 3525 if (d <= threshold && (d <= d0 || d0 == null)) { 3526 d0 = d; 3527 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3528 } 3529 } 3530 } 3531 } 3532 } 3533 break; 3534 3535 default: 3536 x = gridpos.x; 3537 y = gridpos.y; 3538 r = s.renderer; 3539 if (s.show) { 3540 t = s.markerRenderer.size/2+s.neighborThreshold; 3541 threshold = (t > 0) ? t : 0; 3542 for (var j=0; j<s.gridData.length; j++) { 3543 p = s.gridData[j]; 3544 // neighbor looks different to OHLC chart. 3545 if (r.constructor == $.jqplot.OHLCRenderer) { 3546 if (r.candleStick) { 3547 var yp = s._yaxis.series_u2p; 3548 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])) { 3549 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3550 } 3551 } 3552 // if an open hi low close chart 3553 else if (!r.hlc){ 3554 var yp = s._yaxis.series_u2p; 3555 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { 3556 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3557 } 3558 } 3559 // a hi low close chart 3560 else { 3561 var yp = s._yaxis.series_u2p; 3562 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) { 3563 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3564 } 3565 } 3566 3567 } 3568 else { 3569 d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) ); 3570 if (d <= threshold && (d <= d0 || d0 == null)) { 3571 d0 = d; 3572 return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; 3573 } 3574 } 3575 } 3576 } 3577 break; 3578 } 3579 } 3580 3581 return null; 3582 } 3583 3584 3585 3586 this.onClick = function(ev) { 3587 // Event passed in is normalized and will have data attribute. 3588 // Event passed out is unnormalized. 3589 var positions = getEventPosition(ev); 3590 var p = ev.data.plot; 3591 var neighbor = checkIntersection(positions.gridPos, p); 3592 var evt = $.Event('jqplotClick'); 3593 evt.pageX = ev.pageX; 3594 evt.pageY = ev.pageY; 3595 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3596 }; 3597 3598 this.onDblClick = function(ev) { 3599 // Event passed in is normalized and will have data attribute. 3600 // Event passed out is unnormalized. 3601 var positions = getEventPosition(ev); 3602 var p = ev.data.plot; 3603 var neighbor = checkIntersection(positions.gridPos, p); 3604 var evt = $.Event('jqplotDblClick'); 3605 evt.pageX = ev.pageX; 3606 evt.pageY = ev.pageY; 3607 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3608 }; 3609 3610 this.onMouseDown = function(ev) { 3611 var positions = getEventPosition(ev); 3612 var p = ev.data.plot; 3613 var neighbor = checkIntersection(positions.gridPos, p); 3614 var evt = $.Event('jqplotMouseDown'); 3615 evt.pageX = ev.pageX; 3616 evt.pageY = ev.pageY; 3617 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3618 }; 3619 3620 this.onMouseUp = function(ev) { 3621 var positions = getEventPosition(ev); 3622 var evt = $.Event('jqplotMouseUp'); 3623 evt.pageX = ev.pageX; 3624 evt.pageY = ev.pageY; 3625 $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]); 3626 }; 3627 3628 this.onRightClick = function(ev) { 3629 var positions = getEventPosition(ev); 3630 var p = ev.data.plot; 3631 var neighbor = checkIntersection(positions.gridPos, p); 3632 if (p.captureRightClick) { 3633 if (ev.which == 3) { 3634 var evt = $.Event('jqplotRightClick'); 3635 evt.pageX = ev.pageX; 3636 evt.pageY = ev.pageY; 3637 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3638 } 3639 else { 3640 var evt = $.Event('jqplotMouseUp'); 3641 evt.pageX = ev.pageX; 3642 evt.pageY = ev.pageY; 3643 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3644 } 3645 } 3646 }; 3647 3648 this.onMouseMove = function(ev) { 3649 var positions = getEventPosition(ev); 3650 var p = ev.data.plot; 3651 var neighbor = checkIntersection(positions.gridPos, p); 3652 var evt = $.Event('jqplotMouseMove'); 3653 evt.pageX = ev.pageX; 3654 evt.pageY = ev.pageY; 3655 $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); 3656 }; 3657 3658 this.onMouseEnter = function(ev) { 3659 var positions = getEventPosition(ev); 3660 var p = ev.data.plot; 3661 var evt = $.Event('jqplotMouseEnter'); 3662 evt.pageX = ev.pageX; 3663 evt.pageY = ev.pageY; 3664 evt.relatedTarget = ev.relatedTarget; 3665 $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]); 3666 }; 3667 3668 this.onMouseLeave = function(ev) { 3669 var positions = getEventPosition(ev); 3670 var p = ev.data.plot; 3671 var evt = $.Event('jqplotMouseLeave'); 3672 evt.pageX = ev.pageX; 3673 evt.pageY = ev.pageY; 3674 evt.relatedTarget = ev.relatedTarget; 3675 $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]); 3676 }; 3677 3678 // method: drawSeries 3679 // Redraws all or just one series on the plot. No axis scaling 3680 // is performed and no other elements on the plot are redrawn. 3681 // options is an options object to pass on to the series renderers. 3682 // It can be an empty object {}. idx is the series index 3683 // to redraw if only one series is to be redrawn. 3684 this.drawSeries = function(options, idx){ 3685 var i, series, ctx; 3686 // if only one argument passed in and it is a number, use it ad idx.
3687 idx = (typeof(options) === "number" && idx == null) ? options : idx; 3688 options = (typeof(options) === "object") ? options : {}; 3689 // draw specified series 3690 if (idx != undefined) { 3691 series = this.series[idx]; 3692 ctx = series.shadowCanvas._ctx; 3693 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 3694 series.drawShadow(ctx, options, this); 3695 ctx = series.canvas._ctx; 3696 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 3697 series.draw(ctx, options, this); 3698 if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) { 3699 if (idx < this.series.length - 1) { 3700 this.drawSeries(idx+1); 3701 } 3702 } 3703 } 3704 3705 else { 3706 // if call series drawShadow method first, in case all series shadows 3707 // should be drawn before any series. This will ensure, like for 3708 // stacked bar plots, that shadows don't overlap series. 3709 for (i=0; i<this.series.length; i++) { 3710 // first clear the canvas 3711 series = this.series[i]; 3712 ctx = series.shadowCanvas._ctx; 3713 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 3714 series.drawShadow(ctx, options, this); 3715 ctx = series.canvas._ctx; 3716 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 3717 series.draw(ctx, options, this); 3718 } 3719 } 3720 options = idx = i = series = ctx = null; 3721 }; 3722 3723 // method: moveSeriesToFront 3724 // This method requires jQuery 1.4+ 3725 // Moves the specified series canvas in front of all other series canvases. 3726 // This effectively "draws" the specified series on top of all other series, 3727 // although it is performed through DOM manipulation, no redrawing is performed. 3728 // 3729 // Parameters: 3730 // idx - 0 based index of the series to move. This will be the index of the series 3731 // as it was first passed into the jqplot function. 3732 this.moveSeriesToFront = function (idx) { 3733 idx = parseInt(idx, 10); 3734 var stackIndex = $.inArray(idx, this.seriesStack); 3735 // if already in front, return 3736 if (stackIndex == -1) { 3737 return; 3738 } 3739 if (stackIndex == this.seriesStack.length -1) { 3740 this.previousSeriesStack = this.seriesStack.slice(0); 3741 return; 3742 } 3743 var opidx = this.seriesStack[this.seriesStack.length -1]; 3744 var serelem = this.series[idx].canvas._elem.detach(); 3745 var shadelem = this.series[idx].shadowCanvas._elem.detach(); 3746 this.series[opidx].shadowCanvas._elem.after(shadelem); 3747 this.series[opidx].canvas._elem.after(serelem); 3748 this.previousSeriesStack = this.seriesStack.slice(0); 3749 this.seriesStack.splice(stackIndex, 1); 3750 this.seriesStack.push(idx); 3751 }; 3752 3753 // method: moveSeriesToBack 3754 // This method requires jQuery 1.4+ 3755 // Moves the specified series canvas behind all other series canvases. 3756 // 3757 // Parameters: 3758 // idx - 0 based index of the series to move. This will be the index of the series 3759 // as it was first passed into the jqplot function. 3760 this.moveSeriesToBack = function (idx) { 3761 idx = parseInt(idx, 10); 3762 var stackIndex = $.inArray(idx, this.seriesStack); 3763 // if already in back, return 3764 if (stackIndex == 0 || stackIndex == -1) { 3765 return; 3766 } 3767 var opidx = this.seriesStack[0]; 3768 var serelem = this.series[idx].canvas._elem.detach(); 3769 var shadelem = this.series[idx].shadowCanvas._elem.detach(); 3770 this.series[opidx].shadowCanvas._elem.before(shadelem); 3771 this.series[opidx].canvas._elem.before(serelem); 3772 this.previousSeriesStack = this.seriesStack.slice(0); 3773 this.seriesStack.splice(stackIndex, 1); 3774 this.seriesStack.unshift(idx); 3775 }; 3776 3777 // method: restorePreviousSeriesOrder 3778 // This method requires jQuery 1.4+ 3779 // Restore the series canvas order to its previous state.
3780 // Useful to put a series back where it belongs after moving 3781 // it to the front. 3782 this.restorePreviousSeriesOrder = function () { 3783 var i, j, serelem, shadelem, temp, move, keep; 3784 // if no change, return. 3785 if (this.seriesStack == this.previousSeriesStack) { 3786 return; 3787 } 3788 for (i=1; i<this.previousSeriesStack.length; i++) { 3789 move = this.previousSeriesStack[i]; 3790 keep = this.previousSeriesStack[i-1]; 3791 serelem = this.series[move].canvas._elem.detach(); 3792 shadelem = this.series[move].shadowCanvas._elem.detach(); 3793 this.series[keep].shadowCanvas._elem.after(shadelem); 3794 this.series[keep].canvas._elem.after(serelem); 3795 } 3796 temp = this.seriesStack.slice(0); 3797 this.seriesStack = this.previousSeriesStack.slice(0); 3798 this.previousSeriesStack = temp; 3799 }; 3800 3801 // method: restoreOriginalSeriesOrder 3802 // This method requires jQuery 1.4+ 3803 // Restore the series canvas order to its original order 3804 // when the plot was created. 3805 this.restoreOriginalSeriesOrder = function () { 3806 var i, j, arr=[], serelem, shadelem; 3807 for (i=0; i<this.series.length; i++) { 3808 arr.push(i); 3809 } 3810 if (this.seriesStack == arr) { 3811 return; 3812 } 3813 this.previousSeriesStack = this.seriesStack.slice(0); 3814 this.seriesStack = arr; 3815 for (i=1; i<this.seriesStack.length; i++) { 3816 serelem = this.series[i].canvas._elem.detach(); 3817 shadelem = this.series[i].shadowCanvas._elem.detach(); 3818 this.series[i-1].shadowCanvas._elem.after(shadelem); 3819 this.series[i-1].canvas._elem.after(serelem); 3820 } 3821 }; 3822 3823 this.activateTheme = function (name) { 3824 this.themeEngine.activate(this, name); 3825 }; 3826 } 3827 3828 3829 // conpute a highlight color or array of highlight colors from given colors. 3830 $.jqplot.computeHighlightColors = function(colors) { 3831 var ret; 3832 if ($.isArray(colors)) { 3833 ret = []; 3834 for (var i=0; i<colors.length; i++){ 3835 var rgba = $.jqplot.getColorComponents(colors[i]); 3836 var newrgb = [rgba[0], rgba[1], rgba[2]]; 3837 var sum = newrgb[0] + newrgb[1] + newrgb[2]; 3838 for (var j=0; j<3; j++) { 3839 // when darkening, lowest color component can be is 60. 3840 newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90; 3841 newrgb[j] = parseInt(newrgb[j], 10); 3842 (newrgb[j] > 255) ? 255 : newrgb[j]; 3843 } 3844 // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5; 3845 // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2; 3846 newrgb[3] = 0.3 + 0.35 * rgba[3]; 3847 ret.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')'); 3848 } 3849 } 3850 else { 3851 var rgba = $.jqplot.getColorComponents(colors); 3852 var newrgb = [rgba[0], rgba[1], rgba[2]]; 3853 var sum = newrgb[0] + newrgb[1] + newrgb[2]; 3854 for (var j=0; j<3; j++) { 3855 // when darkening, lowest color component can be is 60. 3856 // newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); 3857 // newrgb[j] = parseInt(newrgb[j], 10); 3858 newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90; 3859 newrgb[j] = parseInt(newrgb[j], 10); 3860 (newrgb[j] > 255) ? 255 : newrgb[j]; 3861 } 3862 // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5; 3863 // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2; 3864 newrgb[3] = 0.3 + 0.35 * rgba[3]; 3865 ret = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')'; 3866 } 3867 return ret; 3868 }; 3869 3870 $.jqplot.ColorGenerator = function(colors) { 3871 colors = colors || $.jqplot.config.defaultColors; 3872 var idx = 0; 3873 3874 this.next = function () { 3875 if (idx < colors.length) { 3876 return colors[idx++]; 3877 } 3878 else { 3879 idx = 0; 3880 return colors[idx++]; 3881 } 3882 }; 3883 3884 this.previous = function () { 3885 if (idx > 0) { 3886 return colors[idx--]; 3887 } 3888 else { 3889 idx = colors.length-1; 3890 return colors[idx]; 3891 } 3892 }; 3893 3894 // get a color by index without advancing pointer. 3895 this.get = function(i) { 3896 var idx = i - colors.length * Math.floor(i/colors.length); 3897 return colors[idx]; 3898 }; 3899 3900 this.setColors = function(c) { 3901 colors = c; 3902 }; 3903 3904 this.reset = function() { 3905 idx = 0; 3906 }; 3907 3908 this.getIndex = function() { 3909 return idx; 3910 }; 3911 3912 this.setIndex = function(index) { 3913 idx = index; 3914 }; 3915 }; 3916 3917 // convert a hex color string to rgb string. 3918 // h - 3 or 6 character hex string, with or without leading # 3919 // a - optional alpha 3920 $.jqplot.hex2rgb = function(h, a) { 3921 h = h.replace('#', ''); 3922 if (h.length == 3) { 3923 h = h.charAt(0)+h.charAt(0)+h.charAt(1)+h.charAt(1)+h.charAt(2)+h.charAt(2); 3924 } 3925 var rgb; 3926 rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16); 3927 if (a) { 3928 rgb += ', '+a; 3929 } 3930 rgb += ')'; 3931 return rgb; 3932 }; 3933 3934 // convert an rgb color spec to a hex spec. ignore any alpha specification. 3935 $.jqplot.rgb2hex = function(s) { 3936 var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/; 3937 var m = s.match(pat); 3938 var h = '#'; 3939 for (var i=1; i<4; i++) { 3940 var temp; 3941 if (m[i].search(/%/) != -1) { 3942 temp = parseInt(255*m[i]/100, 10).toString(16); 3943 if (temp.length == 1) { 3944 temp = '0'+temp; 3945 } 3946 } 3947 else { 3948 temp = parseInt(m[i], 10).toString(16); 3949 if (temp.length == 1) { 3950 temp = '0'+temp; 3951 } 3952 } 3953 h += temp; 3954 } 3955 return h; 3956 }; 3957 3958 // given a css color spec, return an rgb css color spec 3959 $.jqplot.normalize2rgb = function(s, a) { 3960 if (s.search(/^ *rgba?\(/) != -1) { 3961 return s; 3962 } 3963 else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) { 3964 return $.jqplot.hex2rgb(s, a); 3965 } 3966 else { 3967 throw new Error('Invalid color spec'); 3968 } 3969 }; 3970 3971 // extract the r, g, b, a color components out of a css color spec. 3972 $.jqplot.getColorComponents = function(s) { 3973 // check to see if a color keyword. 3974 s = $.jqplot.colorKeywordMap[s] || s; 3975 var rgb = $.jqplot.normalize2rgb(s); 3976 var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/; 3977 var m = rgb.match(pat); 3978 var ret = []; 3979 for (var i=1; i<4; i++) { 3980 if (m[i].search(/%/) != -1) { 3981 ret[i-1] = parseInt(255*m[i]/100, 10); 3982 } 3983 else { 3984 ret[i-1] = parseInt(m[i], 10); 3985 } 3986 } 3987 ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0; 3988 return ret; 3989 }; 3990 3991 $.jqplot.colorKeywordMap = { 3992 aliceblue: 'rgb(240, 248, 255)', 3993 antiquewhite: 'rgb(250, 235, 215)', 3994 aqua: 'rgb( 0, 255, 255)', 3995 aquamarine: 'rgb(127, 255, 212)', 3996 azure: 'rgb(240, 255, 255)', 3997 beige: 'rgb(245, 245, 220)', 3998 bisque: 'rgb(255, 228, 196)', 3999 black: 'rgb( 0, 0, 0)', 4000 blanchedalmond: 'rgb(255, 235, 205)', 4001 blue: 'rgb( 0, 0, 255)', 4002 blueviolet: 'rgb(138, 43, 226)', 4003 brown: 'rgb(165, 42, 42)', 4004 burlywood: 'rgb(222, 184, 135)', 4005 cadetblue: 'rgb( 95, 158, 160)', 4006 chartreuse: 'rgb(127, 255, 0)', 4007 chocolate: 'rgb(210, 105, 30)', 4008 coral: 'rgb(255, 127, 80)', 4009 cornflowerblue: 'rgb(100, 149, 237)', 4010 cornsilk: 'rgb(255, 248, 220)', 4011 crimson: 'rgb(220, 20, 60)', 4012 cyan: 'rgb( 0, 255, 255)', 4013 darkblue: 'rgb( 0, 0, 139)', 4014 darkcyan: 'rgb( 0, 139, 139)', 4015 darkgoldenrod: 'rgb(184, 134, 11)', 4016 darkgray: 'rgb(169, 169, 169)', 4017 darkgreen: 'rgb( 0, 100, 0)', 4018 darkgrey: 'rgb(169, 169, 169)', 4019 darkkhaki: 'rgb(189, 183, 107)', 4020 darkmagenta: 'rgb(139, 0, 139)', 4021 darkolivegreen: 'rgb( 85, 107, 47)', 4022 darkorange: 'rgb(255, 140, 0)', 4023 darkorchid: 'rgb(153, 50, 204)',
4024 darkred: 'rgb(139, 0, 0)', 4025 darksalmon: 'rgb(233, 150, 122)', 4026 darkseagreen: 'rgb(143, 188, 143)', 4027 darkslateblue: 'rgb( 72, 61, 139)', 4028 darkslategray: 'rgb( 47, 79, 79)', 4029 darkslategrey: 'rgb( 47, 79, 79)', 4030 darkturquoise: 'rgb( 0, 206, 209)', 4031 darkviolet: 'rgb(148, 0, 211)', 4032 deeppink: 'rgb(255, 20, 147)', 4033 deepskyblue: 'rgb( 0, 191, 255)', 4034 dimgray: 'rgb(105, 105, 105)', 4035 dimgrey: 'rgb(105, 105, 105)', 4036 dodgerblue: 'rgb( 30, 144, 255)', 4037 firebrick: 'rgb(178, 34, 34)', 4038 floralwhite: 'rgb(255, 250, 240)', 4039 forestgreen: 'rgb( 34, 139, 34)', 4040 fuchsia: 'rgb(255, 0, 255)', 4041 gainsboro: 'rgb(220, 220, 220)', 4042 ghostwhite: 'rgb(248, 248, 255)', 4043 gold: 'rgb(255, 215, 0)', 4044 goldenrod: 'rgb(218, 165, 32)', 4045 gray: 'rgb(128, 128, 128)', 4046 grey: 'rgb(128, 128, 128)', 4047 green: 'rgb( 0, 128, 0)', 4048 greenyellow: 'rgb(173, 255, 47)', 4049 honeydew: 'rgb(240, 255, 240)', 4050 hotpink: 'rgb(255, 105, 180)', 4051 indianred: 'rgb(205, 92, 92)', 4052 indigo: 'rgb( 75, 0, 130)', 4053 ivory: 'rgb(255, 255, 240)', 4054 khaki: 'rgb(240, 230, 140)', 4055 lavender: 'rgb(230, 230, 250)', 4056 lavenderblush: 'rgb(255, 240, 245)', 4057 lawngreen: 'rgb(124, 252, 0)', 4058 lemonchiffon: 'rgb(255, 250, 205)', 4059 lightblue: 'rgb(173, 216, 230)', 4060 lightcoral: 'rgb(240, 128, 128)', 4061 lightcyan: 'rgb(224, 255, 255)', 4062 lightgoldenrodyellow: 'rgb(250, 250, 210)', 4063 lightgray: 'rgb(211, 211, 211)', 4064 lightgreen: 'rgb(144, 238, 144)', 4065 lightgrey: 'rgb(211, 211, 211)', 4066 lightpink: 'rgb(255, 182, 193)', 4067 lightsalmon: 'rgb(255, 160, 122)', 4068 lightseagreen: 'rgb( 32, 178, 170)', 4069 lightskyblue: 'rgb(135, 206, 250)', 4070 lightslategray: 'rgb(119, 136, 153)', 4071 lightslategrey: 'rgb(119, 136, 153)', 4072 lightsteelblue: 'rgb(176, 196, 222)', 4073 lightyellow: 'rgb(255, 255, 224)', 4074 lime: 'rgb( 0, 255, 0)', 4075 limegreen: 'rgb( 50, 205, 50)', 4076 linen: 'rgb(250, 240, 230)', 4077 magenta: 'rgb(255, 0, 255)',
4078 maroon: 'rgb(128, 0, 0)', 4079 mediumaquamarine: 'rgb(102, 205, 170)', 4080 mediumblue: 'rgb( 0, 0, 205)', 4081 mediumorchid: 'rgb(186, 85, 211)', 4082 mediumpurple: 'rgb(147, 112, 219)', 4083 mediumseagreen: 'rgb( 60, 179, 113)', 4084 mediumslateblue: 'rgb(123, 104, 238)', 4085 mediumspringgreen: 'rgb( 0, 250, 154)', 4086 mediumturquoise: 'rgb( 72, 209, 204)', 4087 mediumvioletred: 'rgb(199, 21, 133)', 4088 midnightblue: 'rgb( 25, 25, 112)', 4089 mintcream: 'rgb(245, 255, 250)', 4090 mistyrose: 'rgb(255, 228, 225)', 4091 moccasin: 'rgb(255, 228, 181)', 4092 navajowhite: 'rgb(255, 222, 173)', 4093 navy: 'rgb( 0, 0, 128)', 4094 oldlace: 'rgb(253, 245, 230)', 4095 olive: 'rgb(128, 128, 0)', 4096 olivedrab: 'rgb(107, 142, 35)', 4097 orange: 'rgb(255, 165, 0)', 4098 orangered: 'rgb(255, 69, 0)', 4099 orchid: 'rgb(218, 112, 214)', 4100 palegoldenrod: 'rgb(238, 232, 170)', 4101 palegreen: 'rgb(152, 251, 152)', 4102 paleturquoise: 'rgb(175, 238, 238)', 4103 palevioletred: 'rgb(219, 112, 147)', 4104 papayawhip: 'rgb(255, 239, 213)', 4105 peachpuff: 'rgb(255, 218, 185)', 4106 peru: 'rgb(205, 133, 63)', 4107 pink: 'rgb(255, 192, 203)', 4108 plum: 'rgb(221, 160, 221)', 4109 powderblue: 'rgb(176, 224, 230)', 4110 purple: 'rgb(128, 0, 128)', 4111 red: 'rgb(255, 0, 0)', 4112 rosybrown: 'rgb(188, 143, 143)', 4113 royalblue: 'rgb( 65, 105, 225)', 4114 saddlebrown: 'rgb(139, 69, 19)', 4115 salmon: 'rgb(250, 128, 114)', 4116 sandybrown: 'rgb(244, 164, 96)', 4117 seagreen: 'rgb( 46, 139, 87)', 4118 seashell: 'rgb(255, 245, 238)', 4119 sienna: 'rgb(160, 82, 45)', 4120 silver: 'rgb(192, 192, 192)', 4121 skyblue: 'rgb(135, 206, 235)', 4122 slateblue: 'rgb(106, 90, 205)', 4123 slategray: 'rgb(112, 128, 144)', 4124 slategrey: 'rgb(112, 128, 144)', 4125 snow: 'rgb(255, 250, 250)', 4126 springgreen: 'rgb( 0, 255, 127)', 4127 steelblue: 'rgb( 70, 130, 180)', 4128 tan: 'rgb(210, 180, 140)', 4129 teal: 'rgb( 0, 128, 128)', 4130 thistle: 'rgb(216, 191, 216)', 4131 tomato: 'rgb(255, 99, 71)', 4132 turquoise: 'rgb( 64, 224, 208)', 4133 violet: 'rgb(238, 130, 238)', 4134 wheat: 'rgb(245, 222, 179)', 4135 white: 'rgb(255, 255, 255)', 4136 whitesmoke: 'rgb(245, 245, 245)', 4137 yellow: 'rgb(255, 255, 0)', 4138 yellowgreen: 'rgb(154, 205, 50)' 4139 }; 4140 4141 4142 4143 4144 // class: $.jqplot.AxisLabelRenderer 4145 // Renderer to place labels on the axes. 4146 $.jqplot.AxisLabelRenderer = function(options) { 4147 // Group: Properties 4148 $.jqplot.ElemContainer.call(this); 4149 // name of the axis associated with this tick 4150 this.axis; 4151 // prop: show 4152 // whether or not to show the tick (mark and label). 4153 this.show = true; 4154 // prop: label 4155 // The text or html for the label. 4156 this.label = ''; 4157 this.fontFamily = null; 4158 this.fontSize = null; 4159 this.textColor = null; 4160 this._elem; 4161 // prop: escapeHTML 4162 // true to escape HTML entities in the label. 4163 this.escapeHTML = false; 4164 4165 $.extend(true, this, options); 4166 }; 4167 4168 $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer(); 4169 $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer; 4170 4171 $.jqplot.AxisLabelRenderer.prototype.init = function(options) { 4172 $.extend(true, this, options); 4173 }; 4174 4175 $.jqplot.AxisLabelRenderer.prototype.draw = function(ctx, plot) { 4176 // Memory Leaks patch 4177 if (this._elem) { 4178 this._elem.emptyForce(); 4179 this._elem = null; 4180 } 4181 4182 this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>'); 4183 4184 if (Number(this.label)) { 4185 this._elem.css('white-space', 'nowrap'); 4186 } 4187 4188 if (!this.escapeHTML) { 4189 this._elem.html(this.label); 4190 } 4191 else { 4192 this._elem.text(this.label); 4193 } 4194 if (this.fontFamily) { 4195 this._elem.css('font-family', this.fontFamily); 4196 } 4197 if (this.fontSize) { 4198 this._elem.css('font-size', this.fontSize); 4199 } 4200 if (this.textColor) { 4201 this._elem.css('color', this.textColor); 4202 } 4203 4204 return this._elem; 4205 }; 4206 4207 $.jqplot.AxisLabelRenderer.prototype.pack = function() { 4208 }; 4209 4210 // class: $.jqplot.AxisTickRenderer 4211 // A "tick" object showing the value of a tick/gridline on the plot. 4212 $.jqplot.AxisTickRenderer = function(options) { 4213 // Group: Properties 4214 $.jqplot.ElemContainer.call(this); 4215 // prop: mark 4216 // tick mark on the axis. One of 'inside', 'outside', 'cross', '' or null. 4217 this.mark = 'outside'; 4218 // name of the axis associated with this tick 4219 this.axis; 4220 // prop: showMark 4221 // whether or not to show the mark on the axis. 4222 this.showMark = true; 4223 // prop: showGridline 4224 // whether or not to draw the gridline on the grid at this tick. 4225 this.showGridline = true; 4226 // prop: isMinorTick 4227 // if this is a minor tick. 4228 this.isMinorTick = false; 4229 // prop: size 4230 // Length of the tick beyond the grid in pixels. 4231 // DEPRECATED: This has been superceeded by markSize 4232 this.size = 4; 4233 // prop: markSize 4234 // Length of the tick marks in pixels. For 'cross' style, length 4235 // will be stoked above and below axis, so total length will be twice this. 4236 this.markSize = 6; 4237 // prop: show 4238 // whether or not to show the tick (mark and label).
4239 // Setting this to false requires more testing. It is recommended 4240 // to set showLabel and showMark to false instead. 4241 this.show = true; 4242 // prop: showLabel 4243 // whether or not to show the label. 4244 this.showLabel = true; 4245 this.label = null; 4246 this.value = null; 4247 this._styles = {}; 4248 // prop: formatter 4249 // A class of a formatter for the tick text. sprintf by default. 4250 this.formatter = $.jqplot.DefaultTickFormatter; 4251 // prop: prefix 4252 // String to prepend to the tick label. 4253 // Prefix is prepended to the formatted tick label. 4254 this.prefix = ''; 4255 // prop: suffix 4256 // String to append to the tick label. 4257 // Suffix is appended to the formatted tick label. 4258 this.suffix = ''; 4259 // prop: formatString 4260 // string passed to the formatter. 4261 this.formatString = ''; 4262 // prop: fontFamily 4263 // css spec for the font-family css attribute. 4264 this.fontFamily; 4265 // prop: fontSize 4266 // css spec for the font-size css attribute. 4267 this.fontSize; 4268 // prop: textColor 4269 // css spec for the color attribute. 4270 this.textColor; 4271 // prop: escapeHTML 4272 // true to escape HTML entities in the label. 4273 this.escapeHTML = false; 4274 this._elem; 4275 this._breakTick = false; 4276 4277 $.extend(true, this, options); 4278 }; 4279 4280 $.jqplot.AxisTickRenderer.prototype.init = function(options) { 4281 $.extend(true, this, options); 4282 }; 4283 4284 $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer(); 4285 $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer; 4286 4287 $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) { 4288 this.value = value; 4289 this.axis = axisName; 4290 if (isMinor) { 4291 this.isMinorTick = true; 4292 } 4293 return this; 4294 }; 4295 4296 $.jqplot.AxisTickRenderer.prototype.draw = function() { 4297 if (this.label === null) { 4298 this.label = this.prefix + this.formatter(this.formatString, this.value) + this.suffix; 4299 } 4300 var style = {position: 'absolute'}; 4301 if (Number(this.label)) { 4302 style['whitSpace'] = 'nowrap'; 4303 } 4304 4305 // Memory Leaks patch 4306 if (this._elem) { 4307 this._elem.emptyForce(); 4308 this._elem = null; 4309 } 4310 4311 this._elem = $(document.createElement('div')); 4312 this._elem.addClass("jqplot-"+this.axis+"-tick"); 4313 4314 if (!this.escapeHTML) { 4315 this._elem.html(this.label); 4316 } 4317 else { 4318 this._elem.text(this.label); 4319 } 4320 4321 this._elem.css(style); 4322 4323 for (var s in this._styles) { 4324 this._elem.css(s, this._styles[s]); 4325 } 4326 if (this.fontFamily) { 4327 this._elem.css('font-family', this.fontFamily); 4328 } 4329 if (this.fontSize) { 4330 this._elem.css('font-size', this.fontSize); 4331 } 4332 if (this.textColor) { 4333 this._elem.css('color', this.textColor); 4334 } 4335 if (this._breakTick) { 4336 this._elem.addClass('jqplot-breakTick'); 4337 } 4338 4339 return this._elem; 4340 }; 4341 4342 $.jqplot.DefaultTickFormatter = function (format, val) { 4343 if (typeof val == 'number') { 4344 if (!format) { 4345 format = $.jqplot.config.defaultTickFormatString; 4346 } 4347 return $.jqplot.sprintf(format, val); 4348 } 4349 else { 4350 return String(val); 4351 } 4352 }; 4353 4354 $.jqplot.PercentTickFormatter = function (format, val) { 4355 if (typeof val == 'number') { 4356 val = 100 * val; 4357 if (!format) { 4358 format = $.jqplot.config.defaultTickFormatString; 4359 } 4360 return $.jqplot.sprintf(format, val); 4361 } 4362 else { 4363 return String(val); 4364 } 4365 }; 4366 4367 $.jqplot.AxisTickRenderer.prototype.pack = function() { 4368 }; 4369 4370 // Class: $.jqplot.CanvasGridRenderer 4371 // The default jqPlot grid renderer, creating a grid on a canvas element. 4372 // The renderer has no additional options beyond the <Grid> class. 4373 $.jqplot.CanvasGridRenderer = function(){ 4374 this.shadowRenderer = new $.jqplot.ShadowRenderer(); 4375 }; 4376 4377 // called with context of Grid object 4378 $.jqplot.CanvasGridRenderer.prototype.init = function(options) { 4379 this._ctx; 4380 $.extend(true, this, options); 4381 // set the shadow renderer options 4382 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}; 4383 this.renderer.shadowRenderer.init(sopts); 4384 }; 4385 4386 // called with context of Grid. 4387 $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) { 4388 var elem; 4389 // Memory Leaks patch 4390 if (this._elem) { 4391 if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { 4392 elem = this._elem.get(0); 4393 window.G_vmlCanvasManager.uninitElement(elem); 4394 elem = null; 4395 } 4396 4397 this._elem.emptyForce(); 4398 this._elem = null; 4399 } 4400 4401 elem = plot.canvasManager.getCanvas(); 4402 4403 var w = this._plotDimensions.width; 4404 var h = this._plotDimensions.height; 4405 elem.width = w; 4406 elem.height = h; 4407 this._elem = $(elem); 4408 this._elem.addClass('jqplot-grid-canvas'); 4409 this._elem.css({ position: 'absolute', left: 0, top: 0 }); 4410 4411 elem = plot.canvasManager.initCanvas(elem); 4412 4413 this._top = this._offsets.top; 4414 this._bottom = h - this._offsets.bottom; 4415 this._left = this._offsets.left; 4416 this._right = w - this._offsets.right; 4417 this._width = this._right - this._left; 4418 this._height = this._bottom - this._top; 4419 // avoid memory leak 4420 elem = null; 4421 return this._elem; 4422 }; 4423 4424 $.jqplot.CanvasGridRenderer.prototype.draw = function() { 4425 this._ctx = this._elem.get(0).getContext("2d"); 4426 var ctx = this._ctx; 4427 var axes = this._axes; 4428 // Add the grid onto the grid canvas. This is the bottom most layer. 4429 ctx.save(); 4430 ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height); 4431 ctx.fillStyle = this.backgroundColor || this.background; 4432 ctx.fillRect(this._left, this._top, this._width, this._height); 4433 4434 ctx.save(); 4435 ctx.lineJoin = 'miter'; 4436 ctx.lineCap = 'butt'; 4437 ctx.lineWidth = this.gridLineWidth; 4438 ctx.strokeStyle = this.gridLineColor; 4439 var b, e, s, m; 4440 var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis']; 4441 for (var i=4; i>0; i--) { 4442 var name = ax[i-1]; 4443 var axis = axes[name]; 4444 var ticks = axis._ticks; 4445 var numticks = ticks.length; 4446 if (axis.show) { 4447 if (axis.drawBaseline) { 4448 var bopts = {}; 4449 if (axis.baselineWidth !== null) { 4450 bopts.lineWidth = axis.baselineWidth; 4451 } 4452 if (axis.baselineColor !== null) { 4453 bopts.strokeStyle = axis.baselineColor; 4454 } 4455 switch (name) { 4456 case 'xaxis': 4457 drawLine (this._left, this._bottom, this._right, this._bottom, bopts); 4458 break; 4459 case 'yaxis': 4460 drawLine (this._left, this._bottom, this._left, this._top, bopts); 4461 break; 4462 case 'x2axis': 4463 drawLine (this._left, this._bottom, this._right, this._bottom, bopts); 4464 break; 4465 case 'y2axis': 4466 drawLine (this._right, this._bottom, this._right, this._top, bopts); 4467 break; 4468 } 4469 } 4470 for (var j=numticks; j>0; j--) { 4471 var t = ticks[j-1]; 4472 if (t.show) { 4473 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4474 switch (name) { 4475 case 'xaxis': 4476 // draw the grid line if we should 4477 if (t.showGridline && this.drawGri
4477dlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { 4478 drawLine(pos, this._top, pos, this._bottom); 4479 } 4480 // draw the mark 4481 if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { 4482 s = t.markSize; 4483 m = t.mark; 4484 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4485 switch (m) { 4486 case 'outside': 4487 b = this._bottom; 4488 e = this._bottom+s; 4489 break; 4490 case 'inside': 4491 b = this._bottom-s; 4492 e = this._bottom; 4493 break; 4494 case 'cross': 4495 b = this._bottom-s; 4496 e = this._bottom+s; 4497 break; 4498 default: 4499 b = this._bottom; 4500 e = this._bottom+s; 4501 break; 4502 } 4503 // draw the shadow 4504 if (this.shadow) { 4505 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}); 4506 } 4507 // draw the line 4508 drawLine(pos, b, pos, e); 4509 } 4510 break; 4511 case 'yaxis': 4512 // draw the grid line 4513 if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { 4514 drawLine(this._right, pos, this._left, pos); 4515 } 4516 // draw the mark 4517 if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { 4518 s = t.markSize; 4519 m = t.mark; 4520 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4521 switch (m) { 4522 case 'outside': 4523 b = this._left-s; 4524 e = this._left; 4525 break; 4526 case 'inside': 4527 b = this._left; 4528 e = this._left+s; 4529 break; 4530 case 'cross': 4531 b = this._left-s; 4532 e = this._left+s; 4533 break; 4534 default: 4535 b = this._left-s; 4536 e = this._left; 4537 break; 4538 } 4539 // draw the shadow 4540 if (this.shadow) { 4541 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}); 4542 } 4543 drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); 4544 } 4545 break; 4546 case 'x2axis': 4547 // draw the grid line 4548 if (t.showGridline && this.drawGri
4548dlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { 4549 drawLine(pos, this._bottom, pos, this._top); 4550 } 4551 // draw the mark 4552 if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { 4553 s = t.markSize; 4554 m = t.mark; 4555 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4556 switch (m) { 4557 case 'outside': 4558 b = this._top-s; 4559 e = this._top; 4560 break; 4561 case 'inside': 4562 b = this._top; 4563 e = this._top+s; 4564 break; 4565 case 'cross': 4566 b = this._top-s; 4567 e = this._top+s; 4568 break; 4569 default: 4570 b = this._top-s; 4571 e = this._top; 4572 break; 4573 } 4574 // draw the shadow 4575 if (this.shadow) { 4576 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}); 4577 } 4578 drawLine(pos, b, pos, e); 4579 } 4580 break; 4581 case 'y2axis': 4582 // draw the grid line 4583 if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { 4584 drawLine(this._left, pos, this._right, pos); 4585 } 4586 // draw the mark 4587 if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { 4588 s = t.markSize; 4589 m = t.mark; 4590 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4591 switch (m) { 4592 case 'outside': 4593 b = this._right; 4594 e = this._right+s; 4595 break; 4596 case 'inside': 4597 b = this._right-s; 4598 e = this._right; 4599 break; 4600 case 'cross': 4601 b = this._right-s; 4602 e = this._right+s; 4603 break; 4604 default: 4605 b = this._right; 4606 e = this._right+s; 4607 break; 4608 } 4609 // draw the shadow 4610 if (this.shadow) { 4611 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}); 4612 } 4613 drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); 4614 } 4615 break; 4616 default: 4617 break; 4618 } 4619 } 4620 } 4621 t = null; 4622 } 4623 axis = null; 4624 ticks = null; 4625 } 4626 // Now draw grid lines for additional y axes 4627 ////// 4628 // TO DO: handle yMidAxis 4629 ////// 4630 ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; 4631 for (var i=7; i>0; i--) { 4632 var axis = axes[ax[i-1]]; 4633 var ticks = axis._ticks; 4634 if (axis.show) { 4635 var tn = ticks[axis.numberTicks-1]; 4636 var t0 = ticks[0]; 4637 var left = axis.getLeft(); 4638 var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]]; 4639 // draw the shadow 4640 if (this.shadow) { 4641 this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false}); 4642 } 4643 // draw the line 4644 drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth}); 4645 // draw the tick marks 4646 for (var j=ticks.length; j>0; j--) { 4647 var t = ticks[j-1]; 4648 s = t.markSize; 4649 m = t.mark; 4650 var pos = Math.round(axis.u2p(t.value)) + 0.5; 4651 if (t.showMark && t.mark) { 4652 switch (m) { 4653 case 'outside': 4654 b = left; 4655 e = left+s; 4656 break; 4657 case 'inside': 4658 b = left-s; 4659 e = left; 4660 break; 4661 case 'cross': 4662 b = left-s; 4663 e = left+s; 4664 break; 4665 default: 4666 b = left; 4667 e = left+s; 4668 break; 4669 } 4670 points = [[b,pos], [e,pos]]; 4671 // draw the shadow 4672 if (this.shadow) { 4673 this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); 4674 } 4675 // draw the line 4676 drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); 4677 } 4678 t = null; 4679 } 4680 t0 = null; 4681 } 4682 axis = null; 4683 ticks = null; 4684 } 4685
4686 ctx.restore(); 4687 4688 function drawLine(bx, by, ex, ey, opts) { 4689 ctx.save(); 4690 opts = opts || {}; 4691 if (opts.lineWidth == null || opts.lineWidth != 0){ 4692 $.extend(true, ctx, opts); 4693 ctx.beginPath(); 4694 ctx.moveTo(bx, by); 4695 ctx.lineTo(ex, ey); 4696 ctx.stroke(); 4697 ctx.restore(); 4698 } 4699 } 4700 4701 if (this.shadow) { 4702 var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]]; 4703 this.renderer.shadowRenderer.draw(ctx, points); 4704 } 4705 // Now draw border around grid. Use axis border definitions. start at 4706 // upper left and go clockwise. 4707 if (this.borderWidth != 0 && this.drawBorder) { 4708 drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); 4709 drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth}); 4710 drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); 4711 drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); 4712 } 4713 // ctx.lineWidth = this.borderWidth; 4714 // ctx.strokeStyle = this.borderColor; 4715 // ctx.strokeRect(this._left, this._top, this._width, this._height); 4716 4717 ctx.restore(); 4718 ctx = null; 4719 axes = null; 4720 }; 4721 4722 // Class: $.jqplot.DivTitleRenderer 4723 // The default title renderer for jqPlot. This class has no options beyond the <Title> class. 4724 $.jqplot.DivTitleRenderer = function() { 4725 }; 4726 4727 $.jqplot.DivTitleRenderer.prototype.init = function(options) { 4728 $.extend(true, this, options); 4729 }; 4730 4731 $.jqplot.DivTitleRenderer.prototype.draw = function() { 4732 // Memory Leaks patch 4733 if (this._elem) { 4734 this._elem.emptyForce(); 4735 this._elem = null; 4736 } 4737 4738 var r = this.renderer; 4739 var elem = document.createElement('div'); 4740 this._elem = $(elem); 4741 this._elem.addClass('jqplot-title'); 4742 4743 if (!this.text) { 4744 this.show = false; 4745 this._elem.height(0); 4746 this._elem.width(0); 4747 } 4748 else if (this.text) { 4749 var color; 4750 if (this.color) { 4751 color = this.color; 4752 } 4753 else if (this.textColor) { 4754 color = this.textColor; 4755 } 4756 4757 // don't trust that a stylesheet is present, set the position. 4758 var styles = {position:'absolute', top:'0px', left:'0px'}; 4759 4760 if (this._plotWidth) { 4761 styles['width'] = this._plotWidth+'px'; 4762 } 4763 if (this.fontSize) { 4764 styles['fontSize'] = this.fontSize; 4765 } 4766 if (typeof this.textAlign === 'string') { 4767 styles['textAlign'] = this.textAlign; 4768 } 4769 else { 4770 styles['textAlign'] = 'center'; 4771 } 4772 if (color) { 4773 styles['color'] = color; 4774 } 4775 if (this.paddingBottom) { 4776 styles['paddingBottom'] = this.paddingBottom; 4777 } 4778 if (this.fontFamily) { 4779 styles['fontFamily'] = this.fontFamily; 4780 } 4781 4782 this._elem.css(styles); 4783 if (this.escapeHtml) { 4784 this._elem.text(this.text); 4785 } 4786 else { 4787 this._elem.html(this.text); 4788 } 4789 4790 4791 // styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : ''; 4792 // styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; 4793 // styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;'; 4794 // styletext += (color) ? 'color:'+color+';' : ''; 4795 // styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : ''; 4796 // this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>'); 4797 // if (this.fontFamily) { 4798 // this._elem.css('font-family', this.fontFamily); 4799 // } 4800 } 4801 4802 elem = null; 4803 4804 return this._elem; 4805 }; 4806 4807 $.jqplot.DivTitleRenderer.prototype.pack = function() { 4808 // nothing to do here 4809 }; 4810 4811 4812 var dotlen = 0.1; 4813 4814 $.jqplot.LinePattern = function (ctx, pattern) { 4815 4816 var defaultLinePatterns = { 4817 dotted: [ dotlen, $.jqplot.config.dotGapLength ], 4818 dashed: [ $.jqplot.config.dashLength, $.jqplot.config.gapLength ], 4819 solid: null 4820 }; 4821 4822 if (typeof pattern === 'string') {
4823 if (pattern[0] === '.' || pattern[0] === '-') { 4824 var s = pattern; 4825 pattern = []; 4826 for (var i=0, imax=s.length; i<imax; i++) { 4827 if (s[i] === '.') { 4828 pattern.push( dotlen ); 4829 } 4830 else if (s[i] === '-') { 4831 pattern.push( $.jqplot.config.dashLength ); 4832 } 4833 else { 4834 continue; 4835 } 4836 pattern.push( $.jqplot.config.gapLength ); 4837 } 4838 } 4839 else { 4840 pattern = defaultLinePatterns[pattern]; 4841 } 4842 } 4843 4844 if (!(pattern && pattern.length)) { 4845 return ctx; 4846 } 4847 4848 var patternIndex = 0; 4849 var patternDistance = pattern[0]; 4850 var px = 0; 4851 var py = 0; 4852 var pathx0 = 0; 4853 var pathy0 = 0; 4854 4855 var moveTo = function (x, y) { 4856 ctx.moveTo( x, y ); 4857 px = x; 4858 py = y; 4859 pathx0 = x; 4860 pathy0 = y; 4861 }; 4862 4863 var lineTo = function (x, y) { 4864 var scale = ctx.lineWidth; 4865 var dx = x - px; 4866 var dy = y - py; 4867 var dist = Math.sqrt(dx*dx+dy*dy); 4868 if ((dist > 0) && (scale > 0)) { 4869 dx /= dist; 4870 dy /= dist; 4871 while (true) { 4872 var dp = scale * patternDistance; 4873 if (dp < dist) { 4874 px += dp * dx; 4875 py += dp * dy; 4876 if ((patternIndex & 1) == 0) { 4877 ctx.lineTo( px, py ); 4878 } 4879 else { 4880 ctx.moveTo( px, py ); 4881 } 4882 dist -= dp; 4883 patternIndex++; 4884 if (patternIndex >= pattern.length) { 4885 patternIndex = 0; 4886 } 4887 patternDistance = pattern[patternIndex]; 4888 } 4889 else { 4890 px = x; 4891 py = y; 4892 if ((patternIndex & 1) == 0) { 4893 ctx.lineTo( px, py ); 4894 } 4895 else { 4896 ctx.moveTo( px, py ); 4897 } 4898 patternDistance -= dist / scale; 4899 break; 4900 } 4901 } 4902 } 4903 }; 4904 4905 var beginPath = function () { 4906 ctx.beginPath(); 4907 }; 4908 4909 var closePath = function () { 4910 lineTo( pathx0, pathy0 ); 4911 }; 4912 4913 return { 4914 moveTo: moveTo, 4915 lineTo: lineTo, 4916 beginPath: beginPath, 4917 closePath: closePath 4918 }; 4919 }; 4920 4921 // Class: $.jqplot.LineRenderer 4922 // The default line renderer for jqPlot, this class has no options beyond the <Series> class. 4923 // Draws series as a line. 4924 $.jqplot.LineRenderer = function(){ 4925 this.shapeRenderer = new $.jqplot.ShapeRenderer(); 4926 this.shadowRenderer = new $.jqplot.ShadowRenderer(); 4927 }; 4928 4929 // called with scope of series. 4930 $.jqplot.LineRenderer.prototype.init = function(options, plot) { 4931 // Group: Properties 4932 // 4933 options = options || {}; 4934 this._type='line'; 4935 this.renderer.animation = { 4936 show: false, 4937 direction: 'left', 4938 speed: 2500, 4939 _supported: true 4940 }; 4941 // prop: smooth 4942 // True to draw a smoothed (interpolated) line through the data points 4943 // with automatically computed number of smoothing points. 4944 // Set to an integer number > 2 to specify number of smoothing points 4945 // to use between each data point. 4946 this.renderer.smooth = false; // true or a number > 2 for smoothing. 4947 this.renderer.tension = null; // null to auto compute or a number typically > 6. Fewer points requires higher tension. 4948 // prop: constrainSmoothing 4949 // True to use a more accurate smoothing algorithm that will 4950 // not overshoot any data points. False to allow overshoot but 4951 // produce a smoother looking line. 4952 this.renderer.constrainSmoothing = true; 4953 // this is smoothed data in grid coordinates, like gridData 4954 this.renderer._smoothedData = []; 4955 // this is smoothed data in plot units (plot coordinates), like plotData. 4956 this.renderer._smoothedPlotData = []; 4957 this.renderer._hiBandGridData = []; 4958 this.renderer._lowBandGridData = []; 4959 this.renderer._hiBandSmoothedData = []; 4960 this.renderer._lowBandSmoothedData = []; 4961 4962 // prop: bandData 4963 // Data used to draw error bands or confidence intervals above/below a line. 4964 // 4965 // bandData can be input in 3 forms. jqPlot will figure out which is the 4966 // low band line and which is the high band line for all forms: 4967 // 4968 // A 2 dimensional array like [[yl1, yl2, ...], [yu1, yu2, ...]] where 4969 // [yl1, yl2, ...] are y values of the lower line and 4970 // [yu1, yu2, ...] are y values of the upper line. 4971 // In this case there must be the same number of y data points as data points 4972 // in the series and the bands will inherit the x values of the series. 4973 // 4974 // A 2 dimensional array like [[[xl1, yl1], [xl2, yl2], ...], [[xh1, yh1], [xh2, yh2], ...]] 4975 // where [xl1, yl1] are x,y data points for the lower line and 4976 // [xh1, yh1] are x,y data points for the high line. 4977 // x values do not have to correspond to the x values of the series and can 4978 // be of any arbitrary length. 4979 // 4980 // Can be of form [[yl1, yu1], [yl2, yu2], [yl3, yu3], ...] where 4981 // there must be 3 or more arrays and there must be the same number of arrays 4982 // as there are data points in the series. In this case, 4983 // [yl1, yu1] specifies the lower and upper y values for the 1st 4984 // data point and so on. The bands will inherit the x 4985 // values from the series. 4986 this.renderer.bandData = []; 4987 4988 // Group: bands 4989 // Banding around line, e.g error bands or confidence intervals. 4990 this.renderer.bands = { 4991 // prop: show 4992 // true to show the bands. If bandData or interval is 4993 // supplied, show will be set to true by default. 4994 show: false, 4995 hiData: [], 4996 lowData: [], 4997 // prop: color 4998 // color of lines at top and bottom of bands [default: series color]. 4999 color: this.color, 5000 // prop: showLines 5001 // True to show lines at top and bottom of bands [default: false]. 5002 showLines: false, 5003 // prop: fill 5004 // True to fill area between bands [default: true]. 5005 fill: true, 5006 // prop: fillColor 5007 // css color spec for filled area. [default: series color]. 5008 fillColor: null, 5009 _min: null, 5010 _max: null, 5011 // prop: interval 5012 // User specified interval above and below line for bands [default: '3%'']. 5013 // Can be a value like 3 or a string like '3%' 5014 // or an upper/lower array like [1, -2] or ['2%', '-1.5%'] 5015 interval: '3%' 5016 }; 5017 5018 5019 var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor}; 5020 5021 delete (options.highlightMouseOver); 5022 delete (options.highlightMouseDown);
5023 delete (options.highlightColor); 5024 5025 $.extend(true, this.renderer, options); 5026 5027 this.renderer.options = options; 5028 5029 // if we are given some band data, and bands aren't explicity set to false in options, turn them on. 5030 if (this.renderer.bandData.length > 1 && (!options.bands || options.bands.show == null)) { 5031 this.renderer.bands.show = true; 5032 } 5033 5034 // if we are given an interval, and bands aren't explicity set to false in options, turn them on. 5035 else if (options.bands && options.bands.show == null && options.bands.interval != null) { 5036 this.renderer.bands.show = true; 5037 } 5038 5039 // if plot is filled, turn off bands. 5040 if (this.fill) { 5041 this.renderer.bands.show = false; 5042 } 5043 5044 if (this.renderer.bands.show) { 5045 this.renderer.initBands.call(this, this.renderer.options, plot); 5046 } 5047 5048 5049 // smoothing is not compatible with stacked lines, disable 5050 if (this._stack) { 5051 this.renderer.smooth = false; 5052 } 5053 5054 // set the shape renderer options 5055 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}; 5056 this.renderer.shapeRenderer.init(opts); 5057 5058 var shadow_offset = options.shadowOffset; 5059 // set the shadow renderer options 5060 if (shadow_offset == null) { 5061 // scale the shadowOffset to the width of the line. 5062 if (this.lineWidth > 2.5) { 5063 shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6); 5064 // var shadow_offset = this.shadowOffset; 5065 } 5066 // for skinny lines, don't make such a big shadow. 5067 else { 5068 shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163; 5069 } 5070 } 5071 5072 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}; 5073 this.renderer.shadowRenderer.init(sopts); 5074 this._areaPoints = []; 5075 this._boundingBox = [[],[]]; 5076 5077 if (!this.isTrendline && this.fill || this.renderer.bands.show) { 5078 // Group: Properties 5079 // 5080 // prop: highlightMouseOver 5081 // True to highlight area on a filled plot when moused over. 5082 // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot. 5083 this.highlightMouseOver = true; 5084 // prop: highlightMouseDown 5085 // True to highlight when a mouse button is pressed over an area on a filled plot. 5086 // This will be disabled if highlightMouseOver is true. 5087 this.highlightMouseDown = false; 5088 // prop: highlightColor 5089 // color to use when highlighting an area on a filled plot. 5090 this.highlightColor = null; 5091 // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver 5092 if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) { 5093 lopts.highlightMouseOver = false; 5094 } 5095 5096 $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor}); 5097 5098 if (!this.highlightColor) { 5099 var fc = (this.renderer.bands.show) ? this.renderer.bands.fillColor : this.fillColor; 5100 this.highlightColor = $.jqplot.computeHighlightColors(fc); 5101 } 5102 // turn off (disable) the highlighter plugin 5103 if (this.highlighter) { 5104 this.highlighter.show = false; 5105 } 5106 } 5107 5108 if (!this.isTrendline && plot) { 5109 plot.plugins.lineRenderer = {}; 5110 plot.postInitHooks.addOnce(postInit); 5111 plot.postDrawHooks.addOnce(postPlotDraw);
5112 plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); 5113 plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); 5114 plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); 5115 plot.eventListenerHooks.addOnce('jqplotClick', handleClick); 5116 plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); 5117 } 5118 5119 }; 5120 5121 $.jqplot.LineRenderer.prototype.initBands = function(options, plot) { 5122 // use bandData if no data specified in bands option 5123 //var bd = this.renderer.bandData; 5124 var bd = options.bandData || []; 5125 var bands = this.renderer.bands; 5126 bands.hiData = []; 5127 bands.lowData = []; 5128 var data = this.data; 5129 bands._max = null; 5130 bands._min = null; 5131 // If 2 arrays, and each array greater than 2 elements, assume it is hi and low data bands of y values. 5132 if (bd.length == 2) { 5133 // Do we have an array of x,y values? 5134 // like [[[1,1], [2,4], [3,3]], [[1,3], [2,6], [3,5]]] 5135 if ($.isArray(bd[0][0])) { 5136 // since an arbitrary array of points, spin through all of them to determine max and min lines. 5137 5138 var p; 5139 var bdminidx = 0, bdmaxidx = 0; 5140 for (var i = 0, l = bd[0].length; i<l; i++) { 5141 p = bd[0][i]; 5142 if ((p[1] != null && p[1] > bands._max) || bands._max == null) { 5143 bands._max = p[1]; 5144 } 5145 if ((p[1] != null && p[1] < bands._min) || bands._min == null) { 5146 bands._min = p[1]; 5147 } 5148 } 5149 for (var i = 0, l = bd[1].length; i<l; i++) { 5150 p = bd[1][i]; 5151 if ((p[1] != null && p[1] > bands._max) || bands._max == null) { 5152 bands._max = p[1]; 5153 bdmaxidx = 1; 5154 } 5155 if ((p[1] != null && p[1] < bands._min) || bands._min == null) { 5156 bands._min = p[1]; 5157 bdminidx = 1; 5158 } 5159 } 5160 5161 if (bdmaxidx === bdminidx) { 5162 bands.show = false; 5163 } 5164 5165 bands.hiData = bd[bdmaxidx]; 5166 bands.lowData = bd[bdminidx]; 5167 } 5168 // else data is arrays of y values 5169 // like [[1,4,3], [3,6,5]] 5170 // must have same number of band data points as points in series 5171 else if (bd[0].length === data.length && bd[1].length === data.length) { 5172 var hi = (bd[0][0] > bd[1][0]) ? 0 : 1; 5173 var low = (hi) ? 0 : 1; 5174 for (var i=0, l=data.length; i < l; i++) { 5175 bands.hiData.push([data[i][0], bd[hi][i]]); 5176 bands.lowData.push([data[i][0], bd[low][i]]); 5177 } 5178 } 5179 5180 // we don't have proper data array, don't show bands. 5181 else { 5182 bands.show = false; 5183 } 5184 } 5185 5186 // if more than 2 arrays, have arrays of [ylow, yhi] values. 5187 // note, can't distinguish case of [[ylow, yhi], [ylow, yhi]] from [[ylow, ylow], [yhi, yhi]] 5188 // this is assumed to be of the latter form. 5189 else if (bd.length > 2 && !$.isArray(bd[0][0])) { 5190 var hi = (bd[0][0] > bd[0][1]) ? 0 : 1; 5191 var low = (hi) ? 0 : 1; 5192 for (var i=0, l=bd.length; i<l; i++) { 5193 bands.hiData.push([data[i][0], bd[i][hi]]); 5194 bands.lowData.push([data[i][0], bd[i][low]]); 5195 } 5196 } 5197 5198 // don't have proper data, auto calculate 5199 else { 5200 var intrv = bands.interval; 5201 var a = null; 5202 var b = null; 5203 var afunc = null; 5204 var bfunc = null; 5205 5206 if ($.isArray(intrv)) { 5207 a = intrv[0]; 5208 b = intrv[1]; 5209 } 5210 else { 5211 a = intrv; 5212 } 5213 5214 if (isNaN(a)) { 5215 // we have a string 5216 if (a.charAt(a.length - 1) === '%') { 5217 afunc = 'multiply'; 5218 a = parseFloat(a)/100 + 1; 5219 } 5220 } 5221 5222 else { 5223 a = parseFloat(a); 5224 afunc = 'add'; 5225 } 5226 5227 if (b !== null && isNaN(b)) { 5228 // we have a string 5229 if (b.charAt(b.length - 1) === '%') { 5230 bfunc = 'multiply'; 5231 b = parseFloat(b)/100 + 1; 5232 } 5233 } 5234 5235 else if (b !== null) { 5236 b = parseFloat(b); 5237 bfunc = 'add'; 5238 } 5239 5240 if (a !== null) { 5241 if (b === null) { 5242 b = -a; 5243 bfunc = afunc; 5244 if (bfunc === 'multiply') { 5245 b += 2; 5246 } 5247 } 5248 5249 // make sure a always applies to hi band. 5250 if (a < b) { 5251 var temp = a; 5252 a = b; 5253 b = temp; 5254 temp = afunc; 5255 afunc = bfunc; 5256 bfunc = temp; 5257 } 5258 5259 for (var i=0, l = data.length; i < l; i++) { 5260 switch (afunc) { 5261 case 'add': 5262 bands.hiData.push([data[i][0], data[i][1] + a]); 5263 break; 5264 case 'multiply': 5265 bands.hiData.push([data[i][0], data[i][1] * a]); 5266 break; 5267 } 5268 switch (bfunc) { 5269 case 'add': 5270 bands.lowData.push([data[i][0], data[i][1] + b]); 5271 break; 5272 case 'multiply': 5273 bands.lowData.push([data[i][0], data[i][1] * b]); 5274 break; 5275 } 5276 } 5277 } 5278 5279 else { 5280 bands.show = false; 5281 } 5282 } 5283 5284 var hd = bands.hiData; 5285 var ld = bands.lowData; 5286 for (var i = 0, l = hd.length; i<l; i++) { 5287 if ((hd[i][1] != null && hd[i][1] > bands._max) || bands._max == null) { 5288 bands._max = hd[i][1]; 5289 } 5290 } 5291 for (var i = 0, l = ld.length; i<l; i++) { 5292 if ((ld[i][1] != null && ld[i][1] < bands._min) || bands._min == null) { 5293 bands._min = ld[i][1]; 5294 } 5295 } 5296 5297 // one last check for proper data 5298 // these don't apply any more since allowing arbitrary x,y values 5299 // if (bands.hiData.length != bands.lowData.length) { 5300 // bands.show = false; 5301 // } 5302 5303 // if (bands.hiData.length != this.data.length) { 5304 // bands.show = false; 5305 // } 5306 5307 if (bands.fillColor === null) { 5308 var c = $.jqplot.getColorComponents(bands.color); 5309 // now adjust alpha to differentiate fill 5310 c[3] = c[3] * 0.5; 5311 bands.fillColor = 'rgba(' + c[0] +', '+ c[1] +', '+ c[2] +', '+ c[3] + ')'; 5312 } 5313 }; 5314 5315 function getSteps (d, f) { 5316 return (3.4182054+f) * Math.pow(d, -0.3534992); 5317 } 5318 5319 function computeSteps (d1, d2) { 5320 var s = Math.sqrt(Math.pow((d2[0]- d1[0]), 2) + Math.pow ((d2[1] - d1[1]), 2));
5321 return 5.7648 * Math.log(s) + 7.4456; 5322 } 5323 5324 function tanh (x) { 5325 var a = (Math.exp(2*x) - 1) / (Math.exp(2*x) + 1); 5326 return a; 5327 } 5328 5329 ////////// 5330 // computeConstrainedSmoothedData 5331 // An implementation of the constrained cubic spline interpolation 5332 // method as presented in: 5333 // 5334 // Kruger, CJC, Constrained Cubic Spine Interpolation for Chemical Engineering Applications 5335 // http://www.korf.co.uk/spline.pdf 5336 // 5337 // The implementation below borrows heavily from the sample Visual Basic 5338 // implementation by CJC Kruger found in http://www.korf.co.uk/spline.xls 5339 // 5340 ///////// 5341 5342 // called with scope of series 5343 function computeConstrainedSmoothedData (gd) { 5344 var smooth = this.renderer.smooth; 5345 var dim = this.canvas.getWidth(); 5346 var xp = this._xaxis.series_p2u; 5347 var yp = this._yaxis.series_p2u; 5348 var steps =null; 5349 var _steps = null; 5350 var dist = gd.length/dim; 5351 var _smoothedData = []; 5352 var _smoothedPlotData = []; 5353 5354 if (!isNaN(parseFloat(smooth))) { 5355 steps = parseFloat(smooth); 5356 } 5357 else { 5358 steps = getSteps(dist, 0.5); 5359 } 5360 5361 var yy = []; 5362 var xx = []; 5363 5364 for (var i=0, l = gd.length; i<l; i++) { 5365 yy.push(gd[i][1]); 5366 xx.push(gd[i][0]); 5367 } 5368 5369 function dxx(x1, x0) { 5370 if (x1 - x0 == 0) { 5371 return Math.pow(10,10); 5372 } 5373 else { 5374 return x1 - x0; 5375 } 5376 } 5377 5378 var A, B, C, D; 5379 // loop through each line segment. Have # points - 1 line segments. Nmber segments starting at 1. 5380 var nmax = gd.length - 1; 5381 for (var num = 1, gdl = gd.length; num<gdl; num++) { 5382 var gxx = []; 5383 var ggxx = []; 5384 // point at each end of segment. 5385 for (var j = 0; j < 2; j++) { 5386 var i = num - 1 + j; // point number, 0 to # points. 5387 5388 if (i == 0 || i == nmax) { 5389 gxx[j] = Math.pow(10, 10); 5390 } 5391 else if (yy[i+1] - yy[i] == 0 || yy[i] - yy[i-1] == 0) { 5392 gxx[j] = 0; 5393 } 5394 else if (((xx[i+1] - xx[i]) / (yy[i+1] - yy[i]) + (xx[i] - xx[i-1]) / (yy[i] - yy[i-1])) == 0 ) { 5395 gxx[j] = 0; 5396 } 5397 else if ( (yy[i+1] - yy[i]) * (yy[i] - yy[i-1]) < 0 ) { 5398 gxx[j] = 0; 5399 } 5400 5401 else { 5402 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])); 5403 } 5404 } 5405 5406 // Reset first derivative (slope) at first and last point 5407 if (num == 1) { 5408 // First point has 0 2nd derivative 5409 gxx[0] = 3 / 2 * (yy[1] - yy[0]) / dxx(xx[1], xx[0]) - gxx[1] / 2; 5410 } 5411 else if (num == nmax) { 5412 // Last point has 0 2nd derivative 5413 gxx[1] = 3 / 2 * (yy[nmax] - yy[nmax - 1]) / dxx(xx[nmax], xx[nmax - 1]) - gxx[0] / 2; 5414 } 5415 5416 // Calc second derivative at points 5417 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); 5418 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); 5419 5420 // Calc constants for cubic interpolation 5421 D = 1 / 6 * (ggxx[1] - ggxx[0]) / dxx(xx[num], xx[num - 1]); 5422 C = 1 / 2 * (xx[num] * ggxx[0] - xx[num - 1] * ggxx[1]) / dxx(xx[num], xx[num - 1]); 5423 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]); 5424 A = yy[num - 1] - B * xx[num - 1] - C * Math.pow(xx[num - 1], 2) - D * Math.pow(xx[num - 1], 3); 5425 5426 var increment = (xx[num] - xx[num - 1]) / steps; 5427 var temp, tempx; 5428 5429 for (var j = 0, l = steps; j < l; j++) { 5430 temp = []; 5431 tempx = xx[num - 1] + j * increment; 5432 temp.push(tempx); 5433 temp.push(A + B * tempx + C * Math.pow(tempx, 2) + D * Math.pow(tempx, 3)); 5434 _smoothedData.push(temp); 5435 _smoothedPlotData.push([xp(temp[0]), yp(temp[1])]); 5436 } 5437 } 5438 5439 _smoothedData.push(gd[i]); 5440 _smoothedPlotData.push([xp(gd[i][0]), yp(gd[i][1])]); 5441 5442 return [_smoothedData, _smoothedPlotData]; 5443 } 5444
5445 /////// 5446 // computeHermiteSmoothedData 5447 // A hermite spline smoothing of the plot data. 5448 // This implementation is derived from the one posted 5449 // by krypin on the jqplot-users mailing list: 5450 // 5451 // http://groups.google.com/group/jqplot-users/browse_thread/thread/748be6a445723cea?pli=1 5452 // 5453 // with a blog post: 5454 // 5455 // http://blog.statscollector.com/a-plugin-renderer-for-jqplot-to-draw-a-hermite-spline/ 5456 // 5457 // and download of the original plugin: 5458 // 5459 // http://blog.statscollector.com/wp-content/uploads/2010/02/jqplot.hermiteSplineRenderer.js 5460 ////////// 5461 5462 // called with scope of series 5463 function computeHermiteSmoothedData (gd) { 5464 var smooth = this.renderer.smooth; 5465 var tension = this.renderer.tension; 5466 var dim = this.canvas.getWidth(); 5467 var xp = this._xaxis.series_p2u; 5468 var yp = this._yaxis.series_p2u; 5469 var steps =null; 5470 var _steps = null; 5471 var a = null; 5472 var a1 = null; 5473 var a2 = null; 5474 var slope = null; 5475 var slope2 = null; 5476 var temp = null; 5477 var t, s, h1, h2, h3, h4; 5478 var TiX, TiY, Ti1X, Ti1Y; 5479 var pX, pY, p; 5480 var sd = []; 5481 var spd = []; 5482 var dist = gd.length/dim; 5483 var min, max, stretch, scale, shift; 5484 var _smoothedData = []; 5485 var _smoothedPlotData = []; 5486 if (!isNaN(parseFloat(smooth))) { 5487 steps = parseFloat(smooth); 5488 } 5489 else { 5490 steps = getSteps(dist, 0.5); 5491 } 5492 if (!isNaN(parseFloat(tension))) { 5493 tension = parseFloat(tension); 5494 } 5495 5496 for (var i=0, l = gd.length-1; i < l; i++) { 5497 5498 if (tension === null) { 5499 slope = Math.abs((gd[i+1][1] - gd[i][1]) / (gd[i+1][0] - gd[i][0])); 5500 5501 min = 0.3; 5502 max = 0.6; 5503 stretch = (max - min)/2.0; 5504 scale = 2.5; 5505 shift = -1.4; 5506 5507 temp = slope/scale + shift; 5508 5509 a1 = stretch * tanh(temp) - stretch * tanh(shift) + min; 5510 5511 // if have both left and right line segments, will use minimum tension. 5512 if (i > 0) { 5513 slope2 = Math.abs((gd[i][1] - gd[i-1][1]) / (gd[i][0] - gd[i-1][0])); 5514 } 5515 temp = slope2/scale + shift; 5516 5517 a2 = stretch * tanh(temp) - stretch * tanh(shift) + min; 5518 5519 a = (a1 + a2)/2.0; 5520 5521 } 5522 else { 5523 a = tension; 5524 } 5525 for (t=0; t < steps; t++) { 5526 s = t / steps; 5527 h1 = (1 + 2*s)*Math.pow((1-s),2); 5528 h2 = s*Math.pow((1-s),2); 5529 h3 = Math.pow(s,2)*(3-2*s); 5530 h4 = Math.pow(s,2)*(s-1); 5531 5532 if (gd[i-1]) { 5533 TiX = a * (gd[i+1][0] - gd[i-1][0]); 5534 TiY = a * (gd[i+1][1] - gd[i-1][1]); 5535 } else { 5536 TiX = a * (gd[i+1][0] - gd[i][0]); 5537 TiY = a * (gd[i+1][1] - gd[i][1]); 5538 } 5539 if (gd[i+2]) { 5540 Ti1X = a * (gd[i+2][0] - gd[i][0]); 5541 Ti1Y = a * (gd[i+2][1] - gd[i][1]); 5542 } else { 5543 Ti1X = a * (gd[i+1][0] - gd[i][0]); 5544 Ti1Y = a * (gd[i+1][1] - gd[i][1]); 5545 } 5546 5547 pX = h1*gd[i][0] + h3*gd[i+1][0] + h2*TiX + h4*Ti1X; 5548 pY = h1*gd[i][1] + h3*gd[i+1][1] + h2*TiY + h4*Ti1Y; 5549 p = [pX, pY]; 5550 5551 _smoothedData.push(p); 5552 _smoothedPlotData.push([xp(pX), yp(pY)]); 5553 } 5554 } 5555 _smoothedData.push(gd[l]); 5556 _smoothedPlotData.push([xp(gd[l][0]), yp(gd[l][1])]); 5557 5558 return [_smoothedData, _smoothedPlotData]; 5559 } 5560 5561 // setGridData 5562 // converts the user data values to grid coordinates and stores them 5563 // in the gridData array. 5564 // Called with scope of a series. 5565 $.jqplot.LineRenderer.prototype.setGridData = function(plot) { 5566 // recalculate the grid data 5567 var xp = this._xaxis.series_u2p; 5568 var yp = this._yaxis.series_u2p; 5569 var data = this._plotData; 5570 var pdata = this._prevPlotData; 5571 this.gridData = []; 5572 this._prevGridData = []; 5573 this.renderer._smoothedData = []; 5574 this.renderer._smoothedPlotData = []; 5575 this.renderer._hiBandGridData = []; 5576 this.renderer._lowBandGridData = []; 5577 this.renderer._hiBandSmoothedData = []; 5578 this.renderer._lowBandSmoothedData = []; 5579 var bands = this.renderer.bands; 5580 var hasNull = false; 5581 for (var i=0, l=data.length; i < l; i++) { 5582 // if not a line series or if no nulls in data, push the converted point onto the array. 5583 if (data[i][0] != null && data[i][1] != null) { 5584 this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]); 5585 } 5586 // else if there is a null, preserve it. 5587 else if (data[i][0] == null) { 5588 hasNull = true; 5589 this.gridData.push([null, yp.call(this._yaxis, data[i][1])]); 5590 } 5591 else if (data[i][1] == null) { 5592 hasNull = true; 5593 this.gridData.push([xp.call(this._xaxis, data[i][0]), null]); 5594 } 5595 // if not a line series or if no nulls in data, push the converted point onto the array. 5596 if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) { 5597 this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]); 5598 } 5599 // else if there is a null, preserve it. 5600 else if (pdata[i] != null && pdata[i][0] == null) { 5601 this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]); 5602 } 5603 else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) { 5604 this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]); 5605 } 5606 } 5607 5608 // don't do smoothing or bands on broken lines. 5609 if (hasNull) { 5610 this.renderer.smooth = false; 5611 if (this._type === 'line') { 5612 bands.show = false; 5613 } 5614 } 5615 5616 if (this._type === 'line' && bands.show) { 5617 for (var i=0, l=bands.hiData.length; i<l; i++) { 5618 this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]); 5619 } 5620 for (var i=0, l=bands.lowData.length; i<l; i++) { 5621 this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]); 5622 } 5623 } 5624 5625 // calculate smoothed data if enough points and no nulls 5626 if (this._type === 'line' && this.renderer.smooth && this.gridData.length > 2) { 5627 var ret; 5628 if (this.renderer.constrainSmoothing) { 5629 ret = computeConstrainedSmoothedData.call(this, this.gridData); 5630 this.renderer._smoothedData = ret[0]; 5631 this.renderer._smoothedPlotData = ret[1]; 5632 5633 if (bands.show) { 5634 ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData); 5635 this.renderer._hiBandSmoothedData = ret[0]; 5636 ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData); 5637 this.renderer._lowBandSmoothedData = ret[0]; 5638 } 5639 5640 ret = null; 5641 } 5642 else { 5643 ret = computeHermiteSmoothedData.call(this, this.gridData); 5644 this.renderer._smoothedData = ret[0]; 5645 this.renderer._smoothedPlotData = ret[1]; 5646 5647 if (bands.show) { 5648 ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData); 5649 this.renderer._hiBandSmoothedData = ret[0]; 5650 ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData); 5651 this.renderer._lowBandSmoothedData = ret[0]; 5652 } 5653 5654 ret = null; 5655 } 5656 } 5657 }; 5658 5659 // makeGridData 5660 // converts any arbitrary data values to grid coordinates and 5661 // returns them. This method exists so that plugins can use a series' 5662 // linerenderer to generate grid data points without overwriting the 5663 // grid data associated with that series. 5664 // Called with scope of a series. 5665 $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) { 5666 // recalculate the grid data 5667 var xp = this._xaxis.series_u2p; 5668 var yp = this._yaxis.series_u2p; 5669 var gd = []; 5670 var pgd = []; 5671 this.renderer._smoothedData = []; 5672 this.renderer._smoothedPlotData = []; 5673 this.renderer._hiBandGridData = []; 5674 this.renderer._lowBandGridData = []; 5675 this.renderer._hiBandSmoothedData = []; 5676 this.renderer._lowBandSmoothedData = []; 5677 var bands = this.renderer.bands; 5678 var hasNull = false; 5679 for (var i=0; i<data.length; i++) { 5680 // if not a line series or if no nulls in data, push the converted point onto the array. 5681 if (data[i][0] != null && data[i][1] != null) { 5682 if (this.step && i>0) { 5683 gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i-1][1])]); 5684 } 5685 gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]); 5686 } 5687 // else if there is a null, preserve it. 5688 else if (data[i][0] == null) { 5689 hasNull = true; 5690 gd.push([null, yp.call(this._yaxis, data[i][1])]); 5691 } 5692 else if (data[i][1] == null) { 5693 hasNull = true; 5694 gd.push([xp.call(this._xaxis, data[i][0]), null]); 5695 } 5696 } 5697 5698 // don't do smoothing or bands on broken lines. 5699 if (hasNull) { 5700 this.renderer.smooth = false; 5701 if (this._type === 'line') { 5702 bands.show = false; 5703 } 5704 } 5705 5706 if (this._type === 'line' && bands.show) { 5707 for (var i=0, l=bands.hiData.length; i<l; i++) { 5708 this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]); 5709 } 5710 for (var i=0, l=bands.lowData.length; i<l; i++) { 5711 this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]); 5712 } 5713 } 5714 5715 if (this._type === 'line' && this.renderer.smooth && gd.length > 2) { 5716 var ret; 5717 if (this.renderer.constrainSmoothing) { 5718 ret = computeConstrainedSmoothedData.call(this, gd); 5719 this.renderer._smoothedData = ret[0]; 5720 this.renderer._smoothedPlotData = ret[1]; 5721 5722 if (bands.show) { 5723 ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData); 5724 this.renderer._hiBandSmoothedData = ret[0]; 5725 ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData); 5726 this.renderer._lowBandSmoothedData = ret[0]; 5727 } 5728 5729 ret = null; 5730 } 5731 else { 5732 ret = computeHermiteSmoothedData.call(this, gd); 5733 this.renderer._smoothedData = ret[0]; 5734 this.renderer._smoothedPlotData = ret[1]; 5735 5736 if (bands.show) { 5737 ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData); 5738 this.renderer._hiBandSmoothedData = ret[0]; 5739 ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData); 5740 this.renderer._lowBandSmoothedData = ret[0]; 5741 } 5742 5743 ret = null; 5744 } 5745 } 5746 return gd; 5747 }; 5748 5749 5750 // called within scope of series. 5751 $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options, plot) { 5752 var i; 5753 // get a copy of the options, so we don't modify the original object. 5754 var opts = $.extend(true, {}, options); 5755 var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; 5756 var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; 5757 var fill = (opts.fill != undefined) ? opts.fill : this.fill; 5758 var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke; 5759 var xmin, ymin, xmax, ymax; 5760 ctx.save(); 5761 if (gd.length) { 5762 if (showLine) { 5763 // if we fill, we'll have to add points to close the curve. 5764 if (fill) { 5765 if (this.fillToZero) { 5766 // have to break line up into shapes at axis crossings 5767 var negativeColor = this.negativeColor; 5768 if (! this.useNegativeColors) { 5769 negativeColor = opts.fillStyle; 5770 } 5771 var isnegative = false; 5772 var posfs = opts.fillStyle; 5773 5774 // if stoking line as well as filling, get a copy of line data. 5775 if (fillAndStroke) { 5776 var fasgd = gd.slice(0); 5777 } 5778 // if not stacked, fill down to axis 5779 if (this.index == 0 || !this._stack) { 5780 5781 var tempgd = []; 5782 var pd = (this.renderer.smooth) ? this.renderer._smoothedPlotData : this._plotData; 5783 this._areaPoints = []; 5784 var pyzero = this._yaxis.series_u2p(this.fillToValue); 5785 var pxzero = this._xaxis.series_u2p(this.fillToValue); 5786 5787 opts.closePath = true; 5788
5789 if (this.fillAxis == 'y') { 5790 tempgd.push([gd[0][0], pyzero]); 5791 this._areaPoints.push([gd[0][0], pyzero]); 5792 5793 for (var i=0; i<gd.length-1; i++) { 5794 tempgd.push(gd[i]); 5795 this._areaPoints.push(gd[i]); 5796 // do we have an axis crossing? 5797 if (pd[i][1] * pd[i+1][1] <= 0) { 5798 if (pd[i][1] < 0) { 5799 isnegative = true; 5800 opts.fillStyle = negativeColor; 5801 } 5802 else { 5803 isnegative = false; 5804 opts.fillStyle = posfs; 5805 } 5806 5807 var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]); 5808 tempgd.push([xintercept, pyzero]); 5809 this._areaPoints.push([xintercept, pyzero]); 5810 // now draw this shape and shadow. 5811 if (shadow) { 5812 this.renderer.shadowRenderer.draw(ctx, tempgd, opts); 5813 } 5814 this.renderer.shapeRenderer.draw(ctx, tempgd, opts); 5815 // now empty temp array and continue 5816 tempgd = [[xintercept, pyzero]]; 5817 // this._areaPoints = [[xintercept, pyzero]]; 5818 } 5819 } 5820 if (pd[gd.length-1][1] < 0) { 5821 isnegative = true; 5822 opts.fillStyle = negativeColor; 5823 } 5824 else { 5825 isnegative = false; 5826 opts.fillStyle = posfs; 5827 } 5828 tempgd.push(gd[gd.length-1]); 5829 this._areaPoints.push(gd[gd.length-1]); 5830 tempgd.push([gd[gd.length-1][0], pyzero]); 5831 this._areaPoints.push([gd[gd.length-1][0], pyzero]); 5832 } 5833 // now draw the last area. 5834 if (shadow) { 5835 this.renderer.shadowRenderer.draw(ctx, tempgd, opts); 5836 } 5837 this.renderer.shapeRenderer.draw(ctx, tempgd, opts); 5838 5839 5840 // var gridymin = this._yaxis.series_u2p(0); 5841 // // IE doesn't return new length on unshift 5842 // gd.unshift([gd[0][0], gridymin]); 5843 // len = gd.length; 5844 // gd.push([gd[len - 1][0], gridymin]); 5845 } 5846 // if stacked, fill to line below 5847 else { 5848 var prev = this._prevGridData; 5849 for (var i=prev.length; i>0; i--) { 5850 gd.push(prev[i-1]); 5851 // this._areaPoints.push(prev[i-1]); 5852 } 5853 if (shadow) { 5854 this.renderer.shadowRenderer.draw(ctx, gd, opts); 5855 } 5856 this._areaPoints = gd; 5857 this.renderer.shapeRenderer.draw(ctx, gd, opts); 5858 } 5859 }
5860 ///////////////////////// 5861 // Not filled to zero 5862 //////////////////////// 5863 else { 5864 // if stoking line as well as filling, get a copy of line data. 5865 if (fillAndStroke) { 5866 var fasgd = gd.slice(0); 5867 } 5868 // if not stacked, fill down to axis 5869 if (this.index == 0 || !this._stack) { 5870 // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2; 5871 var gridymin = ctx.canvas.height; 5872 // IE doesn't return new length on unshift 5873 gd.unshift([gd[0][0], gridymin]); 5874 var len = gd.length; 5875 gd.push([gd[len - 1][0], gridymin]); 5876 } 5877 // if stacked, fill to line below 5878 else { 5879 var prev = this._prevGridData; 5880 for (var i=prev.length; i>0; i--) { 5881 gd.push(prev[i-1]); 5882 } 5883 } 5884 this._areaPoints = gd; 5885 5886 if (shadow) { 5887 this.renderer.shadowRenderer.draw(ctx, gd, opts); 5888 } 5889 5890 this.renderer.shapeRenderer.draw(ctx, gd, opts); 5891 } 5892 if (fillAndStroke) { 5893 var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false}); 5894 this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
5895 ////////// 5896 // TODO: figure out some way to do shadows nicely 5897 // if (shadow) { 5898 // this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts); 5899 // } 5900 // now draw the markers 5901 if (this.markerRenderer.show) { 5902 if (this.renderer.smooth) { 5903 fasgd = this.gridData; 5904 } 5905 for (i=0; i<fasgd.length; i++) { 5906 this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions); 5907 } 5908 } 5909 } 5910 } 5911 else { 5912 5913 if (this.renderer.bands.show) { 5914 var bdat; 5915 var bopts = $.extend(true, {}, opts); 5916 if (this.renderer.bands.showLines) { 5917 bdat = (this.renderer.smooth) ? this.renderer._hiBandSmoothedData : this.renderer._hiBandGridData; 5918 this.renderer.shapeRenderer.draw(ctx, bdat, opts); 5919 bdat = (this.renderer.smooth) ? this.renderer._lowBandSmoothedData : this.renderer._lowBandGridData; 5920 this.renderer.shapeRenderer.draw(ctx, bdat, bopts); 5921 } 5922 5923 if (this.renderer.bands.fill) { 5924 if (this.renderer.smooth) { 5925 bdat = this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse()); 5926 } 5927 else { 5928 bdat = this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse()); 5929 } 5930 this._areaPoints = bdat; 5931 bopts.closePath = true; 5932 bopts.fill = true; 5933 bopts.fillStyle = this.renderer.bands.fillColor; 5934 this.renderer.shapeRenderer.draw(ctx, bdat, bopts); 5935 } 5936 } 5937 5938 if (shadow) { 5939 this.renderer.shadowRenderer.draw(ctx, gd, opts); 5940 } 5941 5942 this.renderer.shapeRenderer.draw(ctx, gd, opts); 5943 } 5944 } 5945 // calculate the bounding box 5946 var xmin = xmax = ymin = ymax = null; 5947 for (i=0; i<this._areaPoints.length; i++) { 5948 var p = this._areaPoints[i]; 5949 if (xmin > p[0] || xmin == null) { 5950 xmin = p[0]; 5951 } 5952 if (ymax < p[1] || ymax == null) { 5953 ymax = p[1]; 5954 } 5955 if (xmax < p[0] || xmax == null) { 5956 xmax = p[0]; 5957 } 5958 if (ymin > p[1] || ymin == null) { 5959 ymin = p[1]; 5960 } 5961 } 5962 5963 if (this.type === 'line' && this.renderer.bands.show) { 5964 ymax = this._yaxis.series_u2p(this.renderer.bands._min); 5965 ymin = this._yaxis.series_u2p(this.renderer.bands._max); 5966 } 5967 5968 this._boundingBox = [[xmin, ymax], [xmax, ymin]]; 5969 5970 // now draw the markers 5971 if (this.markerRenderer.show && !fill) { 5972 if (this.renderer.smooth) { 5973 gd = this.gridData; 5974 } 5975 for (i=0; i<gd.length; i++) { 5976 if (gd[i][0] != null && gd[i][1] != null) { 5977 this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions); 5978 } 5979 } 5980 } 5981 } 5982 5983 ctx.restore(); 5984 }; 5985 5986 $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) { 5987 // This is a no-op, shadows drawn with lines. 5988 }; 5989 5990 // called with scope of plot. 5991 // make sure to not leave anything highlighted. 5992 function postInit(target, data, options) { 5993 for (var i=0; i<this.series.length; i++) { 5994 if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) { 5995 // don't allow mouseover and mousedown at same time. 5996 if (this.series[i].highlightMouseOver) { 5997 this.series[i].highlightMouseDown = false; 5998 } 5999 } 6000 } 6001 } 6002 6003 // called within context of plot 6004 // create a canvas which we can draw on. 6005 // insert it before the eventCanvas, so eventCanvas will still capture events. 6006 function postPlotDraw() { 6007 // Memory Leaks patch 6008 if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) { 6009 this.plugins.lineRenderer.highlightCanvas.resetCanvas(); 6010 this.plugins.lineRenderer.highlightCanvas = null; 6011 } 6012 6013 this.plugins.lineRenderer.highlightedSeriesIndex = null; 6014 this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); 6015 6016 this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions, this)); 6017 this.plugins.lineRenderer.highlightCanvas.setContext(); 6018 this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); 6019 } 6020 6021 function highlight (plot, sidx, pidx, points) { 6022 var s = plot.series[sidx]; 6023 var canvas = plot.plugins.lineRenderer.highlightCanvas; 6024 canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); 6025 s._highlightedPoint = pidx; 6026 plot.plugins.lineRenderer.highlightedSeriesIndex = sidx; 6027 var opts = {fillStyle: s.highlightColor}; 6028 if (s.type === 'line' && s.renderer.bands.show) { 6029 opts.fill = true; 6030 opts.closePath = true; 6031 } 6032 s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); 6033 canvas = null; 6034 } 6035 6036 function unhighlight (plot) { 6037 var canvas = plot.plugins.lineRenderer.highlightCanvas; 6038 canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); 6039 for (var i=0; i<plot.series.length; i++) { 6040 plot.series[i]._highlightedPoint = null; 6041 } 6042 plot.plugins.lineRenderer.highlightedSeriesIndex = null; 6043 plot.target.trigger('jqplotDataUnhighlight'); 6044 canvas = null; 6045 } 6046 6047 6048 function handleMove(ev, gridpos, datapos, neighbor, plot) { 6049 if (neighbor) { 6050 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 6051 var evt1 = jQuery.Event('jqplotDataMouseOver'); 6052 evt1.pageX = ev.pageX; 6053 evt1.pageY = ev.pageY; 6054 plot.target.trigger(evt1, ins); 6055 if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) { 6056 var evt = jQuery.Event('jqplotDataHighlight'); 6057 evt.which = ev.which; 6058 evt.pageX = ev.pageX; 6059 evt.pageY = ev.pageY; 6060 plot.target.trigger(evt, ins);
6061 highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); 6062 } 6063 } 6064 else if (neighbor == null) { 6065 unhighlight (plot); 6066 } 6067 } 6068 6069 function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { 6070 if (neighbor) { 6071 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 6072 if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) { 6073 var evt = jQuery.Event('jqplotDataHighlight'); 6074 evt.which = ev.which; 6075 evt.pageX = ev.pageX; 6076 evt.pageY = ev.pageY; 6077 plot.target.trigger(evt, ins); 6078 highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); 6079 } 6080 } 6081 else if (neighbor == null) { 6082 unhighlight (plot); 6083 } 6084 } 6085 6086 function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { 6087 var idx = plot.plugins.lineRenderer.highlightedSeriesIndex; 6088 if (idx != null && plot.series[idx].highlightMouseDown) { 6089 unhighlight(plot); 6090 } 6091 } 6092 6093 function handleClick(ev, gridpos, datapos, neighbor, plot) { 6094 if (neighbor) { 6095 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 6096 var evt = jQuery.Event('jqplotDataClick'); 6097 evt.which = ev.which; 6098 evt.pageX = ev.pageX; 6099 evt.pageY = ev.pageY; 6100 plot.target.trigger(evt, ins); 6101 } 6102 } 6103 6104 function handleRightClick(ev, gridpos, datapos, neighbor, plot) { 6105 if (neighbor) { 6106 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 6107 var idx = plot.plugins.lineRenderer.highlightedSeriesIndex; 6108 if (idx != null && plot.series[idx].highlightMouseDown) { 6109 unhighlight(plot); 6110 } 6111 var evt = jQuery.Event('jqplotDataRightClick'); 6112 evt.which = ev.which; 6113 evt.pageX = ev.pageX; 6114 evt.pageY = ev.pageY; 6115 plot.target.trigger(evt, ins); 6116 } 6117 } 6118 6119 6120 // class: $.jqplot.LinearAxisRenderer 6121 // The default jqPlot axis renderer, creating a numeric axis. 6122 $.jqplot.LinearAxisRenderer = function() { 6123 }; 6124 6125 // called with scope of axis object. 6126 $.jqplot.LinearAxisRenderer.prototype.init = function(options){ 6127 // prop: breakPoints 6128 // EXPERIMENTAL!! Use at your own risk! 6129 // Works only with linear axes and the default tick renderer. 6130 // Array of [start, stop] points to create a broken axis. 6131 // Broken axes have a "jump" in them, which is an immediate 6132 // transition from a smaller value to a larger value. 6133 // Currently, axis ticks MUST be manually assigned if using breakPoints 6134 // by using the axis ticks array option. 6135 this.breakPoints = null; 6136 // prop: breakTickLabel 6137 // Label to use at the axis break if breakPoints are specified. 6138 this.breakTickLabel = "≈"; 6139 // prop: drawBaseline 6140 // True to draw the axis baseline. 6141 this.drawBaseline = true; 6142 // prop: baselineWidth 6143 // width of the baseline in pixels. 6144 this.baselineWidth = null; 6145 // prop: baselineColor 6146 // CSS color spec for the baseline. 6147 this.baselineColor = null; 6148 // prop: forceTickAt0 6149 // This will ensure that there is always a tick mark at 0. 6150 // If data range is strictly positive or negative, 6151 // this will force 0 to be inside the axis bounds unless 6152 // the appropriate axis pad (pad, padMin or padMax) is set 6153 // to 0, then this will force an axis min or max value at 0. 6154 // This has know effect when any of the following options 6155 // are set: autoscale, min, max, numberTicks or tickInterval. 6156 this.forceTickAt0 = false; 6157 // prop: forceTickAt100 6158 // This will ensure that there is always a tick mark at 100. 6159 // If data range is strictly above or below 100,
6160 // this will force 100 to be inside the axis bounds unless 6161 // the appropriate axis pad (pad, padMin or padMax) is set 6162 // to 0, then this will force an axis min or max value at 100. 6163 // This has know effect when any of the following options 6164 // are set: autoscale, min, max, numberTicks or tickInterval. 6165 this.forceTickAt100 = false; 6166 // prop: tickInset 6167 // Controls the amount to inset the first and last ticks from 6168 // the edges of the grid, in multiples of the tick interval. 6169 // 0 is no inset, 0.5 is one half a tick interval, 1 is a full 6170 // tick interval, etc. 6171 this.tickInset = 0; 6172 // prop: minorTicks 6173 // Number of ticks to add between "major" ticks. 6174 // Major ticks are ticks supplied by user or auto computed. 6175 // Minor ticks cannot be created by user. 6176 this.minorTicks = 0; 6177 // prop: alignTicks 6178 // true to align tick marks across opposed axes 6179 // such as from the y2axis to yaxis. 6180 this.alignTicks = false; 6181 this._autoFormatString = ''; 6182 this._overrideFormatString = false; 6183 this._scalefact = 1.0; 6184 $.extend(true, this, options); 6185 if (this.breakPoints) { 6186 if (!$.isArray(this.breakPoints)) { 6187 this.breakPoints = null; 6188 } 6189 else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) { 6190 this.breakPoints = null; 6191 } 6192 } 6193 if (this.numberTicks != null && this.numberTicks < 2) { 6194 this.numberTicks = 2; 6195 } 6196 this.resetDataBounds(); 6197 }; 6198 6199 // called with scope of axis 6200 $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) { 6201 if (this.show) { 6202 // populate the axis label and value properties. 6203 // createTicks is a method on the renderer, but 6204 // call it within the scope of the axis. 6205 this.renderer.createTicks.call(this, plot); 6206 // fill a div with axes labels in the right direction. 6207 // Need to pregenerate each axis to get its bounds and 6208 // position it and the labels correctly on the plot. 6209 var dim=0; 6210 var temp; 6211 // Added for theming. 6212 if (this._elem) { 6213 // Memory Leaks patch 6214 //this._elem.empty(); 6215 this._elem.emptyForce(); 6216 this._elem = null; 6217 } 6218 6219 this._elem = $(document.createElement('div')); 6220 this._elem.addClass('jqplot-axis jqplot-'+this.name); 6221 this._elem.css('position', 'absolute'); 6222 6223 6224 if (this.name == 'xaxis' || this.name == 'x2axis') { 6225 this._elem.width(this._plotDimensions.width); 6226 } 6227 else { 6228 this._elem.height(this._plotDimensions.height); 6229 } 6230 6231 // create a _label object. 6232 this.labelOptions.axis = this.name; 6233 this._label = new this.labelRenderer(this.labelOptions); 6234 if (this._label.show) { 6235 var elem = this._label.draw(ctx, plot); 6236 elem.appendTo(this._elem); 6237 elem = null; 6238 } 6239 6240 var t = this._ticks; 6241 var tick; 6242 for (var i=0; i<t.length; i++) { 6243 tick = t[i]; 6244 if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { 6245 this._elem.append(tick.draw(ctx, plot)); 6246 } 6247 } 6248 tick = null; 6249 t = null; 6250 } 6251 return this._elem; 6252 }; 6253 6254 // called with scope of an axis 6255 $.jqplot.LinearAxisRenderer.prototype.reset = function() { 6256 this.min = this._options.min; 6257 this.max = this._options.max; 6258 this.tickInterval = this._options.tickInterval; 6259 this.numberTicks = this._options.numberTicks; 6260 this._autoFormatString = ''; 6261 if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) { 6262 this.tickOptions.formatString = ''; 6263 } 6264 6265 // this._ticks = this.__ticks; 6266 }; 6267 6268 // called with scope of axis 6269 $.jqplot.LinearAxisRenderer.prototype.set = function() { 6270 var dim = 0; 6271 var temp; 6272 var w = 0; 6273 var h = 0; 6274 var lshow = (this._label == null) ? false : this._label.show; 6275 if (this.show) { 6276 var t = this._ticks; 6277 var tick; 6278 for (var i=0; i<t.length; i++) { 6279 tick = t[i]; 6280 if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { 6281 if (this.name == 'xaxis' || this.name == 'x2axis') { 6282 temp = tick._elem.outerHeight(true); 6283 } 6284 else { 6285 temp = tick._elem.outerWidth(true); 6286 } 6287 if (temp > dim) { 6288 dim = temp; 6289 } 6290 } 6291 } 6292 tick = null; 6293 t = null; 6294 6295 if (lshow) { 6296 w = this._label._elem.outerWidth(true); 6297 h = this._label._elem.outerHeight(true); 6298 } 6299 if (this.name == 'xaxis') { 6300 dim = dim + h; 6301 this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); 6302 } 6303 else if (this.name == 'x2axis') { 6304 dim = dim + h; 6305 this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); 6306 } 6307 else if (this.name == 'yaxis') { 6308 dim = dim + w; 6309 this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); 6310 if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { 6311 this._label._elem.css('width', w+'px'); 6312 } 6313 } 6314 else { 6315 dim = dim + w; 6316 this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); 6317 if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { 6318 this._label._elem.css('width', w+'px'); 6319 } 6320 } 6321 } 6322 }; 6323 6324 // called with scope of axis 6325 $.jqplot.LinearAxisRenderer.prototype.createTicks = function(plot) { 6326 // we're are operating on an axis here 6327 var ticks = this._ticks; 6328 var userTicks = this.ticks; 6329 var name = this.name; 6330 // databounds were set on axis initialization. 6331 var db = this._dataBounds; 6332 var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; 6333 var interval; 6334 var min, max; 6335 var pos1, pos2; 6336 var tt, i; 6337 // get a copy of user's settings for min/max. 6338 var userMin = this.min; 6339 var userMax = this.max; 6340 var userNT = this.numberTicks; 6341 var userTI = this.tickInterval; 6342 6343 var threshold = 30; 6344 this._scalefact = (Math.max(dim, threshold+1) - threshold)/300.0; 6345 6346 // if we already have ticks, use them. 6347 // ticks must be in order of increasing value. 6348 6349 if (userTicks.length) { 6350 // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed 6351 for (i=0; i<userTicks.length; i++){ 6352 var ut = userTicks[i]; 6353 var t = new this.tickRenderer(this.tickOptions); 6354 if ($.isArray(ut)) { 6355 t.value = ut[0]; 6356 if (this.breakPoints) { 6357 if (ut[0] == this.breakPoints[0]) { 6358 t.label = this.breakTickLabel; 6359 t._breakTick = true; 6360 t.showGridline = false; 6361 t.showMark = false; 6362 } 6363 else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) { 6364 t.show = false; 6365 t.showGridline = false; 6366 t.label = ut[1]; 6367 } 6368 else { 6369 t.label = ut[1]; 6370 } 6371 } 6372 else { 6373 t.label = ut[1]; 6374 } 6375 t.setTick(ut[0], this.name); 6376 this._ticks.push(t); 6377 } 6378 6379 else if ($.isPlainObject(ut)) { 6380 $.extend(true, t, ut); 6381 t.axis = this.name; 6382 this._ticks.push(t); 6383 } 6384 6385 else { 6386 t.value = ut; 6387 if (this.breakPoints) { 6388 if (ut == this.breakPoints[0]) { 6389 t.label = this.breakTickLabel; 6390 t._breakTick = true; 6391 t.showGridline = false; 6392 t.showMark = false; 6393 } 6394 else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) { 6395 t.show = false; 6396 t.showGridline = false; 6397 } 6398 } 6399 t.setTick(ut, this.name); 6400 this._ticks.push(t); 6401 } 6402 } 6403 this.numberTicks = userTicks.length; 6404 this.min = this._ticks[0].value; 6405 this.max = this._ticks[this.numberTicks-1].value; 6406 this.tickInterval = (this.max - this.min) / (this.numberTicks - 1); 6407 } 6408 6409 // we don't have any ticks yet, let's make some! 6410 else { 6411 if (name == 'xaxis' || name == 'x2axis') { 6412 dim = this._plotDimensions.width; 6413 } 6414 else { 6415 dim = this._plotDimensions.height; 6416 } 6417 6418 var _numberTicks = this.numberTicks; 6419 6420 // if aligning this axis, use number of ticks from previous axis.
6421 // Do I need to reset somehow if alignTicks is changed and then graph is replotted?? 6422 if (this.alignTicks) { 6423 if (this.name === 'x2axis' && plot.axes.xaxis.show) { 6424 _numberTicks = plot.axes.xaxis.numberTicks; 6425 } 6426 else if (this.name.charAt(0) === 'y' && this.name !== 'yaxis' && this.name !== 'yMidAxis' && plot.axes.yaxis.show) { 6427 _numberTicks = plot.axes.yaxis.numberTicks; 6428 } 6429 } 6430 6431 min = ((this.min != null) ? this.min : db.min); 6432 max = ((this.max != null) ? this.max : db.max); 6433 6434 var range = max - min; 6435 var rmin, rmax; 6436 var temp; 6437 6438 if (this.tickOptions == null || !this.tickOptions.formatString) { 6439 this._overrideFormatString = true; 6440 } 6441 6442 // Doing complete autoscaling 6443 if (this.min == null || this.max == null && this.tickInterval == null && !this.autoscale) { 6444 // Check if user must have tick at 0 or 100 and ensure they are in range. 6445 // The autoscaling algorithm will always place ticks at 0 and 100 if they are in range. 6446 if (this.forceTickAt0) { 6447 if (min > 0) { 6448 min = 0; 6449 } 6450 if (max < 0) { 6451 max = 0; 6452 } 6453 } 6454 6455 if (this.forceTickAt100) { 6456 if (min > 100) { 6457 min = 100; 6458 } 6459 if (max < 100) { 6460 max = 100; 6461 } 6462 } 6463 6464 var keepMin = false, 6465 keepMax = false; 6466 6467 if (this.min != null) { 6468 keepMin = true; 6469 } 6470 6471 else if (this.max != null) { 6472 keepMax = true; 6473 } 6474 6475 // var threshold = 30; 6476 // var tdim = Math.max(dim, threshold+1); 6477 // this._scalefact = (tdim-threshold)/300.0; 6478 var ret = $.jqplot.LinearTickGenerator(min, max, this._scalefact, _numberTicks, keepMin, keepMax); 6479 // calculate a padded max and min, points should be less than these 6480 // so that they aren't too close to the edges of the plot. 6481 // User can adjust how much padding is allowed with pad, padMin and PadMax options. 6482 // If min or max is set, don't pad that end of axis. 6483 var tumin = (this.min != null) ? min : min + range*(this.padMin - 1); 6484 var tumax = (this.max != null) ? max : max - range*(this.padMax - 1); 6485 6486 // if they're equal, we shouldn't have to do anything, right? 6487 // if (min <=tumin || max >= tumax) { 6488 if (min <tumin || max > tumax) { 6489 tumin = (this.min != null) ? min : min - range*(this.padMin - 1); 6490 tumax = (this.max != null) ? max : max + range*(this.padMax - 1); 6491 ret = $.jqplot.LinearTickGenerator(tumin, tumax, this._scalefact, _numberTicks, keepMin, keepMax); 6492 } 6493 6494 this.min = ret[0]; 6495 this.max = ret[1]; 6496 // if numberTicks specified, it should return the same. 6497 this.numberTicks = ret[2]; 6498 this._autoFormatString = ret[3]; 6499 this.tickInterval = ret[4]; 6500 } 6501 6502 // User has specified some axis scale related option, can use auto algorithm 6503 else { 6504 6505 // if min and max are same, space them out a bit 6506 if (min == max) { 6507 var adj = 0.05; 6508 if (min > 0) { 6509 adj = Math.max(Math.log(min)/Math.LN10, 0.05); 6510 } 6511 min -= adj; 6512 max += adj; 6513 } 6514 6515 // autoscale. Can't autoscale if min or max is supplied.
6516 // Will use numberTicks and tickInterval if supplied. Ticks 6517 // across multiple axes may not line up depending on how 6518 // bars are to be plotted. 6519 if (this.autoscale && this.min == null && this.max == null) { 6520 var rrange, ti, margin; 6521 var forceMinZero = false; 6522 var forceZeroLine = false; 6523 var intervals = {min:null, max:null, average:null, stddev:null}; 6524 // if any series are bars, or if any are fill to zero, and if this 6525 // is the axis to fill toward, check to see if we can start axis at zero. 6526 for (var i=0; i<this._series.length; i++) { 6527 var s = this._series[i]; 6528 var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name; 6529 // check to see if this is the fill axis 6530 if (this.name == faname) { 6531 var vals = s._plotValues[s.fillAxis]; 6532 var vmin = vals[0]; 6533 var vmax = vals[0]; 6534 for (var j=1; j<vals.length; j++) { 6535 if (vals[j] < vmin) { 6536 vmin = vals[j]; 6537 } 6538 else if (vals[j] > vmax) { 6539 vmax = vals[j]; 6540 } 6541 } 6542 var dp = (vmax - vmin) / vmax; 6543 // is this sries a bar? 6544 if (s.renderer.constructor == $.jqplot.BarRenderer) { 6545 // if no negative values and could also check range. 6546 if (vmin >= 0 && (s.fillToZero || dp > 0.1)) { 6547 forceMinZero = true; 6548 } 6549 else { 6550 forceMinZero = false; 6551 if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) { 6552 forceZeroLine = true; 6553 } 6554 else { 6555 forceZeroLine = false; 6556 } 6557 } 6558 } 6559 6560 // if not a bar and filling, use appropriate method. 6561 else if (s.fill) { 6562 if (vmin >= 0 && (s.fillToZero || dp > 0.1)) { 6563 forceMinZero = true; 6564 } 6565 else if (vmin < 0 && vmax > 0 && s.fillToZero) { 6566 forceMinZero = false; 6567 forceZeroLine = true; 6568 } 6569 else { 6570 forceMinZero = false; 6571 forceZeroLine = false; 6572 } 6573 } 6574 6575 // if not a bar and not filling, only change existing state 6576 // if it doesn't make sense 6577 else if (vmin < 0) { 6578 forceMinZero = false; 6579 } 6580 } 6581 } 6582 6583 // check if we need make axis min at 0. 6584 if (forceMinZero) { 6585 // compute number of ticks 6586 this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); 6587 this.min = 0; 6588 userMin = 0; 6589 // what order is this range? 6590 // what tick interval does that give us? 6591 ti = max/(this.numberTicks-1); 6592 temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); 6593 if (ti/temp == parseInt(ti/temp, 10)) { 6594 ti += temp; 6595 }
6596 this.tickInterval = Math.ceil(ti/temp) * temp; 6597 this.max = this.tickInterval * (this.numberTicks - 1); 6598 } 6599 6600 // check if we need to make sure there is a tick at 0. 6601 else if (forceZeroLine) { 6602 // compute number of ticks 6603 this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); 6604 var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1)); 6605 var ntmax = this.numberTicks - 1 - ntmin; 6606 ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax)); 6607 temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); 6608 this.tickInterval = Math.ceil(ti/temp) * temp; 6609 this.max = this.tickInterval * ntmax; 6610 this.min = -this.tickInterval * ntmin; 6611 } 6612 6613 // if nothing else, do autoscaling which will try to line up ticks across axes. 6614 else { 6615 if (this.numberTicks == null){ 6616 if (this.tickInterval) { 6617 this.numberTicks = 3 + Math.ceil(range / this.tickInterval); 6618 } 6619 else { 6620 this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); 6621 } 6622 } 6623 6624 if (this.tickInterval == null) { 6625 // get a tick interval 6626 ti = range/(this.numberTicks - 1); 6627 6628 if (ti < 1) { 6629 temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); 6630 } 6631 else { 6632 temp = 1; 6633 } 6634 this.tickInterval = Math.ceil(ti*temp*this.pad)/temp; 6635 } 6636 else { 6637 temp = 1 / this.tickInterval; 6638 } 6639 6640 // try to compute a nicer, more even tick interval 6641 // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10)); 6642 // this.tickInterval = Math.ceil(ti/temp) * temp; 6643 rrange = this.tickInterval * (this.numberTicks - 1); 6644 margin = (rrange - range)/2; 6645 6646 if (this.min == null) { 6647 this.min = Math.floor(temp*(min-margin))/temp; 6648 } 6649 if (this.max == null) { 6650 this.max = this.min + rrange; 6651 } 6652 } 6653 6654 // Compute a somewhat decent format string if it is needed. 6655 // get precision of interval and determine a format string. 6656 var sf = $.jqplot.getSignificantFigures(this.tickInterval); 6657 6658 var fstr; 6659 6660 // if we have only a whole number, use integer formatting 6661 if (sf.digitsLeft >= sf.significantDigits) { 6662 fstr = '%d'; 6663 } 6664 6665 else { 6666 var temp = Math.max(0, 5 - sf.digitsLeft); 6667 temp = Math.min(temp, sf.digitsRight); 6668 fstr = '%.'+ temp + 'f'; 6669 } 6670 6671 this._autoFormatString = fstr; 6672 } 6673 6674 // Use the default algorithm which pads each axis to make the chart 6675 // centered nicely on the grid. 6676 else { 6677 6678 rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1); 6679 rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1); 6680 range = rmax - rmin; 6681 6682 if (this.numberTicks == null){ 6683 // if tickInterval is specified by user, we will ignore computed maximum. 6684 // max will be equal or greater to fit even # of ticks. 6685 if (this.tickInterval != null) { 6686 this.numberTicks = Math.ceil((rmax - rmin)/this.tickInterval)+1; 6687 } 6688 else if (dim > 100) { 6689 this.numberTicks = parseInt(3+(dim-100)/75, 10); 6690 } 6691 else { 6692 this.numberTicks = 2; 6693 } 6694 } 6695 6696 if (this.tickInterval == null) { 6697 this.tickInterval = range / (this.numberTicks-1); 6698 } 6699 6700 if (this.max == null) { 6701 rmax = rmin + this.tickInterval*(this.numberTicks - 1); 6702 } 6703 if (this.min == null) { 6704 rmin = rmax - this.tickInterval*(this.numberTicks - 1); 6705 } 6706 6707 // get precision of interval and determine a format str
6707ing. 6708 var sf = $.jqplot.getSignificantFigures(this.tickInterval); 6709 6710 var fstr; 6711 6712 // if we have only a whole number, use integer formatting 6713 if (sf.digitsLeft >= sf.significantDigits) { 6714 fstr = '%d'; 6715 } 6716 6717 else { 6718 var temp = Math.max(0, 5 - sf.digitsLeft); 6719 temp = Math.min(temp, sf.digitsRight); 6720 fstr = '%.'+ temp + 'f'; 6721 } 6722 6723 6724 this._autoFormatString = fstr; 6725 6726 this.min = rmin; 6727 this.max = rmax; 6728 } 6729 6730 if (this.renderer.constructor == $.jqplot.LinearAxisRenderer && this._autoFormatString == '') { 6731 // fix for misleading tick display with small range and low precision. 6732 range = this.max - this.min; 6733 // figure out precision 6734 var temptick = new this.tickRenderer(this.tickOptions); 6735 // use the tick formatString or, the default. 6736 var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString; 6737 var fs = fs.match($.jqplot.sprintf.regex)[0]; 6738 var precision = 0; 6739 if (fs) { 6740 if (fs.search(/[fFeEgGpP]/) > -1) { 6741 var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/); 6742 if (m) { 6743 precision = parseInt(m[1], 10); 6744 } 6745 else { 6746 precision = 6; 6747 } 6748 } 6749 else if (fs.search(/[di]/) > -1) { 6750 precision = 0; 6751 } 6752 // fact will be <= 1; 6753 var fact = Math.pow(10, -precision); 6754 if (this.tickInterval < fact) { 6755 // need to correct underrange 6756 if (userNT == null && userTI == null) { 6757 this.tickInterval = fact; 6758 if (userMax == null && userMin == null) { 6759 // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact; 6760 this.min = Math.floor(this._dataBounds.min/fact) * fact; 6761 if (this.min == this._dataBounds.min) { 6762 this.min = this._dataBounds.min - this.tickInterval; 6763 } 6764 // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact; 6765 this.max = Math.ceil(this._dataBounds.max/fact) * fact; 6766 if (this.max == this._dataBounds.max) { 6767 this.max = this._dataBounds.max + this.tickInterval; 6768 } 6769 var n = (this.max - this.min)/this.tickInterval; 6770 n = n.toFixed(11); 6771 n = Math.ceil(n); 6772 this.numberTicks = n + 1; 6773 } 6774 else if (userMax == null) { 6775 // add one tick for top of range. 6776 var n = (this._dataBounds.max - this.min) / this.tickInterval; 6777 n = n.toFixed(11); 6778 this.numberTicks = Math.ceil(n) + 2; 6779 this.max = this.min + this.tickInterval * (this.numberTicks-1); 6780 } 6781 else if (userMin == null) { 6782 // add one tick for bottom of range. 6783 var n = (this.max - this._dataBounds.min) / this.tickInterval; 6784 n = n.toFixed(11); 6785 this.numberTicks = Math.ceil(n) + 2; 6786 this.min = this.max - this.tickInterval * (this.numberTicks-1); 6787 } 6788 else { 6789 // calculate a number of ticks so max is within axis scale 6790 this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1; 6791 // if user's min and max don't fit evenly in ticks, adju
6791st. 6792 // This takes care of cases such as user min set to 0, max set to 3.5 but tick 6793 // format string set to %d (integer ticks) 6794 this.min = Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision); 6795 this.max = Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision); 6796 // this.max = this.min + this.tickInterval*(this.numberTicks-1); 6797 this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1; 6798 } 6799 } 6800 } 6801 } 6802 } 6803 6804 } 6805 6806 if (this._overrideFormatString && this._autoFormatString != '') { 6807 this.tickOptions = this.tickOptions || {}; 6808 this.tickOptions.formatString = this._autoFormatString; 6809 } 6810 6811 var t, to; 6812 for (var i=0; i<this.numberTicks; i++){ 6813 tt = this.min + i * this.tickInterval; 6814 t = new this.tickRenderer(this.tickOptions); 6815 // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); 6816 6817 t.setTick(tt, this.name); 6818 this._ticks.push(t); 6819 6820 if (i < this.numberTicks - 1) { 6821 for (var j=0; j<this.minorTicks; j++) { 6822 tt += this.tickInterval/(this.minorTicks+1); 6823 to = $.extend(true, {}, this.tickOptions, {name:this.name, value:tt, label:'', isMinorTick:true}); 6824 t = new this.tickRenderer(to); 6825 this._ticks.push(t); 6826 } 6827 } 6828 t = null; 6829 } 6830 } 6831 6832 if (this.tickInset) { 6833 this.min = this.min - this.tickInset * this.tickInterval; 6834 this.max = this.max + this.tickInset * this.tickInterval; 6835 } 6836 6837 ticks = null; 6838 }; 6839 6840 // Used to reset just the values of the ticks and then repack, which will 6841 // recalculate the positioning functions. It is assuemd that the 6842 // number of ticks is the same and the values of the new array are at the 6843 // proper interval. 6844 // This method needs to be called with the scope of an axis object, like: 6845 // 6846 // > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr); 6847 // 6848 $.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) { 6849 if ($.isArray(opts) && opts.length == this._ticks.length) { 6850 var t; 6851 for (var i=0; i<opts.length; i++) { 6852 t = this._ticks[i]; 6853 t.value = opts[i]; 6854 t.label = t.formatter(t.formatString, opts[i]); 6855 t.label = t.prefix + t.label; 6856 t._elem.html(t.label); 6857 } 6858 t = null; 6859 this.min = $.jqplot.arrayMin(opts); 6860 this.max = $.jqplot.arrayMax(opts); 6861 this.pack(); 6862 } 6863 // Not implemented yet. 6864 // else if ($.isPlainObject(opts)) { 6865 // 6866 // } 6867 }; 6868 6869 // called with scope of axis 6870 $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) { 6871 // Add defaults for repacking from resetTickValues function. 6872 pos = pos || {}; 6873 offsets = offsets || this._offsets; 6874 6875 var ticks = this._ticks; 6876 var max = this.max; 6877 var min = this.min; 6878 var offmax = offsets.max; 6879 var offmin = offsets.min; 6880 var lshow = (this._label == null) ? false : this._label.show; 6881 6882 for (var p in pos) { 6883 this._elem.css(p, pos[p]); 6884 } 6885 6886 this._offsets = offsets; 6887 // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. 6888 var pixellength = offmax - offmin; 6889 var unitlength = max - min; 6890 6891 // point to unit and unit to point conversions references to Plot DOM element top left corner. 6892 if (this.breakPoints) { 6893 unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0]; 6894 6895 this.p2u = function(p){ 6896 return (p - offmin) * unitlength / pixellength + min; 6897 }; 6898 6899 this.u2p = function(u){ 6900 if (u > this.breakPoints[0] && u < this.breakPoints[1]){ 6901 u = this.breakPoints[0]; 6902 } 6903 if (u <= this.breakPoints[0]) { 6904 return (u - min) * pixellength / unitlength + offmin; 6905 } 6906 else { 6907 return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin; 6908 } 6909 }; 6910 6911 if (this.name.charAt(0) == 'x'){ 6912 this.series_u2p = function(u){ 6913 if (u > this.breakPoints[0] && u < this.breakPoints[1]){ 6914 u = this.breakPoints[0]; 6915 } 6916 if (u <= this.breakPoints[0]) { 6917 return (u - min) * pixellength / unitlength; 6918 } 6919 else { 6920 return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength; 6921 } 6922 }; 6923 this.series_p2u = function(p){ 6924 return p * unitlength / pixellength + min; 6925 }; 6926 } 6927 6928 else { 6929 this.series_u2p = function(u){ 6930 if (u > this.breakPoints[0] && u < this.breakPoints[1]){ 6931 u = this.breakPoints[0]; 6932 } 6933 if (u >= this.breakPoints[1]) { 6934 return (u - max) * pixellength / unitlength; 6935 } 6936 else { 6937 return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength; 6938 } 6939 }; 6940 this.series_p2u = function(p){ 6941 return p * unitlength / pixellength + max; 6942 }; 6943 } 6944 } 6945 else { 6946 this.p2u = function(p){ 6947 return (p - offmin) * unitlength / pixellength + min; 6948 }; 6949 6950 this.u2p = function(u){ 6951 return (u - min) * pixellength / unitlength + offmin; 6952 }; 6953 6954 if (this.name == 'xaxis' || this.name == 'x2axis'){ 6955 this.series_u2p = function(u){ 6956 return (u - min) * pixellength / unitlength; 6957 }; 6958 this.series_p2u = function(p){ 6959 return p * unitlength / pixellength + min; 6960 }; 6961 } 6962 6963 else { 6964 this.series_u2p = function(u){ 6965 return (u - max) * pixellength / unitlength; 6966 }; 6967 this.series_p2u = function(p){ 6968 return p * unitlength / pixellength + max; 6969 }; 6970 } 6971 } 6972 6973 if (this.show) { 6974 if (this.name == 'xaxis' || this.name == 'x2axis') { 6975 for (var i=0; i<ticks.length; i++) { 6976 var t = ticks[i]; 6977 if (t.show && t.showLabel) { 6978 var shim; 6979 6980 if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { 6981 // will need to adjust auto positioning based on which axis this is. 6982 var temp = (this.name == 'xaxis') ? 1 : -1; 6983 switch (t.labelPosition) { 6984 case 'auto': 6985 // position at end 6986 if (temp * t.angle < 0) {
6987 shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 6988 } 6989 // position at start 6990 else { 6991 shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; 6992 } 6993 break; 6994 case 'end': 6995 shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 6996 break; 6997 case 'start': 6998 shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; 6999 break; 7000 case 'middle': 7001 shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 7002 break; 7003 default: 7004 shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 7005 break; 7006 } 7007 } 7008 else { 7009 shim = -t.getWidth()/2; 7010 } 7011 var val = this.u2p(t.value) + shim + 'px'; 7012 t._elem.css('left', val); 7013 t.pack(); 7014 } 7015 } 7016 if (lshow) { 7017 var w = this._label._elem.outerWidth(true); 7018 this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); 7019 if (this.name == 'xaxis') { 7020 this._label._elem.css('bottom', '0px'); 7021 } 7022 else { 7023 this._label._elem.css('top', '0px'); 7024 } 7025 this._label.pack(); 7026 } 7027 } 7028 else { 7029 for (var i=0; i<ticks.length; i++) { 7030 var t = ticks[i]; 7031 if (t.show && t.showLabel) { 7032 var shim; 7033 if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { 7034 // will need to adjust auto positioning based on which axis this is. 7035 var temp = (this.name == 'yaxis') ? 1 : -1; 7036 switch (t.labelPosition) { 7037 case 'auto': 7038 // position at end 7039 case 'end': 7040 if (temp * t.angle < 0) { 7041 shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; 7042 } 7043 else { 7044 shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; 7045 } 7046 break; 7047 case 'start': 7048 if (t.angle > 0) { 7049 shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; 7050 } 7051 else { 7052 shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; 7053 } 7054 break; 7055 case 'middle': 7056 // if (t.angle > 0) { 7057 // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; 7058 // } 7059 // else { 7060 // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; 7061 // } 7062 shim = -t.getHeight()/2; 7063 break; 7064 default: 7065 shim = -t.getHeight()/2; 7066 break; 7067 } 7068 } 7069 else { 7070 shim = -t.getHeight()/2; 7071 } 7072 7073 var val = this.u2p(t.value) + shim + 'px'; 7074 t._elem.css('top', val); 7075 t.pack(); 7076 } 7077 } 7078 if (lshow) { 7079 var h = this._label._elem.outerHeight(true); 7080 this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); 7081 if (this.name == 'yaxis') { 7082 this._label._elem.css('left', '0px'); 7083 } 7084 else { 7085 this._label._elem.css('right', '0px'); 7086 } 7087 this._label.pack(); 7088 } 7089 } 7090 } 7091 7092 ticks = null; 7093 }; 7094 7095 7096 /** 7097 * The following code was generaously given to me a while back by Scott Prahl. 7098 * He did a good job at computing axes min, max and number of ticks for the 7099 * case where the user has not set any scale related parameters (tickInterval, 7100 * numberTicks, min or max). I had ignored this use case for a long time, 7101 * focusing on the more difficult case where user has set some option controlling 7102 * tick generation. Anyway, about time I got this into jqPlot. 7103 * Thanks Scott!! 7104 */ 7105 7106 /** 7107 * Copyright (c) 2010 Scott Prahl 7108 * The next three routines are currently available for use in all personal 7109 * or commercial projects under both the MIT and GPL version 2.0 licenses. 7110 * This means that you can choose the license that best suits your project 7111 * and use it accordingly. 7112 */ 7113
7114 // A good format string depends on the interval. If the interval is greater 7115 // than 1 then there is no need to show any decimal digits. If it is < 1.0, then 7116 // use the magnitude of the interval to determine the number of digits to show. 7117 function bestFormatString (interval) 7118 { 7119 var fstr; 7120 interval = Math.abs(interval); 7121 if (interval >= 10) { 7122 fstr = '%d'; 7123 } 7124 7125 else if (interval > 1) { 7126 if (interval === parseInt(interval, 10)) { 7127 fstr = '%d'; 7128 } 7129 else { 7130 fstr = '%.1f'; 7131 } 7132 } 7133 7134 else { 7135 var expv = -Math.floor(Math.log(interval)/Math.LN10); 7136 fstr = '%.' + expv + 'f'; 7137 } 7138 7139 return fstr; 7140 } 7141 7142 var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5]; 7143 7144 var _getLowerFactor = function(f) { 7145 var i = _factors.indexOf(f); 7146 if (i > 0) { 7147 return _factors[i-1]; 7148 } 7149 else { 7150 return _factors[_factors.length - 1] / 100; 7151 } 7152 }; 7153 7154 var _getHigherFactor = function(f) { 7155 var i = _factors.indexOf(f); 7156 if (i < _factors.length-1) { 7157 return _factors[i+1]; 7158 } 7159 else { 7160 return _factors[0] * 100; 7161 } 7162 }; 7163 7164 // Given a fixed minimum and maximum and a target number ot ticks 7165 // figure out the best interval and 7166 // return min, max, number ticks, format string and tick interval 7167 function bestConstrainedInterval(min, max, nttarget) { 7168 // run through possible number to ticks and see which interval is best 7169 var low = Math.floor(nttarget/2); 7170 var hi = Math.ceil(nttarget*1.5); 7171 var badness = Number.MAX_VALUE; 7172 var r = (max - min); 7173 var temp; 7174 var sd; 7175 var bestNT; 7176 var gsf = $.jqplot.getSignificantFigures; 7177 var fsd; 7178 var fs; 7179 var currentNT; 7180 var bestPrec; 7181 7182 for (var i=0, l=hi-low+1; i<l; i++) { 7183 currentNT = low + i; 7184 temp = r/(currentNT-1); 7185 sd = gsf(temp); 7186 7187 temp = Math.abs(nttarget - currentNT) + sd.digitsRight; 7188 if (temp < badness) { 7189 badness = temp; 7190 bestNT = currentNT; 7191 bestPrec = sd.digitsRight; 7192 } 7193 else if (temp === badness) { 7194 // let nicer ticks trump number ot ticks 7195 if (sd.digitsRight < bestPrec) { 7196 bestNT = currentNT; 7197 bestPrec = sd.digitsRight; 7198 } 7199 } 7200 7201 } 7202 7203 fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight)); 7204 if (fsd === 0) { 7205 fs = '%d'; 7206 } 7207 else { 7208 fs = '%.' + fsd + 'f'; 7209 } 7210 temp = r / (bestNT - 1); 7211 // min, max, number ticks, format string, tick interval 7212 return [min, max, bestNT, fs, temp]; 7213 } 7214 7215 // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n 7216 // it is based soley on the range and number of ticks. So if user specifies 7217 // number of ticks, use this. 7218 function bestInterval(range, numberTicks) { 7219 numberTicks = numberTicks || 7; 7220 var minimum = range / (numberTicks - 1); 7221 var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10)); 7222 var residual = minimum / magnitude; 7223 var interval; 7224 // "nicest" ranges are 1, 2, 5 or powers of these. 7225 // for magnitudes below 1, only allow these. 7226 if (magnitude < 1) { 7227 if (residual > 5) { 7228 interval = 10 * magnitude; 7229 } 7230 else if (residual > 2) { 7231 interval = 5 * magnitude; 7232 } 7233 else if (residual > 1) { 7234 interval = 2 * magnitude; 7235 } 7236 else { 7237 interval = magnitude; 7238 } 7239 } 7240 // for large ranges (whole integers), allow intervals like 3, 4 or powers of these. 7241 // this helps a lot with poor choices for number of ticks. 7242 else { 7243 if (residual > 5) { 7244 interval = 10 * magnitude; 7245 } 7246 else if (residual > 4) { 7247 interval = 5 * magnitude; 7248 } 7249 else if (residual > 3) { 7250 interval = 4 * magnitude; 7251 } 7252 else if (residual > 2) { 7253 interval = 3 * magnitude; 7254 } 7255 else if (residual > 1) { 7256 interval = 2 * magnitude; 7257 } 7258 else { 7259 interval = magnitude; 7260 } 7261 } 7262 7263 return interval; 7264 } 7265 7266 // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n 7267 // it is based soley on the range of data, number of ticks must be computed later. 7268 function bestLinearInterval(range, scalefact) { 7269 scalefact = scalefact || 1; 7270 var expv = Math.floor(Math.log(range)/Math.LN10); 7271 var magnitude = Math.pow(10, expv); 7272 // 0 < f < 10 7273 var f = range / magnitude; 7274 var fact; 7275 // for large plots, scalefact will decrease f and increase number of ticks. 7276 // for small plots, scalefact will increase f and decrease number of ticks. 7277 f = f/scalefact; 7278 7279 // for large plots, smaller interval, more ticks. 7280 if (f<=0.38) { 7281 fact = 0.1; 7282 } 7283 else if (f<=1.6) { 7284 fact = 0.2; 7285 } 7286 else if (f<=4.0) { 7287 fact = 0.5; 7288 } 7289 else if (f<=8.0) { 7290 fact = 1.0; 7291 } 7292 // for very small plots, larger interval, less ticks in number ticks 7293 else if (f<=16.0) { 7294 fact = 2; 7295 } 7296 else { 7297 fact = 5; 7298 } 7299 7300 return fact*magnitude; 7301 } 7302 7303 function bestLinearComponents(range, scalefact) { 7304 var expv = Math.floor(Math.log(range)/Math.LN10); 7305 var magnitude = Math.pow(10, expv); 7306 // 0 < f < 10 7307 var f = range / magnitude; 7308 var interval; 7309 var fact; 7310 // for large plots, scalefact will decrease f and increase number of ticks. 7311 // for small plots, scalefact will increase f and decrease number of ticks. 7312 f = f/scalefact; 7313 7314 // for large plots, smaller interval, more ticks. 7315 if (f<=0.38) { 7316 fact = 0.1; 7317 } 7318 else if (f<=1.6) { 7319 fact = 0.2; 7320 } 7321 else if (f<=4.0) { 7322 fact = 0.5; 7323 } 7324 else if (f<=8.0) { 7325 fact = 1.0; 7326 } 7327 // for very small plots, larger interval, less ticks in number ticks 7328 else if (f<=16.0) { 7329 fact = 2; 7330 } 7331 // else if (f<=20.0) { 7332 // fact = 3; 7333 // } 7334 // else if (f<=24.0) { 7335 // fact = 4; 7336 // } 7337 else { 7338 fact = 5; 7339 } 7340 7341 interval = fact * magnitude; 7342 7343 return [interval, fact, magnitude]; 7344 } 7345 7346 // Given the min and max for a dataset, return suitable endpoints 7347 // for the graphing, a good number for the number of ticks, and a
7348 // format string so that extraneous digits are not displayed. 7349 // returned is an array containing [min, max, nTicks, format] 7350 $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) { 7351 // Set to preserve EITHER min OR max. 7352 // If min is preserved, max must be free. 7353 keepMin = (keepMin === null) ? false : keepMin; 7354 keepMax = (keepMax === null || keepMin) ? false : keepMax; 7355 // if endpoints are equal try to include zero otherwise include one 7356 if (axis_min === axis_max) { 7357 axis_max = (axis_max) ? 0 : 1; 7358 } 7359 7360 scalefact = scalefact || 1.0; 7361 7362 // make sure range is positive 7363 if (axis_max < axis_min) { 7364 var a = axis_max; 7365 axis_max = axis_min; 7366 axis_min = a; 7367 } 7368 7369 var r = []; 7370 var ss = bestLinearInterval(axis_max - axis_min, scalefact); 7371 7372 var gsf = $.jqplot.getSignificantFigures; 7373 7374 if (numberTicks == null) { 7375 7376 // Figure out the axis min, max and number of ticks 7377 // the min and max will be some multiple of the tick interval, 7378 // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the 7379 // axis min is negative, 0 will be a tick. 7380 if (!keepMin && !keepMax) { 7381 r[0] = Math.floor(axis_min / ss) * ss; // min 7382 r[1] = Math.ceil(axis_max / ss) * ss; // max 7383 r[2] = Math.round((r[1]-r[0])/ss+1.0); // number of ticks 7384 r[3] = bestFormatString(ss); // format string 7385 r[4] = ss; // tick Interval 7386 } 7387 7388 else if (keepMin) { 7389 r[0] = axis_min; // min 7390 r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks 7391 r[1] = axis_min + (r[2] - 1) * ss; // max 7392 var digitsMin = gsf(axis_min).digitsRight; 7393 var digitsSS = gsf(ss).digitsRight; 7394 if (digitsMin < digitsSS) { 7395 r[3] = bestFormatString(ss); // format string 7396 } 7397 else { 7398 r[3] = '%.' + digitsMin + 'f'; 7399 } 7400 r[4] = ss; // tick Interval 7401 } 7402 7403 else if (keepMax) { 7404 r[1] = axis_max; // max 7405 r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks 7406 r[0] = axis_max - (r[2] - 1) * ss; // min 7407 var digitsMax = gsf(axis_max).digitsRight; 7408 var digitsSS = gsf(ss).digitsRight; 7409 if (digitsMax < digitsSS) { 7410 r[3] = bestFormatString(ss); // format string 7411 } 7412 else { 7413 r[3] = '%.' + digitsMax + 'f'; 7414 } 7415 r[4] = ss; // tick Interval 7416 } 7417 } 7418 7419 else { 7420 var tempr = []; 7421 7422 // Figure out the axis min, max and number of ticks 7423 // the min and max will be some multiple of the tick interval, 7424 // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the 7425 // axis min is negative, 0 will be a tick. 7426 tempr[0] = Math.floor(axis_min / ss) * ss; // min 7427 tempr[1] = Math.ceil(axis_max / ss) * ss; // max 7428 tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0); // number of ticks 7429 tempr[3] = bestFormatString(ss); // format string 7430 tempr[4] = ss; // tick Interval 7431 7432 // first, see if we happen to get the right number of ticks 7433 if (tempr[2] === numberTicks) { 7434 r = tempr; 7435 } 7436 7437 else { 7438 7439 var newti = bestInterval(tempr[1] - tempr[0], numberTicks); 7440 7441 r[0] = tempr[0]; // min 7442 r[2] = numberTicks; // number of ticks 7443 r[4] = newti; // tick interval 7444 r[3] = bestFormatString(newti);
7444 // format string 7445 r[1] = r[0] + (r[2] - 1) * r[4]; // max 7446 } 7447 } 7448 7449 return r; 7450 }; 7451 7452 $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval; 7453 $.jqplot.LinearTickGenerator.bestInterval = bestInterval; 7454 $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents; 7455 $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval; 7456 7457 7458 // class: $.jqplot.MarkerRenderer 7459 // The default jqPlot marker renderer, rendering the points on the line. 7460 $.jqplot.MarkerRenderer = function(options){ 7461 // Group: Properties 7462 7463 // prop: show 7464 // whether or not to show the marker. 7465 this.show = true; 7466 // prop: style 7467 // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare 7468 this.style = 'filledCircle'; 7469 // prop: lineWidth 7470 // size of the line for non-filled markers. 7471 this.lineWidth = 2; 7472 // prop: size 7473 // Size of the marker (diameter or circle, length of edge of square, etc.) 7474 this.size = 9.0; 7475 // prop: color 7476 // color of marker. Will be set to color of series by default on init. 7477 this.color = '#666666'; 7478 // prop: shadow 7479 // whether or not to draw a shadow on the line 7480 this.shadow = true; 7481 // prop: shadowAngle 7482 // Shadow angle in degrees 7483 this.shadowAngle = 45; 7484 // prop: shadowOffset 7485 // Shadow offset from line in pixels 7486 this.shadowOffset = 1; 7487 // prop: shadowDepth 7488 // Number of times shadow is stroked, each stroke offset shadowOffset from the last. 7489 this.shadowDepth = 3; 7490 // prop: shadowAlpha 7491 // Alpha channel transparency of shadow. 0 = transparent. 7492 this.shadowAlpha = '0.07'; 7493 // prop: shadowRenderer 7494 // Renderer that will draws the shadows on the marker. 7495 this.shadowRenderer = new $.jqplot.ShadowRenderer(); 7496 // prop: shapeRenderer 7497 // Renderer that will draw the marker. 7498 this.shapeRenderer = new $.jqplot.ShapeRenderer(); 7499 7500 $.extend(true, this, options); 7501 }; 7502 7503 $.jqplot.MarkerRenderer.prototype.init = function(options) { 7504 $.extend(true, this, options); 7505 var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true}; 7506 if (this.style.indexOf('filled') != -1) { 7507 sdopt.fill = true; 7508 } 7509 if (this.style.indexOf('ircle') != -1) { 7510 sdopt.isarc = true; 7511 sdopt.closePath = false; 7512 } 7513 this.shadowRenderer.init(sdopt); 7514 7515 var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true}; 7516 if (this.style.indexOf('filled') != -1) { 7517 shopt.fill = true; 7518 } 7519 if (this.style.indexOf('ircle') != -1) { 7520 shopt.isarc = true; 7521 shopt.closePath = false; 7522 } 7523 this.shapeRenderer.init(shopt); 7524 }; 7525 7526 $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) { 7527 var stretch = 1.2; 7528 var dx = this.size/2/stretch; 7529 var dy = this.size/2*stretch; 7530 var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]]; 7531 if (this.shadow) { 7532 this.shadowRenderer.draw(ctx, points); 7533 } 7534 this.shapeRenderer.draw(ctx, points, options); 7535 }; 7536 7537 $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) { 7538 var stretch = 1.0; 7539 var dx = this.size/2*stretch; 7540 var dy = this.size/2*stretch; 7541 var points1 = [[x, y-dy], [x, y+dy]]; 7542 var points2 = [[x+dx, y], [x-dx, y]]; 7543 var opts = $.extend(true, {}, this.options, {closePath:false}); 7544 if (this.shadow) {
7545 this.shadowRenderer.draw(ctx, points1, {closePath:false}); 7546 this.shadowRenderer.draw(ctx, points2, {closePath:false}); 7547 } 7548 this.shapeRenderer.draw(ctx, points1, opts); 7549 this.shapeRenderer.draw(ctx, points2, opts); 7550 }; 7551 7552 $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) { 7553 var stretch = 1.0; 7554 var dx = this.size/2*stretch; 7555 var dy = this.size/2*stretch; 7556 var opts = $.extend(true, {}, this.options, {closePath:false}); 7557 var points1 = [[x-dx, y-dy], [x+dx, y+dy]]; 7558 var points2 = [[x-dx, y+dy], [x+dx, y-dy]]; 7559 if (this.shadow) { 7560 this.shadowRenderer.draw(ctx, points1, {closePath:false}); 7561 this.shadowRenderer.draw(ctx, points2, {closePath:false}); 7562 } 7563 this.shapeRenderer.draw(ctx, points1, opts); 7564 this.shapeRenderer.draw(ctx, points2, opts); 7565 }; 7566 7567 $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) { 7568 var stretch = 1.0; 7569 var dx = this.size/2*stretch; 7570 var dy = this.size/2*stretch; 7571 var points = [[x-dx, y], [x+dx, y]]; 7572 if (this.shadow) { 7573 this.shadowRenderer.draw(ctx, points); 7574 } 7575 this.shapeRenderer.draw(ctx, points, options); 7576 }; 7577 7578 $.jqplot.MarkerRenderer.prototype.drawLine = function(p1, p2, ctx, fill, options) { 7579 var points = [p1, p2]; 7580 if (this.shadow) { 7581 this.shadowRenderer.draw(ctx, points); 7582 } 7583 this.shapeRenderer.draw(ctx, points, options); 7584 }; 7585 7586 $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) { 7587 var stretch = 1.0; 7588 var dx = this.size/2/stretch; 7589 var dy = this.size/2*stretch; 7590 var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]]; 7591 if (this.shadow) { 7592 this.shadowRenderer.draw(ctx, points); 7593 } 7594 this.shapeRenderer.draw(ctx, points, options); 7595 }; 7596 7597 $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) { 7598 var radius = this.size/2; 7599 var end = 2*Math.PI; 7600 var points = [x, y, radius, 0, end, true]; 7601 if (this.shadow) { 7602 this.shadowRenderer.draw(ctx, points); 7603 } 7604 this.shapeRenderer.draw(ctx, points, options); 7605 }; 7606 7607 $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) { 7608 options = options || {}; 7609 // hack here b/c shape renderer uses canvas based color style options 7610 // and marker uses css style names. 7611 if (options.show == null || options.show != false) { 7612 if (options.color && !options.fillStyle) { 7613 options.fillStyle = options.color; 7614 } 7615 if (options.color && !options.strokeStyle) { 7616 options.strokeStyle = options.color; 7617 } 7618 switch (this.style) { 7619 case 'diamond': 7620 this.drawDiamond(x,y,ctx, false, options); 7621 break; 7622 case 'filledDiamond': 7623 this.drawDiamond(x,y,ctx, true, options); 7624 break; 7625 case 'circle': 7626 this.drawCircle(x,y,ctx, false, options); 7627 break; 7628 case 'filledCircle': 7629 this.drawCircle(x,y,ctx, true, options); 7630 break; 7631 case 'square': 7632 this.drawSquare(x,y,ctx, false, options); 7633 break; 7634 case 'filledSquare': 7635 this.drawSquare(x,y,ctx, true, options); 7636 break; 7637 case 'x': 7638 this.drawX(x,y,ctx, true, options); 7639 break; 7640 case 'plus': 7641 this.drawPlus(x,y,ctx, true, options); 7642 break; 7643 case 'dash': 7644 this.drawDash(x,y,ctx, true, options); 7645 break; 7646 case 'line': 7647 this.drawLine(x, y, ctx, false, options); 7648 break; 7649 default: 7650 this.drawDiamond(x,y,ctx, false, options); 7651 break; 7652 } 7653 } 7654 }; 7655 7656 // class: $.jqplot.shadowRenderer 7657 // The default jqPlot shadow renderer, rendering shadows behind shapes. 7658 $.jqplot.ShadowRenderer = function(options){ 7659 // Group: Properties 7660 7661 // prop: angle 7662 // Angle of the shadow in degrees. Measured counter-clockwise from the x axis. 7663 this.angle = 45; 7664 // prop: offset 7665 // Pixel offset at the given shadow angle of each shadow stroke from the last stroke. 7666 this.offset = 1; 7667 // prop: alpha 7668 // alpha transparency of shadow stroke. 7669 this.alpha = 0.07; 7670 // prop: lineWidth 7671 // width of the shadow line stroke. 7672 this.lineWidth = 1.5; 7673 // prop: lineJoin 7674 // How line segments of the shadow are joined. 7675 this.lineJoin = 'miter'; 7676 // prop: lineCap 7677 // how ends of the shadow line are rendered. 7678 this.lineCap = 'round'; 7679 // prop; closePath 7680 // whether line path segment is closed upon itself. 7681 this.closePath = false; 7682 // prop: fill 7683 // whether to fill the shape. 7684 this.fill = false; 7685 // prop: depth 7686 // how many times the shadow is stroked. Each stroke will be offset by offset at angle degrees. 7687 this.depth = 3; 7688 this.strokeStyle = 'rgba(0,0,0,0.1)'; 7689 // prop: isarc 7690 // whether the shadow is an arc or not. 7691 this.isarc = false; 7692 7693 $.extend(true, this, options); 7694 }; 7695 7696 $.jqplot.ShadowRenderer.prototype.init = function(options) { 7697 $.extend(true, this, options); 7698 }; 7699 7700 // function: draw 7701 // draws an transparent black (i.e. gray) shadow. 7702 // 7703 // ctx - canvas drawing context 7704 // points - array of points or [x, y, radius, start angle (rad), end angle (rad)] 7705 $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) { 7706 ctx.save(); 7707 var opts = (options != null) ? options : {}; 7708 var fill = (opts.fill != null) ? opts.fill : this.fill; 7709 var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; 7710 var closePath = (opts.closePath != null) ? opts.closePath
7710: this.closePath; 7711 var offset = (opts.offset != null) ? opts.offset : this.offset; 7712 var alpha = (opts.alpha != null) ? opts.alpha : this.alpha; 7713 var depth = (opts.depth != null) ? opts.depth : this.depth; 7714 var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; 7715 var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; 7716 ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth; 7717 ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin; 7718 ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap; 7719 ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')'; 7720 ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')'; 7721 for (var j=0; j<depth; j++) { 7722 var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); 7723 ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset); 7724 ctxPattern.beginPath(); 7725 if (isarc) { 7726 ctx.arc(points[0], points[1], points[2], points[3], points[4], true); 7727 } 7728 else if (fillRect) { 7729 if (fillRect) { 7730 ctx.fillRect(points[0], points[1], points[2], points[3]); 7731 } 7732 } 7733 else if (points && points.length){ 7734 var move = true; 7735 for (var i=0; i<points.length; i++) { 7736 // skip to the first non-null point and move to it. 7737 if (points[i][0] != null && points[i][1] != null) { 7738 if (move) { 7739 ctxPattern.moveTo(points[i][0], points[i][1]); 7740 move = false; 7741 } 7742 else { 7743 ctxPattern.lineTo(points[i][0], points[i][1]); 7744 } 7745 } 7746 else { 7747 move = true; 7748 } 7749 } 7750 7751 } 7752 if (closePath) { 7753 ctxPattern.closePath(); 7754 } 7755 if (fill) { 7756 ctx.fill(); 7757 } 7758 else { 7759 ctx.stroke(); 7760 } 7761 } 7762 ctx.restore(); 7763 }; 7764 7765 // class: $.jqplot.shapeRenderer 7766 // The default jqPlot shape renderer. Given a set of points will 7767 // plot them and either stroke a line (fill = false) or fill them (fill = true). 7768 // If a filled shape is desired, closePath = true must also be set to close 7769 // the shape. 7770 $.jqplot.ShapeRenderer = function(options){ 7771 7772 this.lineWidth = 1.5; 7773 // prop: linePattern 7774 // line pattern 'dashed', 'dotted', 'solid', some combination 7775 // of '-' and '.' characters such as '.-.' or a numerical array like 7776 // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, 7777 // [1, 10, 20, 10] to draw a dot-dash line, and so on. 7778 this.linePattern = 'solid'; 7779 // prop: lineJoin 7780 // How line segments of the shadow are joined. 7781 this.lineJoin = 'miter'; 7782 // prop: lineCap 7783 // how ends of the shadow line are rendered. 7784 this.lineCap = 'round'; 7785 // prop; closePath 7786 // whether line path segment is closed upon itself. 7787 this.closePath = false; 7788 // prop: fill 7789 // whether to fill the shape. 7790 this.fill = false; 7791 // prop: isarc 7792 // whether the shadow is an arc or not. 7793 this.isarc = false; 7794 // prop: fillRect 7795 // true to draw shape as a filled rectangle. 7796 this.fillRect = false; 7797 // prop: strokeRect 7798 // true to draw shape as a stroked rectangle. 7799 this.strokeRect = false; 7800 // prop: clearRect 7801 // true to cear a rectangle. 7802 this.clearRect = false; 7803 // prop: strokeStyle 7804 // css color spec for the stoke style 7805 this.strokeStyle = '#999999'; 7806 // prop: fillStyle 7807 // css color spec for the fill style. 7808 this.fillStyle = '#999999'; 7809 7810 $.extend(true, this, options); 7811 }; 7812 7813 $.jqplot.ShapeRenderer.prototype.init = function(options) { 7814 $.extend(true, this, options); 7815 }; 7816 7817 // function: draw 7818 // draws the shape. 7819 // 7820 // ctx - canvas drawing context 7821 // points - array of points for shapes or 7822 // [x, y, width, height] for rectangles or 7823 // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs. 7824 $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) { 7825 ctx.save(); 7826 var opts = (options != null) ? options : {}; 7827 var fill = (opts.fill != null) ? opts.fill : this.fill; 7828 var closePath = (opts.closePath != null) ? opts.closePath
7828: this.closePath; 7829 var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; 7830 var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect; 7831 var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect; 7832 var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; 7833 var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; 7834 var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); 7835 ctx.lineWidth = opts.lineWidth || this.lineWidth; 7836 ctx.lineJoin = opts.lineJoin || this.lineJoin; 7837 ctx.lineCap = opts.lineCap || this.lineCap; 7838 ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle; 7839 ctx.fillStyle = opts.fillStyle || this.fillStyle; 7840 ctx.beginPath(); 7841 if (isarc) { 7842 ctx.arc(points[0], points[1], points[2], points[3], points[4], true); 7843 if (closePath) { 7844 ctx.closePath(); 7845 } 7846 if (fill) { 7847 ctx.fill(); 7848 } 7849 else { 7850 ctx.stroke(); 7851 } 7852 ctx.restore(); 7853 return; 7854 } 7855 else if (clearRect) { 7856 ctx.clearRect(points[0], points[1], points[2], points[3]); 7857 ctx.restore(); 7858 return; 7859 } 7860 else if (fillRect || strokeRect) { 7861 if (fillRect) { 7862 ctx.fillRect(points[0], points[1], points[2], points[3]); 7863 } 7864 if (strokeRect) { 7865 ctx.strokeRect(points[0], points[1], points[2], points[3]); 7866 ctx.restore(); 7867 return; 7868 } 7869 } 7870 else if (points && points.length){ 7871 var move = true; 7872 for (var i=0; i<points.length; i++) { 7873 // skip to the first non-null point and move to it. 7874 if (points[i][0] != null && points[i][1] != null) { 7875 if (move) { 7876 ctxPattern.moveTo(points[i][0], points[i][1]); 7877 move = false; 7878 } 7879 else { 7880 ctxPattern.lineTo(points[i][0], points[i][1]); 7881 } 7882 } 7883 else { 7884 move = true; 7885 } 7886 } 7887 if (closePath) { 7888 ctxPattern.closePath(); 7889 } 7890 if (fill) { 7891 ctx.fill(); 7892 } 7893 else { 7894 ctx.stroke(); 7895 } 7896 } 7897 ctx.restore(); 7898 }; 7899 7900 // class $.jqplot.TableLegendRenderer 7901 // The default legend renderer for jqPlot. 7902 $.jqplot.TableLegendRenderer = function(){ 7903 // 7904 }; 7905 7906 $.jqplot.TableLegendRenderer.prototype.init = function(options) { 7907 $.extend(true, this, options); 7908 }; 7909 7910 $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) { 7911 var rs = (pad) ? this.rowSpacing+'px' : '0px'; 7912 var tr; 7913 var td; 7914 var elem; 7915 var div0; 7916 var div1; 7917 elem = document.createElement('tr'); 7918 tr = $(elem); 7919 tr.addClass('jqplot-table-legend'); 7920 elem = null; 7921 7922 if (reverse){ 7923 tr.prependTo(this._elem); 7924 } 7925 7926 else{ 7927 tr.appendTo(this._elem); 7928 } 7929 7930 if (this.showSwatches) { 7931 td = $(document.createElement('td')); 7932 td.addClass('jqplot-table-legend jqplot-table-legend-swatch'); 7933 td.css({textAlign: 'center', paddingTop: rs}); 7934 7935 div0 = $(document.createElement('div')); 7936 div0.addClass('jqplot-table-legend-swatch-outline'); 7937 div1 = $(document.createElement('div')); 7938 div1.addClass('jqplot-table-legend-swatch'); 7939 div1.css({backgroundColor: color, borderColor: color}); 7940 7941 tr.append(td.append(div0.append(div1))); 7942 7943 // $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ 7944 // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+ 7945 // '</div></td>').appendTo(tr); 7946 } 7947 if (this.showLabels) { 7948 td = $(document.createElement('td')); 7949 td.addClass('jqplot-table-legend jqplot-table-legend-label'); 7950 td.css('paddingTop', rs); 7951 tr.append(td); 7952 7953 // elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
7954 // elem.appendTo(tr); 7955 if (this.escapeHtml) { 7956 td.text(label); 7957 } 7958 else { 7959 td.html(label); 7960 } 7961 } 7962 td = null; 7963 div0 = null; 7964 div1 = null; 7965 tr = null; 7966 elem = null; 7967 }; 7968 7969 // called with scope of legend 7970 $.jqplot.TableLegendRenderer.prototype.draw = function() { 7971 if (this._elem) { 7972 this._elem.emptyForce(); 7973 this._elem = null; 7974 } 7975 7976 if (this.show) { 7977 var series = this._series; 7978 // make a table. one line label per row. 7979 var elem = document.createElement('table'); 7980 this._elem = $(elem); 7981 this._elem.addClass('jqplot-table-legend'); 7982 7983 var ss = {position:'absolute'}; 7984 if (this.background) { 7985 ss['background'] = this.background; 7986 } 7987 if (this.border) { 7988 ss['border'] = this.border; 7989 } 7990 if (this.fontSize) { 7991 ss['fontSize'] = this.fontSize; 7992 } 7993 if (this.fontFamily) { 7994 ss['fontFamily'] = this.fontFamily; 7995 } 7996 if (this.textColor) { 7997 ss['textColor'] = this.textColor; 7998 } 7999 if (this.marginTop != null) { 8000 ss['marginTop'] = this.marginTop; 8001 } 8002 if (this.marginBottom != null) { 8003 ss['marginBottom'] = this.marginBottom; 8004 } 8005 if (this.marginLeft != null) { 8006 ss['marginLeft'] = this.marginLeft; 8007 } 8008 if (this.marginRight != null) { 8009 ss['marginRight'] = this.marginRight; 8010 } 8011 8012 8013 var pad = false, 8014 reverse = false, 8015 s; 8016 for (var i = 0; i< series.length; i++) { 8017 s = series[i]; 8018 if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){ 8019 reverse = true; 8020 } 8021 if (s.show && s.showLabel) { 8022 var lt = this.labels[i] || s.label.toString(); 8023 if (lt) { 8024 var color = s.color; 8025 if (reverse && i < series.length - 1){ 8026 pad = true; 8027 } 8028 else if (reverse && i == series.length - 1){ 8029 pad = false; 8030 } 8031 this.renderer.addrow.call(this, lt, color, pad, reverse); 8032 pad = true; 8033 } 8034 // let plugins add more rows to legend. Used by trend line plugin. 8035 for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) { 8036 var item = $.jqplot.addLegendRowHooks[j].call(this, s); 8037 if (item) { 8038 this.renderer.addrow.call(this, item.label, item.color, pad); 8039 pad = true; 8040 } 8041 } 8042 lt = null; 8043 } 8044 } 8045 } 8046 return this._elem; 8047 }; 8048 8049 $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) { 8050 if (this.show) { 8051 if (this.placement == 'insideGrid') { 8052 switch (this.location) { 8053 case 'nw': 8054 var a = offsets.left; 8055 var b = offsets.top; 8056 this._elem.css('left', a); 8057 this._elem.css('top', b); 8058 break; 8059 case 'n': 8060 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8061 var b = offsets.top; 8062 this._elem.css('left', a); 8063 this._elem.css('top', b); 8064 break;
8065 case 'ne': 8066 var a = offsets.right; 8067 var b = offsets.top; 8068 this._elem.css({right:a, top:b}); 8069 break; 8070 case 'e': 8071 var a = offsets.right; 8072 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8073 this._elem.css({right:a, top:b}); 8074 break; 8075 case 'se': 8076 var a = offsets.right; 8077 var b = offsets.bottom; 8078 this._elem.css({right:a, bottom:b}); 8079 break; 8080 case 's': 8081 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8082 var b = offsets.bottom; 8083 this._elem.css({left:a, bottom:b}); 8084 break; 8085 case 'sw': 8086 var a = offsets.left; 8087 var b = offsets.bottom; 8088 this._elem.css({left:a, bottom:b}); 8089 break; 8090 case 'w': 8091 var a = offsets.left; 8092 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8093 this._elem.css({left:a, top:b}); 8094 break; 8095 default: // same as 'se' 8096 var a = offsets.right; 8097 var b = offsets.bottom; 8098 this._elem.css({right:a, bottom:b}); 8099 break; 8100 } 8101 8102 } 8103 else if (this.placement == 'outside'){ 8104 switch (this.location) { 8105 case 'nw': 8106 var a = this._plotDimensions.width - offsets.left; 8107 var b = offsets.top; 8108 this._elem.css('right', a); 8109 this._elem.css('top', b); 8110 break; 8111 case 'n': 8112 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8113 var b = this._plotDimensions.height - offsets.top; 8114 this._elem.css('left', a); 8115 this._elem.css('bottom', b); 8116 break; 8117 case 'ne': 8118 var a = this._plotDimensions.width - offsets.right; 8119 var b = offsets.top; 8120 this._elem.css({left:a, top:b}); 8121 break; 8122 case 'e': 8123 var a = this._plotDimensions.width - offsets.right; 8124 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8125 this._elem.css({left:a, top:b}); 8126 break; 8127 case 'se': 8128 var a = this._plotDimensions.width - offsets.right; 8129 var b = offsets.bottom; 8130 this._elem.css({left:a, bottom:b}); 8131 break; 8132 case 's': 8133 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8134 var b = this._plotDimensions.height - offsets.bottom; 8135 this._elem.css({left:a, top:b}); 8136 break; 8137 case 'sw': 8138 var a = this._plotDimensions.width - offsets.left; 8139 var b = offsets.bottom; 8140 this._elem.css({right:a, bottom:b}); 8141 break; 8142 case 'w': 8143 var a = this._plotDimensions.width - offsets.left; 8144 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8145 this._elem.css({right:a, top:b}); 8146 break; 8147 default: // same as 'se' 8148 var a = offsets.right; 8149 var b = offsets.bottom; 8150 this._elem.css({right:a, bottom:b}); 8151 break; 8152 } 8153 } 8154 else { 8155 switch (this.location) { 8156 case 'nw': 8157 this._elem.css({left:0, top:offsets.top}); 8158 break; 8159 case 'n': 8160 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8161 this._elem.css({left: a, top:offsets.top}); 8162 break;
8163 case 'ne': 8164 this._elem.css({right:0, top:offsets.top}); 8165 break; 8166 case 'e': 8167 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8168 this._elem.css({right:offsets.right, top:b}); 8169 break; 8170 case 'se': 8171 this._elem.css({right:offsets.right, bottom:offsets.bottom}); 8172 break; 8173 case 's': 8174 var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 8175 this._elem.css({left: a, bottom:offsets.bottom}); 8176 break; 8177 case 'sw': 8178 this._elem.css({left:offsets.left, bottom:offsets.bottom}); 8179 break; 8180 case 'w': 8181 var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 8182 this._elem.css({left:offsets.left, top:b}); 8183 break; 8184 default: // same as 'se' 8185 this._elem.css({right:offsets.right, bottom:offsets.bottom}); 8186 break; 8187 } 8188 } 8189 } 8190 }; 8191 8192 /** 8193 * Class: $.jqplot.ThemeEngine 8194 * Theme Engine provides a programatic way to change some of the more 8195 * common jqplot styling options such as fonts, colors and grid options. 8196 * A theme engine instance is created with each plot. The theme engine 8197 * manages a collection of themes which can be modified, added to, or 8198 * applied to the plot. 8199 * 8200 * The themeEngine class is not instantiated directly. 8201 * When a plot is initialized, the current plot options are scanned 8202 * an a default theme named "Default" is created. This theme is 8203 * used as the basis for other themes added to the theme engine and 8204 * is always available. 8205 * 8206 * A theme is a simple javascript object with styling parameters for 8207 * various entities of the plot. A theme has the form: 8208 * 8209 * 8210 * > { 8211 * > _name:f "Default", 8212 * > target: { 8213 * > backgroundColor: "transparent" 8214 * > }, 8215 * > legend: { 8216 * > textColor: null, 8217 * > fontFamily: null, 8218 * > fontSize: null, 8219 * > border: null, 8220 * > background: null 8221 * > }, 8222 * > title: { 8223 * > textColor: "rgb(102, 102, 102)", 8224 * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif", 8225 * > fontSize: "19.2px", 8226 * > textAlign: "center" 8227 * > }, 8228 * > seriesStyles: {}, 8229 * > series: [{ 8230 * > color: "#4bb2c5", 8231 * > lineWidth: 2.5, 8232 * > linePattern: "solid", 8233 * > shadow: true, 8234 * > fillColor: "#4bb2c5", 8235 * > showMarker: true, 8236 * > markerOptions: { 8237 * > color: "#4bb2c5", 8238 * > show: true, 8239 * > style: 'filledCircle', 8240 * > lineWidth: 1.5, 8241 * > size: 4, 8242 * > shadow: true 8243 * > } 8244 * > }], 8245 * > grid: { 8246 * > drawGridlines: true, 8247 * > gridLineColor: "#cccccc", 8248 * > gridLineWidth: 1, 8249 * > backgroundColor: "#fffdf6", 8250 * > borderColor: "#999999", 8251 * > borderWidth: 2, 8252 * > shadow: true 8253 * > }, 8254 * > axesStyles: { 8255 * > label: {}, 8256 * > ticks: {} 8257 * > }, 8258 * > axes: { 8259 * > xaxis: { 8260 * > borderColor: "#999999", 8261 * > borderWidth: 2, 8262 * > ticks: { 8263 * > show: true, 8264 * > showGridline: true, 8265 * > showLabel: true, 8266 * > showMark: true, 8267 * > size: 4, 8268 * > textColor: "", 8269 * > whiteSpace: "nowrap", 8270 * > fontSize: "12px", 8271 * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif" 8272 * > }, 8273 * > label: { 8274 * > textColor: "rgb(102, 102, 102)", 8275 * > whiteSpace: "normal", 8276 * > fontSize: "14.6667px", 8277 * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif", 8278 * > fontWeight: "400" 8279 * > } 8280 * > }, 8281 * > yaxis: { 8282 * > borderColor: "#999999", 8283 * > borderWidth: 2, 8284 * > ticks: { 8285 * > show: true, 8286 * > showGridline: true, 8287 * > showLabel: true, 8288 * > showMark: true, 8289 * > size: 4, 8290 * > textColor: "", 8291 * > whiteSpace: "nowrap", 8292 * > fontSize: "12px", 8293 * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif" 8294 * > }, 8295 * > label: { 8296 * > textColor: null, 8297 * > whiteSpace: null, 8298 * > fontSize: null, 8299 * > fontFamily: null, 8300 * > fontWeight: null 8301 * > } 8302 * > }, 8303 * > x2axis: {... 8304 * > }, 8305 * > ... 8306 * > y9axis: {... 8307 * > } 8308 * > } 8309 * > } 8310 * 8311 * "seriesStyles" is a style object that will be applied to all series in the plot. 8312 * It will forcibly override any styles applied on the individual series. "axesStyles" is 8313 * a style object that will be applied to all axes in the plot. It will also forcibly 8314 * override any styles on the individual axes. 8315 * 8316 * The example shown above has series options for a line series. Options for other 8317 * series types are shown below: 8318 * 8319 * Bar Series: 8320 * 8321 * > { 8322 * > color: "#4bb2c5", 8323 * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], 8324 * > lineWidth: 2.5, 8325 * > shadow: true, 8326 * > barPadding: 2, 8327 * > barMargin: 10, 8328 * > barWidth: 15.09375, 8329 * > 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)"] 8330 * > } 8331 * 8332 * Pie Series: 8333 * 8334 * > { 8335 * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], 8336 * > padding: 20, 8337 * > sliceMargin: 0, 8338 * > fill: true, 8339 * > shadow: true, 8340 * > startAngle: 0, 8341 * > lineWidth: 2.5, 8342 * > 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)"] 8343 * > } 8344 * 8345 * Funnel Series: 8346 * 8347 * > { 8348 * > color: "#4bb2c5", 8349 * > lineWidth: 2, 8350 * > shadow: true, 8351 * > padding: { 8352 * > top: 20, 8353 * > right: 20, 8354 * > bottom: 20, 8355 * > left: 20 8356 * > }, 8357 * > sectionMargin: 6, 8358 * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], 8359 * > 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)"] 8360 * > } 8361 * 8362 */ 8363 $.jqplot.ThemeEngine = function(){ 8364 // Group: Properties 8365 // 8366 // prop: themes 8367 // hash of themes managed by the theme engine. 8368 // Indexed by theme name. 8369 this.themes = {}; 8370 // prop: activeTheme 8371 // Pointer to currently active theme 8372 this.activeTheme=null; 8373 8374 }; 8375 8376 // called with scope of plot 8377 $.jqplot.ThemeEngine.prototype.init = function() { 8378 // get the Default theme from the current plot settings. 8379 var th = new $.jqplot.Theme({_name:'Default'}); 8380 var n, i, nn; 8381 8382 for (n in th.target) { 8383 if (n == "textColor") { 8384 th.target[n] = this.target.css('color'); 8385 } 8386 else { 8387 th.target[n] = this.target.css(n);
8388 } 8389 } 8390 8391 if (this.title.show && this.title._elem) { 8392 for (n in th.title) { 8393 if (n == "textColor") { 8394 th.title[n] = this.title._elem.css('color'); 8395 } 8396 else { 8397 th.title[n] = this.title._elem.css(n); 8398 } 8399 } 8400 } 8401 8402 for (n in th.grid) { 8403 th.grid[n] = this.grid[n]; 8404 } 8405 if (th.grid.backgroundColor == null && this.grid.background != null) { 8406 th.grid.backgroundColor = this.grid.background; 8407 } 8408 if (this.legend.show && this.legend._elem) { 8409 for (n in th.legend) { 8410 if (n == 'textColor') { 8411 th.legend[n] = this.legend._elem.css('color'); 8412 } 8413 else { 8414 th.legend[n] = this.legend._elem.css(n); 8415 } 8416 } 8417 } 8418 var s; 8419 8420 for (i=0; i<this.series.length; i++) { 8421 s = this.series[i]; 8422 if (s.renderer.constructor == $.jqplot.LineRenderer) { 8423 th.series.push(new LineSeriesProperties()); 8424 } 8425 else if (s.renderer.constructor == $.jqplot.BarRenderer) { 8426 th.series.push(new BarSeriesProperties()); 8427 } 8428 else if (s.renderer.constructor == $.jqplot.PieRenderer) { 8429 th.series.push(new PieSeriesProperties()); 8430 } 8431 else if (s.renderer.constructor == $.jqplot.DonutRenderer) { 8432 th.series.push(new DonutSeriesProperties()); 8433 } 8434 else if (s.renderer.constructor == $.jqplot.FunnelRenderer) { 8435 th.series.push(new FunnelSeriesProperties()); 8436 } 8437 else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) { 8438 th.series.push(new MeterSeriesProperties()); 8439 } 8440 else { 8441 th.series.push({}); 8442 } 8443 for (n in th.series[i]) { 8444 th.series[i][n] = s[n]; 8445 } 8446 } 8447 var a, ax; 8448 for (n in this.axes) { 8449 ax = this.axes[n]; 8450 a = th.axes[n] = new AxisProperties(); 8451 a.borderColor = ax.borderColor; 8452 a.borderWidth = ax.borderWidth; 8453 if (ax._ticks && ax._ticks[0]) { 8454 for (nn in a.ticks) { 8455 if (ax._ticks[0].hasOwnProperty(nn)) { 8456 a.ticks[nn] = ax._ticks[0][nn]; 8457 } 8458 else if (ax._ticks[0]._elem){ 8459 a.ticks[nn] = ax._ticks[0]._elem.css(nn); 8460 } 8461 } 8462 } 8463 if (ax._label && ax._label.show) { 8464 for (nn in a.label) { 8465 // a.label[nn] = ax._label._elem.css(nn); 8466 if (ax._label[nn]) { 8467 a.label[nn] = ax._label[nn]; 8468 } 8469 else if (ax._label._elem){ 8470 if (nn == 'textColor') { 8471 a.label[nn] = ax._label._elem.css('color'); 8472 } 8473 else { 8474 a.label[nn] = ax._label._elem.css(nn); 8475 } 8476 } 8477 } 8478 } 8479 } 8480 this.themeEngine._add(th); 8481 this.themeEngine.activeTheme = this.themeEngine.themes[th._name]; 8482 }; 8483 /** 8484 * Group: methods 8485 * 8486 * method: get 8487 * 8488 * Get and return the named theme or the active theme if no name given. 8489 * 8490 * parameter: 8491 * 8492 * name - name of theme to get. 8493 * 8494 * returns: 8495 * 8496 * Theme instance of given name. 8497 */ 8498 $.jqplot.ThemeEngine.prototype.get = function(name) { 8499 if (!name) { 8500 // return the active theme 8501 return this.activeTheme; 8502 } 8503 else { 8504 return this.themes[name]; 8505 } 8506 }; 8507 8508 function numericalOrder(a,b) { return a-b; } 8509 8510 /** 8511 * method: getThemeNames 8512 * 8513 * Return the list of theme names in this manager in alpha-numerical order. 8514 * 8515 * parameter: 8516 * 8517 * None 8518 * 8519 * returns: 8520 * 8521 * A the list of theme names in this manager in alpha-numerical order. 8522 */ 8523 $.jqplot.ThemeEngine.prototype.getThemeNames = function() { 8524 var tn = []; 8525 for (var n in this.themes) { 8526 tn.push(n); 8527 } 8528 return tn.sort(numericalOrder); 8529 }; 8530 8531 /** 8532 * method: getThemes 8533 * 8534 * Return a list of themes in alpha-numerical order by name. 8535 * 8536 * parameter: 8537 * 8538 * None 8539 * 8540 * returns: 8541 * 8542 * A list of themes in alpha-numerical order by name. 8543 */ 8544 $.jqplot.ThemeEngine.prototype.getThemes = function() { 8545 var tn = []; 8546 var themes = []; 8547 for (var n in this.themes) { 8548 tn.push(n); 8549 } 8550 tn.sort(numericalOrder); 8551 for (var i=0; i<tn.length; i++) { 8552 themes.push(this.themes[tn[i]]); 8553 } 8554 return themes; 8555 }; 8556 8557 $.jqplot.ThemeEngine.prototype.activate = function(plot, name) { 8558 // sometimes need to redraw whole plot. 8559 var redrawPlot = false; 8560 if (!name && this.activeTheme && this.activeTheme._name) { 8561 name = this.activeTheme._name; 8562 } 8563 if (!this.themes.hasOwnProperty(name)) { 8564 throw new Error("No theme of that name"); 8565 } 8566 else { 8567 var th = this.themes[name]; 8568 this.activeTheme = th; 8569 var val, checkBorderColor = false, checkBorderWidth = false; 8570 var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis']; 8571 8572 for (i=0; i<arr.length; i++) { 8573 var ax = arr[i]; 8574 if (th.axesStyles.borderColor != null) { 8575 plot.axes[ax].borderColor = th.axesStyles.borderColor; 8576 } 8577 if (th.axesStyles.borderWidth != null) { 8578 plot.axes[ax].borderWidth = th.axesStyles.borderWidth; 8579 } 8580 } 8581 8582 for (var axname in plot.axes) { 8583 var axis = plot.axes[axname]; 8584 if (axis.show) { 8585 var thaxis = th.axes[axname] || {}; 8586 var thaxstyle = th.axesStyles; 8587 var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle); 8588 val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor; 8589 if (thax.borderColor != null) { 8590 axis.borderColor = thax.borderColor; 8591 redrawPlot = true; 8592 } 8593 val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth; 8594 if (thax.borderWidth != null) { 8595 axis.borderWidth = thax.borderWidth; 8596 redrawPlot = true; 8597 } 8598 if (axis._ticks && axis._ticks[0]) { 8599 for (var nn in thax.ticks) { 8600 // val = null; 8601 // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) { 8602 // val = th.axesStyles.ticks[nn]; 8603 // } 8604 // else if (thax.ticks[nn] != null){ 8605 // val = thax.ticks[nn] 8606 // } 8607 val = thax.ticks[nn]; 8608 if (val != null) { 8609 axis.tickOptions[nn] = val; 8610 axis._ticks = []; 8611 redrawPlot = true; 8612 } 8613 } 8614 } 8615 if (axis._label && axis._label.show) { 8616 for (var nn in thax.label) { 8617 // val = null; 8618 // if (th.axesStyles.label && th.axesStyles.label[nn] != null) { 8619 // val = th.axesStyles.label[nn]; 8620 // } 8621 // else if (thax.label && thax.label[nn] != null){ 8622 // val = thax.label[nn] 8623 // } 8624 val = thax.label[nn]; 8625 if (val != null) { 8626 axis.labelOptions[nn] = val; 8627 redrawPlot = true; 8628 } 8629 } 8630 } 8631 8632 } 8633 } 8634 8635 for (var n in th.grid) { 8636 if (th.grid[n] != null) { 8637 plot.grid[n] = th.grid[n]; 8638 } 8639 } 8640 if (!redrawPlot) { 8641 plot.grid.draw(); 8642 } 8643 8644 if (plot.legend.show) { 8645 for (n in th.legend) { 8646 if (th.legend[n] != null) { 8647 plot.legend[n] = th.legend[n]; 8648 } 8649 } 8650 } 8651 if (plot.title.show) { 8652 for (n in th.title) { 8653 if (th.title[n] != null) { 8654 plot.title[n] = th.title[n]; 8655 } 8656 } 8657 } 8658 8659 var i; 8660 for (i=0; i<th.series.length; i++) { 8661 var opts = {}; 8662 var redrawSeries = false; 8663 for (n in th.series[i]) { 8664 val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n];
8665 if (val != null) { 8666 opts[n] = val; 8667 if (n == 'color') { 8668 plot.series[i].renderer.shapeRenderer.fillStyle = val; 8669 plot.series[i].renderer.shapeRenderer.strokeStyle = val; 8670 plot.series[i][n] = val; 8671 } 8672 else if ((n == 'lineWidth') || (n == 'linePattern')) { 8673 plot.series[i].renderer.shapeRenderer[n] = val; 8674 plot.series[i][n] = val; 8675 } 8676 else if (n == 'markerOptions') { 8677 merge (plot.series[i].markerOptions, val); 8678 merge (plot.series[i].markerRenderer, val); 8679 } 8680 else { 8681 plot.series[i][n] = val; 8682 } 8683 redrawPlot = true; 8684 } 8685 } 8686 } 8687 8688 if (redrawPlot) { 8689 plot.target.empty(); 8690 plot.draw(); 8691 } 8692 8693 for (n in th.target) { 8694 if (th.target[n] != null) { 8695 plot.target.css(n, th.target[n]); 8696 } 8697 } 8698 } 8699 8700 }; 8701 8702 $.jqplot.ThemeEngine.prototype._add = function(theme, name) { 8703 if (name) { 8704 theme._name = name; 8705 } 8706 if (!theme._name) { 8707 theme._name = Date.parse(new Date()); 8708 } 8709 if (!this.themes.hasOwnProperty(theme._name)) { 8710 this.themes[theme._name] = theme; 8711 } 8712 else { 8713 throw new Error("jqplot.ThemeEngine Error: Theme already in use"); 8714 } 8715 }; 8716 8717 // method remove 8718 // Delete the named theme, return true on success, false on failure. 8719 8720 8721 /** 8722 * method: remove 8723 * 8724 * Remove the given theme from the themeEngine. 8725 * 8726 * parameters: 8727 * 8728 * name - name of the theme to remove. 8729 * 8730 * returns: 8731 * 8732 * true on success, false on failure. 8733 */ 8734 $.jqplot.ThemeEngine.prototype.remove = function(name) { 8735 if (name == 'Default') { 8736 return false; 8737 } 8738 return delete this.themes[name]; 8739 }; 8740 8741 /** 8742 * method: newTheme 8743 * 8744 * Create a new theme based on the default theme, adding it the themeEngine. 8745 * 8746 * parameters: 8747 * 8748 * name - name of the new theme. 8749 * obj - optional object of styles to be applied to this new theme. 8750 * 8751 * returns: 8752 * 8753 * new Theme object. 8754 */ 8755 $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) { 8756 if (typeof(name) == 'object') { 8757 obj = obj || name; 8758 name = null; 8759 } 8760 if (obj && obj._name) { 8761 name = obj._name; 8762 } 8763 else { 8764 name = name || Date.parse(new Date()); 8765 } 8766 // var th = new $.jqplot.Theme(name); 8767 var th = this.copy(this.themes['Default']._name, name); 8768 $.jqplot.extend(th, obj); 8769 return th; 8770 }; 8771 8772 // function clone(obj) { 8773 // return eval(obj.toSource()); 8774 // } 8775 8776 function clone(obj){ 8777 if(obj == null || typeof(obj) != 'object'){ 8778 return obj; 8779 } 8780 8781 var temp = new obj.constructor(); 8782 for(var key in obj){ 8783 temp[key] = clone(obj[key]); 8784 } 8785 return temp; 8786 } 8787 8788 $.jqplot.clone = clone; 8789 8790 function merge(obj1, obj2) { 8791 if (obj2 == null || typeof(obj2) != 'object') { 8792 return; 8793 } 8794 for (var key in obj2) { 8795 if (key == 'highlightColors') { 8796 obj1[key] = clone(obj2[key]); 8797 } 8798 if (obj2[key] != null && typeof(obj2[key]) == 'object') { 8799 if (!obj1.hasOwnProperty(key)) { 8800 obj1[key] = {}; 8801 }
8802 merge(obj1[key], obj2[key]); 8803 } 8804 else { 8805 obj1[key] = obj2[key]; 8806 } 8807 } 8808 } 8809 8810 $.jqplot.merge = merge; 8811 8812 // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic 8813 $.jqplot.extend = function() { 8814 // copy reference to target object 8815 var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; 8816 8817 // Handle a deep copy situation 8818 if ( typeof target === "boolean" ) { 8819 deep = target; 8820 target = arguments[1] || {}; 8821 // skip the boolean and the target 8822 i = 2; 8823 } 8824 8825 // Handle case when target is a string or something (possible in deep copy) 8826 if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) { 8827 target = {}; 8828 } 8829 8830 for ( ; i < length; i++ ){ 8831 // Only deal with non-null/undefined values 8832 if ( (options = arguments[ i ]) != null ) { 8833 // Extend the base object 8834 for ( var name in options ) { 8835 var src = target[ name ], copy = options[ name ]; 8836 8837 // Prevent never-ending loop 8838 if ( target === copy ) { 8839 continue; 8840 } 8841 8842 // Recurse if we're merging object values 8843 if ( deep && copy && typeof copy === "object" && !copy.nodeType ) { 8844 target[ name ] = $.jqplot.extend( deep, 8845 // Never move original objects, clone them 8846 src || ( copy.length != null ? [ ] : { } ) 8847 , copy ); 8848 } 8849 // Don't bring in undefined values 8850 else if ( copy !== undefined ) { 8851 target[ name ] = copy; 8852 } 8853 } 8854 } 8855 } 8856 // Return the modified object 8857 return target; 8858 }; 8859 8860 /** 8861 * method: rename 8862 * 8863 * Rename a theme. 8864 * 8865 * parameters: 8866 * 8867 * oldName - current name of the theme. 8868 * newName - desired name of the theme. 8869 * 8870 * returns: 8871 * 8872 * new Theme object. 8873 */ 8874 $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) { 8875 if (oldName == 'Default' || newName == 'Default') { 8876 throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default"); 8877 } 8878 if (this.themes.hasOwnProperty(newName)) { 8879 throw new Error ("jqplot.ThemeEngine Error: New name already in use."); 8880 } 8881 else if (this.themes.hasOwnProperty(oldName)) { 8882 var th = this.copy (oldName, newName); 8883 this.remove(oldName); 8884 return th; 8885 } 8886 throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid"); 8887 }; 8888 8889 /** 8890 * method: copy 8891 * 8892 * Create a copy of an existing theme in the themeEngine, adding it the themeEngine. 8893 * 8894 * parameters: 8895 * 8896 * sourceName - name of the existing theme. 8897 * targetName - name of the copy. 8898 * obj - optional object of style parameter to apply to the new theme. 8899 * 8900 * returns: 8901 * 8902 * new Theme object. 8903 */ 8904 $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) { 8905 if (targetName == 'Default') { 8906 throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme"); 8907 } 8908 if (!this.themes.hasOwnProperty(sourceName)) { 8909 var s = "jqplot.ThemeEngine Error: Source name invalid"; 8910 throw new Error(s); 8911 } 8912 if (this.themes.hasOwnProperty(targetName)) { 8913 var s = "jqplot.ThemeEngine Error: Target name invalid"; 8914 throw new Error(s); 8915 } 8916 else { 8917 var th = clone(this.themes[sourceName]); 8918 th._name = targetName; 8919 $.jqplot.extend(true, th, obj); 8920 this._add(th); 8921 return th; 8922 } 8923 }; 8924 8925 8926 $.jqplot.Theme = function(name, obj) { 8927 if (typeof(name) == 'object') { 8928 obj = obj || name; 8929 name = null; 8930 } 8931 name = name || Date.parse(new Date()); 8932 this._name = name; 8933 this.target = { 8934 backgroundColor: null 8935 }; 8936 this.legend = { 8937 textColor: null, 8938 fontFamily: null, 8939 fontSize: null, 8940 border: null, 8941 background: null 8942 }; 8943 this.title = { 8944 textColor: null, 8945 fontFamily: null, 8946 fontSize: null, 8947 textAlign: null 8948 }; 8949 this.seriesStyles = {}; 8950 this.series = []; 8951 this.grid = {
8952 drawGridlines: null, 8953 gridLineColor: null, 8954 gridLineWidth: null, 8955 backgroundColor: null, 8956 borderColor: null, 8957 borderWidth: null, 8958 shadow: null 8959 }; 8960 this.axesStyles = {label:{}, ticks:{}}; 8961 this.axes = {}; 8962 if (typeof(obj) == 'string') { 8963 this._name = obj; 8964 } 8965 else if(typeof(obj) == 'object') { 8966 $.jqplot.extend(true, this, obj); 8967 } 8968 }; 8969 8970 var AxisProperties = function() { 8971 this.borderColor = null; 8972 this.borderWidth = null; 8973 this.ticks = new AxisTicks(); 8974 this.label = new AxisLabel(); 8975 }; 8976 8977 var AxisTicks = function() { 8978 this.show = null; 8979 this.showGridline = null; 8980 this.showLabel = null; 8981 this.showMark = null; 8982 this.size = null; 8983 this.textColor = null; 8984 this.whiteSpace = null; 8985 this.fontSize = null; 8986 this.fontFamily = null; 8987 }; 8988 8989 var AxisLabel = function() { 8990 this.textColor = null; 8991 this.whiteSpace = null; 8992 this.fontSize = null; 8993 this.fontFamily = null; 8994 this.fontWeight = null; 8995 }; 8996 8997 var LineSeriesProperties = function() { 8998 this.color=null; 8999 this.lineWidth=null; 9000 this.linePattern=null; 9001 this.shadow=null; 9002 this.fillColor=null; 9003 this.showMarker=null; 9004 this.markerOptions = new MarkerOptions(); 9005 }; 9006 9007 var MarkerOptions = function() { 9008 this.show = null; 9009 this.style = null; 9010 this.lineWidth = null; 9011 this.size = null; 9012 this.color = null; 9013 this.shadow = null; 9014 }; 9015 9016 var BarSeriesProperties = function() { 9017 this.color=null; 9018 this.seriesColors=null; 9019 this.lineWidth=null; 9020 this.shadow=null; 9021 this.barPadding=null; 9022 this.barMargin=null; 9023 this.barWidth=null; 9024 this.highlightColors=null; 9025 }; 9026 9027 var PieSeriesProperties = function() { 9028 this.seriesColors=null; 9029 this.padding=null; 9030 this.sliceMargin=null; 9031 this.fill=null; 9032 this.shadow=null; 9033 this.startAngle=null; 9034 this.lineWidth=null; 9035 this.highlightColors=null; 9036 }; 9037 9038 var DonutSeriesProperties = function() { 9039 this.seriesColors=null; 9040 this.padding=null; 9041 this.sliceMargin=null; 9042 this.fill=null; 9043 this.shadow=null; 9044 this.startAngle=null; 9045 this.lineWidth=null; 9046 this.innerDiameter=null; 9047 this.thickness=null; 9048 this.ringMargin=null; 9049 this.highlightColors=null; 9050 }; 9051 9052 var FunnelSeriesProperties = function() { 9053 this.color=null; 9054 this.lineWidth=null; 9055 this.shadow=null; 9056 this.padding=null; 9057 this.sectionMargin=null; 9058 this.seriesColors=null; 9059 this.highlightColors=null; 9060 }; 9061 9062 var MeterSeriesProperties = function() { 9063 this.padding=null; 9064 this.backgroundColor=null; 9065 this.ringColor=null; 9066 this.tickColor=null; 9067 this.ringWidth=null; 9068 this.intervalColors=null; 9069 this.intervalInnerRadius=null; 9070 this.intervalOuterRadius=null; 9071 this.hubRadius=null; 9072 this.needleThickness=null; 9073 this.needlePad=null; 9074 }; 9075 9076 9077 9078 9079 $.fn.jqplotChildText = function() { 9080 return $(this).contents().filter(function() { 9081 return this.nodeType == 3; // Node.TEXT_NODE not defined in I7 9082 }).text(); 9083 }; 9084 9085 // Returns font style as abbreviation for "font" property. 9086 $.fn.jqplotGetComputedFontStyle = function() { 9087 var css = window.getComputedStyle ? window.getComputedStyle(this[0], "") : this[0].currentStyle; 9088 var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily']; 9089 var style = []; 9090 9091 for (var i=0 ; i < attrs.length; ++i) { 9092 var attr = String(css[attrs[i]]); 9093 9094 if (attr && attr != 'normal') { 9095 style.push(attr); 9096 } 9097 } 9098 return style.join(' '); 9099 }; 9100 9101 /** 9102 * Namespace: $.fn 9103 * jQuery namespace to attach functions to jQuery elements. 9104 * 9105 */ 9106 9107 $.fn.jqplotToImageCanvas = function(options) { 9108 9109 options = options || {}; 9110 var x_offset = (options.x_offset == null) ? 0 : options.x_offset; 9111 var y_offset = (options.y_offset == null) ? 0 : options.y_offset; 9112 var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor; 9113 9114 if ($(this).width() == 0 || $(this).height() == 0) { 9115 return null; 9116 } 9117 9118 // excanvas and hence IE < 9 do not support toDataURL and cannot export images. 9119 if ($.jqplot.use_excanvas) { 9120 return null; 9121 } 9122 9123 var newCanvas = document.createElement("canvas"); 9124 var h = $(this).outerHeight(true); 9125 var w = $(this).outerWidth(true); 9126 var offs = $(this).offset(); 9127 var plotleft = offs.left; 9128 var plottop = offs.top; 9129 var transx = 0, transy = 0; 9130 9131 // have to check if any elements are hanging outside of plot area before rendering, 9132 // since changing width of canvas will erase canvas. 9133 9134 var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick', 9135 'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick', 9136 'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label', 9137 'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ]; 9138 9139 var temptop, templeft, tempbottom, tempright; 9140 9141 for (var i = 0; i < clses.length; i++) { 9142 $(this).find('.'+clses[i]).each(function() { 9143 temptop = $(this).offset().top - plottop; 9144 templeft = $(this).offset().left - plotleft; 9145 tempright = templeft + $(this).outerWidth(true) + transx; 9146 tempbottom = temptop + $(this).outerHeight(true) + transy; 9147 if (templeft < -transx) { 9148 w = w - transx - templeft; 9149 transx = -templeft; 9150 } 9151 if (temptop < -transy) { 9152 h = h - transy - temptop; 9153 transy = - temptop; 9154 } 9155 if (tempright > w) { 9156 w = tempright; 9157 } 9158 if (tempbottom > h) { 9159 h = tempbottom; 9160 } 9161 }); 9162 } 9163 9164 newCanvas.width = w + Number(x_offset); 9165 newCanvas.height = h + Number(y_offset); 9166 9167 var newContext = newCanvas.getContext("2d"); 9168 9169 newContext.save(); 9170 newContext.fillStyle = backgroundColor; 9171 newContext.fillRect(0,0, newCanvas.width, newCanvas.height);
9172 newContext.restore(); 9173 9174 newContext.translate(transx, transy); 9175 newContext.textAlign = 'left'; 9176 newContext.textBaseline = 'top'; 9177 9178 function getLineheight(el) { 9179 var lineheight = parseInt($(el).css('line-height'), 10); 9180 9181 if (isNaN(lineheight)) { 9182 lineheight = parseInt($(el).css('font-size'), 10) * 1.2; 9183 } 9184 return lineheight; 9185 } 9186 9187 function writeWrappedText (el, context, text, left, top, canvasWidth) { 9188 var lineheight = getLineheight(el); 9189 var tagwidth = $(el).innerWidth(); 9190 var tagheight = $(el).innerHeight(); 9191 var words = text.split(/\s+/); 9192 var wl = words.length; 9193 var w = ''; 9194 var breaks = []; 9195 var temptop = top; 9196 var templeft = left; 9197 9198 for (var i=0; i<wl; i++) { 9199 w += words[i]; 9200 if (context.measureText(w).width > tagwidth && w.length > words[i].length) { 9201 breaks.push(i); 9202 w = ''; 9203 i--; 9204 } 9205 } 9206 if (breaks.length === 0) { 9207 // center text if necessary 9208 if ($(el).css('textAlign') === 'center') { 9209 templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; 9210 } 9211 context.fillText(text, templeft, top); 9212 } 9213 else { 9214 w = words.slice(0, breaks[0]).join(' '); 9215 // center text if necessary 9216 if ($(el).css('textAlign') === 'center') { 9217 templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; 9218 } 9219 context.fillText(w, templeft, temptop); 9220 temptop += lineheight; 9221 for (var i=1, l=breaks.length; i<l; i++) { 9222 w = words.slice(breaks[i-1], breaks[i]).join(' '); 9223 // center text if necessary 9224 if ($(el).css('textAlign') === 'center') { 9225 templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; 9226 } 9227 context.fillText(w, templeft, temptop); 9228 temptop += lineheight; 9229 } 9230 w = words.slice(breaks[i-1], words.length).join(' '); 9231 // center text if necessary 9232 if ($(el).css('textAlign') === 'center') { 9233 templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; 9234 } 9235 context.fillText(w, templeft, temptop); 9236 } 9237 9238 } 9239 9240 function _jqpToImage(el, x_offset, y_offset) { 9241 var tagname = el.tagName.toLowerCase(); 9242 var p = $(el).position(); 9243 var css = window.getComputedStyle ? window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9 9244 var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10); 9245 var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10); 9246 var w = newCanvas.width; 9247 // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth') 9248 9249 // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas. 9250 9251 if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) { 9252 $(el).children().each(function() { 9253 _jqpToImage(this, left, top); 9254 }); 9255 var text = $(el).jqplotChildText(); 9256 9257 if (text) { 9258 newContext.font = $(el).jqplotGetComputedFontStyle(); 9259 newContext.fillStyle = $(el).css('color'); 9260 9261 writeWrappedText(el, newContext, text, left, top, w); 9262 } 9263 } 9264 9265 // handle the standard table legend 9266 9267 else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) { 9268 newContext.strokeStyle = $(el).css('border-top-color'); 9269 newContext.fillStyle = $(el).css('background-color'); 9270 newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight()); 9271 if (parseInt($(el).css('border-top-width'), 10) > 0) { 9272 newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight()); 9273 } 9274 9275 // find all the swatches 9276 $(el).find('div.jqplot-table-legend-swatch-outline').each(function() { 9277 // get the first div and stroke it 9278 var elem = $(this); 9279 newContext.strokeStyle = elem.css('border-top-color'); 9280 var l = left + elem.position().left; 9281 var t = top + elem.position().top; 9282 newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHe
9282ight()); 9283 9284 // now fill the swatch 9285 9286 l += parseInt(elem.css('padding-left'), 10); 9287 t += parseInt(elem.css('padding-top'), 10); 9288 var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10); 9289 var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10); 9290 9291 var swatch = elem.children('div.jqplot-table-legend-swatch'); 9292 newContext.fillStyle = swatch.css('background-color'); 9293 newContext.fillRect(l, t, w, h); 9294 }); 9295 9296 // now add text 9297 9298 $(el).find('td.jqplot-table-legend-label').each(function(){ 9299 var elem = $(this); 9300 var l = left + elem.position().left; 9301 var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10); 9302 newContext.font = elem.jqplotGetComputedFontStyle(); 9303 newContext.fillStyle = elem.css('color'); 9304 writeWrappedText(elem, newContext, elem.text(), l, t, w); 9305 }); 9306 9307 var elem = null; 9308 } 9309 9310 else if (tagname == 'canvas') { 9311 newContext.drawImage(el, left, top); 9312 } 9313 } 9314 $(this).children().each(function() { 9315 _jqpToImage(this, x_offset, y_offset); 9316 }); 9317 return newCanvas; 9318 }; 9319 9320 // return the raw image data string. 9321 // Should work on canvas supporting browsers. 9322 $.fn.jqplotToImageStr = function(options) { 9323 var imgCanvas = $(this).jqplotToImageCanvas(options); 9324 if (imgCanvas) { 9325 return imgCanvas.toDataURL("image/png"); 9326 } 9327 else { 9328 return null; 9329 } 9330 }; 9331 9332 // return a DOM <img> element and return it. 9333 // Should work on canvas supporting browsers. 9334 $.fn.jqplotToImageElem = function(options) { 9335 var elem = document.createElement("img"); 9336 var str = $(this).jqplotToImageStr(options); 9337 elem.src = str; 9338 return elem; 9339 }; 9340 9341 // return a string for an <img> element and return it. 9342 // Should work on canvas supporting browsers. 9343 $.fn.jqplotToImageElemStr = function(options) { 9344 var str = '<img src='+$(this).jqplotToImageStr(options)+' />'; 9345 return str; 9346 }; 9347 9348 // Not guaranteed to work, even on canvas supporting browsers due to 9349 // limitations with location.href and browser support. 9350 $.fn.jqplotSaveImage = function() { 9351 var imgData = $(this).jqplotToImageStr({}); 9352 if (imgData) { 9353 window.location.href = imgData.replace("image/png", "image/octet-stream"); 9354 } 9355 9356 }; 9357 9358 // Not guaranteed to work, even on canvas supporting browsers due to 9359 // limitations with window.open and arbitrary data. 9360 $.fn.jqplotViewImage = function() { 9361 var imgStr = $(this).jqplotToImageElemStr({}); 9362 var imgData = $(this).jqplotToImageStr({}); 9363 if (imgStr) { 9364 var w = window.open(''); 9365 w.document.open("image/png"); 9366 w.document.write(imgStr); 9367 w.document.close(); 9368 w = null; 9369 } 9370 }; 9371 9372 9373 9374 9375 /** 9376 * @description 9377 * <p>Object with extended date parsing and formatting capabilities. 9378 * This library borrows many concepts and ideas from the Date Instance 9379 * Methods by Ken Snyder along with some parts of Ken's actual code.</p> 9380 * 9381 * <p>jsDate takes a different approach by not extending the built-in 9382 * Date Object, improving date parsing, allowing for multiple formatting 9383 * syntaxes and multiple and more easily expandable localization.</p> 9384 * 9385 * @author Chris Leonello 9386 * @date #date# 9387 * @version #VERSION# 9388 * @copyright (c) 2010-2015 Chris Leonello 9389 * jsDate is currently available for use in all personal or commercial projects 9390 * under both the MIT and GPL version 2.0 licenses. This means that you can 9391 * choose the license that best suits your project and use it accordingly. 9392 * 9393 * <p>Ken's original Date Instance Methods and copyright notice:</p> 9394 * <pre> 9395 * Ken Snyder (ken d snyder at gmail dot com) 9396 * 2008-09-10 9397 * version 2.0.2 (http://kendsnyder.com/sandbox/date/)
9398 * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/) 9399 * </pre> 9400 * 9401 * @class 9402 * @name jsDate 9403 * @param {String | Number | Array | Date Object | Options Object} arguments Optional arguments, either a parsable date/time string, 9404 * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], 9405 * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional. 9406 */ 9407 9408 var jsDate = function () { 9409 9410 this.syntax = jsDate.config.syntax; 9411 this._type = "jsDate"; 9412 this.proxy = new Date(); 9413 this.options = {}; 9414 this.locale = jsDate.regional.getLocale(); 9415 this.formatString = ''; 9416 this.defaultCentury = jsDate.config.defaultCentury; 9417 9418 switch ( arguments.length ) { 9419 case 0: 9420 break; 9421 case 1: 9422 // other objects either won't have a _type property or, 9423 // if they do, it shouldn't be set to "jsDate", so 9424 // assume it is an options argument. 9425 if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { 9426 var opts = this.options = arguments[0]; 9427 this.syntax = opts.syntax || this.syntax; 9428 this.defaultCentury = opts.defaultCentury || this.defaultCentury; 9429 this.proxy = jsDate.createDate(opts.date); 9430 } 9431 else { 9432 this.proxy = jsDate.createDate(arguments[0]); 9433 } 9434 break; 9435 default: 9436 var a = []; 9437 for ( var i=0; i<arguments.length; i++ ) { 9438 a.push(arguments[i]); 9439 } 9440 // this should be the current date/time? 9441 this.proxy = new Date(); 9442 this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); 9443 if ( a.slice(3).length ) { 9444 this.proxy.setHours.apply( this.proxy, a.slice(3) ); 9445 } 9446 break; 9447 } 9448 }; 9449 9450 /** 9451 * @namespace Configuration options that will be used as defaults for all instances on the page. 9452 * @property {String} defaultLocale The default locale to use [en]. 9453 * @property {String} syntax The default syntax to use [perl]. 9454 * @property {Number} defaultCentury The default centry for 2 digit dates. 9455 */ 9456 jsDate.config = { 9457 defaultLocale: 'en', 9458 syntax: 'perl', 9459 defaultCentury: 1900 9460 }; 9461 9462 /** 9463 * Add an arbitrary amount to the currently stored date 9464 * 9465 * @param {Number} number 9466 * @param {String} unit 9467 * @returns {jsDate} 9468 */ 9469 9470 jsDate.prototype.add = function(number, unit) { 9471 var factor = multipliers[unit] || multipliers.day; 9472 if (typeof factor == 'number') { 9473 this.proxy.setTime(this.proxy.getTime() + (factor * number)); 9474 } else { 9475 factor.add(this, number); 9476 } 9477 return this; 9478 }; 9479 9480 /** 9481 * Create a new jqplot.date object with the same date 9482 * 9483 * @returns {jsDate} 9484 */ 9485 9486 jsDate.prototype.clone = function() { 9487 return new jsDate(this.proxy.getTime()); 9488 }; 9489 9490 /** 9491 * Get the UTC TimeZone Offset of this date in milliseconds. 9492 * 9493 * @returns {Number} 9494 */ 9495 9496 jsDate.prototype.getUtcOffset = function() { 9497 return this.proxy.getTimezoneOffset() * 60000; 9498 }; 9499 9500 /** 9501 * Find the difference between this jsDate and another date. 9502 * 9503 * @param {String| Number| Array| jsDate Object| Date Object} dateObj 9504 * @param {String} unit 9505 * @param {Boolean} allowDecimal 9506 * @returns {Number} Number of units difference between dates. 9507 */ 9508 9509 jsDate.prototype.diff = function(dateObj, unit, allowDecimal) { 9510 // ensure we have a Date object 9511 dateObj = new jsDate(dateObj); 9512 if (dateObj === null) { 9513 return null; 9514 } 9515 // get the multiplying factor integer or factor function 9516 var factor = multipliers[unit] || multipliers.day; 9517 if (typeof factor == 'number') { 9518 // multiply 9519 var unitDiff = (this.proxy.getTime() - dateObj.proxy.getTime()) / factor; 9520 } else { 9521 // run function 9522 var unitDiff = factor.diff(this.proxy, dateObj.proxy); 9523 }
9524 // if decimals are not allowed, round toward zero 9525 return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff)); 9526 }; 9527 9528 /** 9529 * Get the abbreviated name of the current week day 9530 * 9531 * @returns {String} 9532 */ 9533 9534 jsDate.prototype.getAbbrDayName = function() { 9535 return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()]; 9536 }; 9537 9538 /** 9539 * Get the abbreviated name of the current month 9540 * 9541 * @returns {String} 9542 */ 9543 9544 jsDate.prototype.getAbbrMonthName = function() { 9545 return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()]; 9546 }; 9547 9548 /** 9549 * Get UPPER CASE AM or PM for the current time 9550 * 9551 * @returns {String} 9552 */ 9553 9554 jsDate.prototype.getAMPM = function() { 9555 return this.proxy.getHours() >= 12 ? 'PM' : 'AM'; 9556 }; 9557 9558 /** 9559 * Get lower case am or pm for the current time 9560 * 9561 * @returns {String} 9562 */ 9563 9564 jsDate.prototype.getAmPm = function() { 9565 return this.proxy.getHours() >= 12 ? 'pm' : 'am'; 9566 }; 9567 9568 /** 9569 * Get the century (19 for 20th Century) 9570 * 9571 * @returns {Integer} Century (19 for 20th century). 9572 */ 9573 jsDate.prototype.getCentury = function() { 9574 return parseInt(this.proxy.getFullYear()/100, 10); 9575 }; 9576 9577 /** 9578 * Implements Date functionality 9579 */ 9580 jsDate.prototype.getDate = function() { 9581 return this.proxy.getDate(); 9582 }; 9583 9584 /** 9585 * Implements Date functionality 9586 */ 9587 jsDate.prototype.getDay = function() { 9588 return this.proxy.getDay(); 9589 }; 9590 9591 /** 9592 * Get the Day of week 1 (Monday) thru 7 (Sunday) 9593 * 9594 * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday) 9595 */ 9596 jsDate.prototype.getDayOfWeek = function() { 9597 var dow = this.proxy.getDay(); 9598 return dow===0?7:dow; 9599 }; 9600 9601 /** 9602 * Get the day of the year 9603 * 9604 * @returns {Integer} 1 - 366, day of the year 9605 */ 9606 jsDate.prototype.getDayOfYear = function() { 9607 var d = this.proxy; 9608 var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT'); 9609 ms += d.getTimezoneOffset()*60000; 9610 d = null; 9611 return parseInt(ms/60000/60/24, 10)+1; 9612 }; 9613 9614 /** 9615 * Get the name of the current week day 9616 * 9617 * @returns {String} 9618 */ 9619 9620 jsDate.prototype.getDayName = function() { 9621 return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()]; 9622 }; 9623 9624 /** 9625 * Get the week number of the given year, starting with the first Sunday as the first week 9626 * @returns {Integer} Week number (13 for the 13th full week of the year). 9627 */ 9628 jsDate.prototype.getFullWeekOfYear = function() { 9629 var d = this.proxy; 9630 var doy = this.getDayOfYear(); 9631 var rdow = 6-d.getDay(); 9632 var woy = parseInt((doy+rdow)/7, 10); 9633 return woy; 9634 }; 9635 9636 /** 9637 * Implements Date functionality 9638 */ 9639 jsDate.prototype.getFullYear = function() { 9640 return this.proxy.getFullYear(); 9641 }; 9642 9643 /** 9644 * Get the GMT offset in hours and minutes (e.g. +06:30) 9645 * 9646 * @returns {String} 9647 */ 9648 9649 jsDate.prototype.getGmtOffset = function() { 9650 // divide the minutes offset by 60 9651 var hours = this.proxy.getTimezoneOffset() / 60; 9652 // decide if we are ahead of or behind GMT 9653 var prefix = hours < 0 ? '+' : '-'; 9654 // remove the negative sign if any 9655 hours = Math.abs(hours); 9656 // add the +/- to the padded number of hours to : to the padded minutes 9657 return prefix + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2); 9658 }; 9659 9660 /** 9661 * Implements Date functionality 9662 */ 9663 jsDate.prototype.getHours = function() { 9664 return this.proxy.getHours(); 9665 }; 9666 9667 /** 9668 * Get the current hour on a 12-hour scheme 9669 * 9670 * @returns {Integer} 9671 */ 9672 9673 jsDate.prototype.getHours12 = function() { 9674 var hours = this.proxy.getHours(); 9675 return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours); 9676 }; 9677 9678 9679 jsDate.prototype.getIsoWeek = function() { 9680 var d = this.proxy; 9681 var woy = this.getWeekOfYear(); 9682 var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay(); 9683 // First week is 01 and not 00 as in the case of %U and %W, 9684 // so we add 1 to the final result except if day 1 of the year 9685 // is a Monday (then %W returns 01). 9686 // We also need to subtract 1 if the day 1 of the year is 9687 // Friday-Sunday, so the resulting equation becomes: 9688 var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1); 9689 if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4) 9690 { 9691 idow = 1; 9692 } 9693 else if(idow === 0) 9694 { 9695 d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31')); 9696 idow = d.getIsoWeek(); 9697 } 9698 d = null; 9699 return idow; 9700 }; 9701 9702 /** 9703 * Implements Date functionality 9704 */ 9705 jsDate.prototype.getMilliseconds = function() { 9706 return this.proxy.getMilliseconds(); 9707 }; 9708 9709 /** 9710 * Implements Date functionality 9711 */ 9712 jsDate.prototype.getMinutes = function() { 9713 return this.proxy.getMinutes(); 9714 }; 9715 9716 /** 9717 * Implements Date functionality 9718 */ 9719 jsDate.prototype.getMonth = function() { 9720 return this.proxy.getMonth(); 9721 }; 9722 9723 /** 9724 * Get the name of the current month 9725 * 9726 * @returns {String} 9727 */ 9728 9729 jsDate.prototype.getMonthName = function() { 9730 return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()];
9731 }; 9732 9733 /** 9734 * Get the number of the current month, 1-12 9735 * 9736 * @returns {Integer} 9737 */ 9738 9739 jsDate.prototype.getMonthNumber = function() { 9740 return this.proxy.getMonth() + 1; 9741 }; 9742 9743 /** 9744 * Implements Date functionality 9745 */ 9746 jsDate.prototype.getSeconds = function() { 9747 return this.proxy.getSeconds(); 9748 }; 9749 9750 /** 9751 * Return a proper two-digit year integer 9752 * 9753 * @returns {Integer} 9754 */ 9755 9756 jsDate.prototype.getShortYear = function() { 9757 return this.proxy.getYear() % 100; 9758 }; 9759 9760 /** 9761 * Implements Date functionality 9762 */ 9763 jsDate.prototype.getTime = function() { 9764 return this.proxy.getTime(); 9765 }; 9766 9767 /** 9768 * Get the timezone abbreviation 9769 * 9770 * @returns {String} Abbreviation for the timezone 9771 */ 9772 jsDate.prototype.getTimezoneAbbr = function() { 9773 return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1'); 9774 }; 9775 9776 /** 9777 * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time) 9778 * 9779 * @returns {String} 9780 */ 9781 jsDate.prototype.getTimezoneName = function() { 9782 var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString()); 9783 return match[1] || match[2] || 'GMT' + this.getGmtOffset(); 9784 }; 9785 9786 /** 9787 * Implements Date functionality 9788 */ 9789 jsDate.prototype.getTimezoneOffset = function() { 9790 return this.proxy.getTimezoneOffset(); 9791 }; 9792 9793 9794 /** 9795 * Get the week number of the given year, starting with the first Monday as the first week 9796 * @returns {Integer} Week number (13 for the 13th week of the year). 9797 */ 9798 jsDate.prototype.getWeekOfYear = function() { 9799 var doy = this.getDayOfYear(); 9800 var rdow = 7 - this.getDayOfWeek(); 9801 var woy = parseInt((doy+rdow)/7, 10); 9802 return woy; 9803 }; 9804 9805 /** 9806 * Get the current date as a Unix timestamp 9807 * 9808 * @returns {Integer} 9809 */ 9810 9811 jsDate.prototype.getUnix = function() { 9812 return Math.round(this.proxy.getTime() / 1000, 0); 9813 }; 9814 9815 /** 9816 * Implements Date functionality 9817 */ 9818 jsDate.prototype.getYear = function() { 9819 return this.proxy.getYear(); 9820 }; 9821 9822 /** 9823 * Return a date one day ahead (or any other unit) 9824 * 9825 * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond 9826 * @returns {jsDate} 9827 */ 9828 9829 jsDate.prototype.next = function(unit) { 9830 unit = unit || 'day'; 9831 return this.clone().add(1, unit); 9832 }; 9833 9834 /** 9835 * Set the jsDate instance to a new date. 9836 * 9837 * @param {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments, 9838 * either a parsable date/time string, 9839 * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], 9840 * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional. 9841 */ 9842 jsDate.prototype.set = function() { 9843 switch ( arguments.length ) { 9844 case 0: 9845 this.proxy = new Date(); 9846 break; 9847 case 1: 9848 // other objects either won't have a _type property or, 9849 // if they do, it shouldn't be set to "jsDate", so 9850 // assume it is an options argument. 9851 if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { 9852 var opts = this.options = arguments[0]; 9853 this.syntax = opts.syntax || this.syntax; 9854 this.defaultCentury = opts.defaultCentury || this.defaultCentury; 9855 this.proxy = jsDate.createDate(opts.date); 9856 } 9857 else { 9858 this.proxy = jsDate.createDate(arguments[0]); 9859 } 9860 break; 9861 default: 9862 var a = []; 9863 for ( var i=0; i<arguments.length; i++ ) { 9864 a.push(arguments[i]); 9865 } 9866 // this should be the current date/time 9867 this.proxy = new Date(); 9868 this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); 9869 if ( a.slice(3).length ) { 9870 this.proxy.setHours.apply( this.proxy, a.slice(3) ); 9871 } 9872 break; 9873 } 9874 return this; 9875 }; 9876 9877 /** 9878 * Sets the day of the month for a specified date according to local time. 9879 * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month. 9880 */ 9881 jsDate.prototype.setDate = function(n) { 9882 this.proxy.setDate(n); 9883 return this; 9884 }; 9885 9886 /** 9887 * Sets the full year for a specified date according to local time. 9888 * @param {Integer} yearValue The numeric value of the year, for example, 1995. 9889 * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December. 9890 * @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. 9891 */ 9892 jsDate.prototype.setFullYear = function() { 9893 this.proxy.setFullYear.apply(this.proxy, arguments); 9894 return this; 9895 }; 9896 9897 /** 9898 * Sets the hours for a specified date according to local time. 9899 * 9900 * @param {Integer} hoursValue An integer between 0 and 23, representing the hour. 9901 * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes. 9902 * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds. 9903 * If you specify the secondsValue parameter, you must also specify the minutesValue. 9904 * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds. 9905 * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue. 9906 */ 9907 jsDate.prototype.setHours = function() { 9908 this.proxy.setHours.apply(this.proxy, arguments); 9909 return this; 9910 }; 9911 9912 /** 9913 * Implements Date functionality 9914 */ 9915 jsDate.prototype.setMilliseconds = function(n) { 9916 this.proxy.setMilliseconds(n); 9917 return this; 9918 }; 9919 9920 /** 9921 * Implements Date functionality 9922 */ 9923 jsDate.prototype.setMinutes = function() { 9924 this.proxy.setMinutes.apply(this.proxy, arguments); 9925 return this; 9926 }; 9927 9928 /** 9929 * Implements Date functionality 9930 */ 9931 jsDate.prototype.setMonth = function() { 9932 this.proxy.setMonth.apply(this.proxy, arguments); 9933 return this; 9934 }; 9935 9936 /** 9937 * Implements Date functionality 9938 */ 9939 jsDate.prototype.setSeconds = function() { 9940 this.proxy.setSeconds.apply(this.proxy, arguments); 9941 return this; 9942 }; 9943 9944 /** 9945 * Implements Date functionality 9946 */ 9947 jsDate.prototype.setTime = function(n) { 9948 this.proxy.setTime(n); 9949 return this; 9950 }; 9951 9952 /** 9953 * Implements Date functionality 9954 */ 9955 jsDate.prototype.setYear = function() { 9956 this.proxy.setYear.apply(this.proxy, arguments); 9957 return this; 9958 }; 9959 9960 /** 9961 * Provide a formatted string representation of this date. 9962 * 9963 * @param {String}
9963 formatString A format string. 9964 * See: {@link jsDate.formats}. 9965 * @returns {String} Date String. 9966 */ 9967 9968 jsDate.prototype.strftime = function(formatString) { 9969 formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString']; 9970 return jsDate.strftime(this, formatString, this.syntax); 9971 }; 9972 9973 /** 9974 * Return a String representation of this jsDate object. 9975 * @returns {String} Date string. 9976 */ 9977 9978 jsDate.prototype.toString = function() { 9979 return this.proxy.toString(); 9980 }; 9981 9982 /** 9983 * Convert the current date to an 8-digit integer (%Y%m%d) 9984 * 9985 * @returns {Integer} 9986 */ 9987 9988 jsDate.prototype.toYmdInt = function() { 9989 return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate(); 9990 }; 9991 9992 /** 9993 * @namespace Holds localizations for month/day names. 9994 * <p>jsDate attempts to detect locale when loaded and defaults to 'en'. 9995 * If a localization is detected which is not available, jsDate defaults to 'en'. 9996 * Additional localizations can be added after jsDate loads. After adding a localization, 9997 * call the jsDate.regional.getLocale() method. Currently, en, fr and de are defined.</p> 9998 * 9999 * <p>Localizations must be an object and have the following properties defined: monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p> 10000 * <pre class="code"> 10001 * jsDate.regional['en'] = { 10002 * monthNames : 'January February March April May June July August September October November December'.split(' '), 10003 * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), 10004 * dayNames : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), 10005 * dayNamesShort : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ') 10006 * }; 10007 * </pre> 10008 * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the 10009 * new localizations.</p> 10010 */ 10011 10012 jsDate.regional = { 10013 'en': { 10014 monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], 10015 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 10016 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 10017 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], 10018 formatString: '%Y-%m-%d %H:%M:%S' 10019 }, 10020 10021 'fr': { 10022 monthNames: ['Janvier','Fรà¸vrier','Mars','Avril','Mai','Juin','Juillet','Aoรà¸t','Septembre','Octobre','Novembre','Dรà¸cembre'], 10023 monthNamesShort: ['Jan','Fรà¸v','Mar','Avr','Mai','Jun','Jul','Aoรà¸','Sep','Oct','Nov','Dรà¸c'], 10024 dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], 10025 dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], 10026 formatString: '%Y-%m-%d %H:%M:%S' 10027 }, 10028 10029 'de': { 10030 monthNames: ['Januar','Februar','Mรà¸rz','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'], 10031 monthNamesShort: ['Jan','Feb','Mรà¸r','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'], 10032 dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], 10033 dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], 10034 formatString: '%Y-%m-%d %H:%M:%S' 10035 }, 10036 10037 'es': { 10038 monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], 10039 monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'], 10040 dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], 10041 dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], 10042 formatString: '%Y-%m-%d %H:%M:%S' 10043 }, 10044 10045 'ru': { 10046 monthNames: ['ะà¸à¸°à¸à¸°à¸à¸°à¸à¸±â¬à¸±Â','ะà¸à¸°à¸à¸°à¸à¸±â¬à¸°à¸à¸°à¸à¸±Â','ะÂะà¸à¸±â¬à¸±Â','ะ
10046Âะà¸à¸±â¬à¸°à¸à¸°à¸à¸±Â','ะÂะà¸à¸°à¸','ะÂัÂะà¸à¸±Â','ะÂัÂะà¸à¸±Â','ะÂะà¸à¸°à¸à¸±ÂัÂัÂ','ะà¸à¸°à¸à¸°à¸à¸±ÂัÂะà¸à¸±â¬à¸±Â','ะÂะà¸à¸±ÂัÂะà¸à¸±â¬à¸±Â','ะÂะà¸à¸±Âะà¸à¸±â¬à¸±Â','ะâะà¸à¸°à¸à¸°à¸à¸°à¸à¸±â¬à¸±Â'], 10047 monthNamesShort: ['ะà¸à¸°à¸à¸°à¸','ะà¸à¸°à¸à¸°à¸','ะÂะà¸à¸±â¬','ะÂะà¸à¸±â¬','ะÂะà¸à¸°à¸','ะÂัÂะà¸','ะÂัÂะà¸','ะÂะà¸à¸°à¸','ะà¸à¸°à¸à¸°à¸','ะÂะà¸à¸±Â','ะÂะà¸à¸±Â','ะâะà¸à¸°à¸'], 10048 dayNames: ['ะà¸à¸°à¸à¸±Âะà¸à¸±â¬à¸°à¸à¸±Âะà¸à¸°à¸à¸±Âะà¸','ะà¸à¸°à¸à¸°à¸à¸°à¸à¸°à¸à¸°à¸à¸°à¸à¸±Âะà¸à¸°à¸à¸°à¸','ะà¸à¸±Âะà¸à¸±â¬à¸°à¸à¸°à¸à¸°à¸','ัÂัâ¬à¸°à¸à¸°à¸à¸°à¸','ัÂะà¸à¸±Âะà¸à¸°à¸à¸±â¬à¸°à¸','ะà¸à¸±ÂัÂะà¸à¸°à¸à¸±Âะà¸','ัÂัÂะà¸à¸°à¸à¸°à¸à¸±Âะà¸'], 10049 dayNamesShort: ['ะà¸à¸±Âะà¸','ะà¸à¸°à¸à¸°à¸','ะà¸à¸±Âัâ¬','ัÂัâ¬à¸°à¸','ัÂัÂะà¸','ะà¸à¸±Âะà¸','ัÂะà¸à¸±Â'], 10050 formatString: '%Y-%m-%d %H:%M:%S' 10051 }, 10052 10053 'ar': { 10054 monthNames: ['ูÂุà¸à¸¹ÂูÂู ุà¸à¸¹Âุà¸à¸¸à¸à¸¹ÂูÂ', 'ุà¸à¸¸à¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¸à¸à¸¸à¸à¸¸à¸', 'ูÂูÂุà¸à¸¸à¸à¸¹Â', 'ุà¸à¸¸à¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¸à¸à¸¹Âุà¸à¸¸à¸à¸¹Â','ุà¸à¸¹â¦à¸¹Âุà¸', 'ุà¸à¸¸à¸', 'ุà¸à¸¹ÂูÂูÂูÂ', 'ุà¸à¸¸à¸à¸¸à¸à¸¹Âู ุà¸à¸¹Âุà¸à¸¹ÂูÂ', 'ุà¸à¸¸à¸à¸¸à¸à¸¹Âู ุà¸à¸¹Âุà¸à¸¸à¸à¸¹ÂูÂ', 'ูÂุà¸à¸¹ÂูÂู ุà¸à¸¹Âุà¸à¸¹ÂูÂ'], 10055 monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'], 10056 dayNames: ['ุà¸à¸¹Âุà¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¹Âุà¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¹Âุà¸à¸¸à¸à¸¹ÂูÂูÂ', 'ุà¸à¸¹Âุà¸à¸¹Âุà¸à¸¸à¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¹Âุà¸à¸¸à¸à¸¸à¸à¸¸à¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¹Âุà¸à¸¹â¦à¸¹Âุà¸', 'ุà¸à¸¹Âุà¸à¸¹â¦à¸¸à¸à¸¸à¸'], 10057 dayNamesShort: ['ุà¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¸à¸à¸¹ÂูÂูÂ', 'ุà¸à¸¹Âุà¸à¸¸à¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¸à¸à¸¸à¸à¸¸à¸à¸¸à¸à¸¸à¸', 'ุà¸à¸¹â¦à¸¹Âุà¸', 'ุà¸à¸¹â¦à¸¸à¸à¸¸à¸'], 10058 formatString: '%Y-%m-%d %H:%M:%S' 10059 }, 10060 10061 'pt': { 10062 monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], 10063 monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], 10064 dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], 10065 dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], 10066 formatString: '%Y-%m-%d %H:%M:%S' 10067 }, 10068 10069 'pt-BR': { 10070 monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], 10071 monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], 10072 dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], 10073 dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], 10074 formatString: '%Y-%m-%d %H:%M:%S' 10075 }, 10076 10077 'pl': { 10078 monthNames: ['StyczeลÂ','Luty','Marzec','KwiecieลÂ','Maj','Czerwiec','Lipiec','SierpieลÂ','WrzesieลÂ','Paลà¸dziernik','Listopad','GrudzieลÂ'], 10079 monthNamesShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze','Lip', 'Sie', 'Wrz', 'Paลà¸', 'Lis', 'Gru'], 10080 dayNames: ['Niedziela', 'PoniedziaลÂek', 'Wtorek', 'ลÂroda', 'Czwartek', 'Piฤâ¦tek', 'Sobota'], 10081 dayNamesShort: ['Ni', 'Pn', 'Wt', 'ลÂr', 'Cz', 'Pt', 'Sb'], 10082 formatString: '%Y-%m-%d %H:%M:%S' 10083 }, 10084 10085 'nl': { 10086 monthNames: ['Januari','Februari','Maart','April','Mei','Juni','July','Augustus','September','Oktober','November','December'], 10087 monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Aug','Sep','Okt','N
10087ov','Dec'], 10088 dayNames:','['Zondag','Maandag','Dinsdag','Woensdag','Donderdag','Vrijdag','Zaterdag'], 10089 dayNamesShort: ['Zo','Ma','Di','Wo','Do','Vr','Za'], 10090 formatString: '%Y-%m-%d %H:%M:%S' 10091 }, 10092 10093 'sv': { 10094 monthNames: ['januari','februari','mars','april','maj','juni','juli','augusti','september','oktober','november','december'], 10095 monthNamesShort: ['jan','feb','mar','apr','maj','jun','jul','aug','sep','okt','nov','dec'], 10096 dayNames: ['sรà¸ndag','mรภndag','tisdag','onsdag','torsdag','fredag','lรà¸rdag'], 10097 dayNamesShort: ['sรà¸n','mรภn','tis','ons','tor','fre','lรà¸r'], 10098 formatString: '%Y-%m-%d %H:%M:%S' 10099 }, 10100 10101 'it': { 10102 monthNames: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno','Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'], 10103 monthNamesShort: ['Gen','Feb','Mar','Apr','Mag','Giu','Lug','Ago','Set','Ott','Nov','Dic'], 10104 dayNames: ['Domenica','Lunedi','Martedi','Mercoledi','Giovedi','Venerdi','Sabato'], 10105 dayNamesShort: ['Dom','Lun','Mar','Mer','Gio','Ven','Sab'], 10106 formatString: '%d-%m-%Y %H:%M:%S' 10107 } 10108 10109 }; 10110 10111 // Set english variants to 'en' 10112 jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en']; 10113 10114 /** 10115 * Try to determine the users locale based on the lang attribute of the html page. Defaults to 'en' 10116 * if it cannot figure out a locale of if the locale does not have a localization defined. 10117 * @returns {String} locale 10118 */ 10119 10120 jsDate.regional.getLocale = function () { 10121 var l = jsDate.config.defaultLocale; 10122 10123 if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) { 10124 l = document.getElementsByTagName('html')[0].lang; 10125 if (!jsDate.regional.hasOwnProperty(l)) { 10126 l = jsDate.config.defaultLocale; 10127 } 10128 } 10129 10130 return l; 10131 }; 10132 10133 // ms in day 10134 var day = 24 * 60 * 60 * 1000; 10135 10136 // padd a number with zeros 10137 var addZeros = function(num, digits) { 10138 num = String(num); 10139 var i = digits - num.length; 10140 var s = String(Math.pow(10, i)).slice(1); 10141 return s.concat(num); 10142 }; 10143 10144 // representations used for calculating differences between dates. 10145 // This borrows heavily from Ken Snyder's work. 10146 var multipliers = { 10147 millisecond: 1, 10148 second: 1000, 10149 minute: 60 * 1000, 10150 hour: 60 * 60 * 1000, 10151 day: day, 10152 week: 7 * day, 10153 month: { 10154 // add a number of months 10155 add: function(d, number) { 10156 // add any years needed (increments of 12) 10157 multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12)); 10158 // ensure that we properly wrap betwen December and January 10159 // 11 % 12 = 11 10160 // 12 % 12 = 0 10161 var prevMonth = d.getMonth() + (number % 12); 10162 if (prevMonth == 12) { 10163 prevMonth = 0; 10164 d.setYear(d.getFullYear() + 1); 10165 } else if (prevMonth == -1) { 10166 prevMonth = 11; 10167 d.setYear(d.getFullYear() - 1); 10168 } 10169 d.setMonth(prevMonth); 10170 }, 10171 // get the number of months between two Date objects (decimal to the nearest day) 10172 diff: function(d1, d2) { 10173 // get the number of years 10174 var diffYears = d1.getFullYear() - d2.getFullYear(); 10175 // get the number of remaining months 10176 var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12); 10177 // get the number of remaining days 10178 var diffDays = d1.getDate() - d2.getDate(); 10179 // return the month difference with the days difference as a decimal 10180 return diffMonths + (diffDays / 30); 10181 } 10182 }, 10183 year: { 10184 // add a number of years 10185 add: function(d, number) { 10186 d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number)); 10187 }, 10188 // get the number of years between two Date objects (decimal to the nearest day) 10189 diff: function(d1, d2) { 10190 return multipliers.month.diff(d1, d2) / 12; 10191 } 10192 } 10193 }; 10194 // 10195 // Alias each multiplier with an 's' to allow 'year' and 'years' for example. 10196 // This comes from Ken Snyders work. 10197 // 10198 for (var unit in multipliers) { 10199 if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :| 10200 multipliers[unit + 's'] = multipliers[unit]; 10201 } 10202 } 10203 10204 // 10205 // take a jsDate instance and a format code and return the formatted value. 10206 // This is a somewhat modified version of Ken Snyder's method. 10207 // 10208 var format = function(d, code, syntax) { 10209 // if shorcut codes are used, recursively expand those. 10210 if (jsDate.formats[syntax]["shortcuts"][code]) { 10211 return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax); 10212 } else { 10213 // get the format code function and addZeros() argument 10214 var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.'); 10215 var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : ''; 10216 if (getter[1]) { 10217 nbr = addZeros(nbr, getter[1]); 10218 } 10219 return nbr; 10220 } 10221 }; 10222 10223 /** 10224 * @static 10225 * Static function for convert a date to a string according to a given format. Also acts as namespace for strftime format codes. 10226 * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p> 10227 * <pre class="code"> 10228 * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S'); 10229 * </pre> 10230 * @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. 10231 * @param {String} formatString String with embedded date formatting codes. 10232 * See: {@link jsDate.formats}. 10233 * @param {String} syntax Optional syntax to use [default perl]. 10234 * @param {String} locale Optional locale to use. 10235 * @returns {String} Formatted representation of the date. 10236 */ 10237 // 10238 // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods. 10239 // 10240 jsDate.strftime = function(d, formatString, syntax, locale) { 10241 var syn = 'perl'; 10242 var loc = jsDate.regional.getLocale(); 10243 10244 // check if syntax and locale are available or reversed 10245 if (syntax && jsDate.formats.hasOwnProperty(syntax)) { 10246 syn = syntax; 10247 } 10248 else if (syntax && jsDate.regional.hasOwnProperty(syntax)) { 10249 loc = syntax; 10250 } 10251 10252 if (locale && jsDate.formats.hasOwnProperty(locale)) { 10253 syn = locale; 10254 } 10255 else if (locale && jsDate.regional.hasOwnProperty(locale)) { 10256 loc = locale; 10257 } 10258 10259 if (get_type(d) != "[object Object]" || d._type != "jsDate") { 10260 d = new jsDate(d); 10261 d.locale = loc; 10262 } 10263 if (!formatString) { 10264 formatString = d.formatString || jsDate.regional[loc]['formatString']; 10265 }
10266 // default the format string to year-month-day 10267 var source = formatString || '%Y-%m-%d', 10268 result = '', 10269 match; 10270 // replace each format code 10271 while (source.length > 0) { 10272 if (match = source.match(jsDate.formats[syn].codes.matcher)) { 10273 result += source.slice(0, match.index); 10274 result += (match[1] || '') + format(d, match[2], syn); 10275 source = source.slice(match.index + match[0].length); 10276 } else { 10277 result += source; 10278 source = ''; 10279 } 10280 } 10281 return result; 10282 }; 10283 10284 /** 10285 * @namespace 10286 * Namespace to hold format codes and format shortcuts. "perl" and "php" format codes 10287 * and shortcuts are defined by default. Additional codes and shortcuts can be 10288 * added like: 10289 * 10290 * <pre class="code"> 10291 * jsDate.formats["perl"] = { 10292 * "codes": { 10293 * matcher: /someregex/, 10294 * Y: "fullYear", // name of "get" method without the "get", 10295 * ..., // more codes 10296 * }, 10297 * "shortcuts": { 10298 * F: '%Y-%m-%d', 10299 * ..., // more shortcuts 10300 * } 10301 * }; 10302 * </pre> 10303 * 10304 * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via: 10305 * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code> 10306 */ 10307 10308 jsDate.formats = { 10309 ISO:'%Y-%m-%dT%H:%M:%S.%N%G', 10310 SQL:'%Y-%m-%d %H:%M:%S' 10311 }; 10312 10313 /** 10314 * Perl format codes and shortcuts for strftime. 10315 * 10316 * A hash (object) of codes where each code must be an array where the first member is 10317 * the name of a Date.prototype or jsDate.prototype function to call 10318 * and optionally a second member indicating the number to pass to addZeros() 10319 * 10320 * <p>The following format codes are defined:</p> 10321 * 10322 * <pre class="code"> 10323 * Code Result Description 10324 * == Years == 10325 * %Y 2008 Four-digit year 10326 * %y 08 Two-digit year 10327 * 10328 * == Months == 10329 * %m 09 Two-digit month 10330 * %#m 9 One or two-digit month 10331 * %B September Full month name 10332 * %b Sep Abbreviated month name 10333 * 10334 * == Days == 10335 * %d 05 Two-digit day of month 10336 * %#d 5 One or two-digit day of month 10337 * %e 5 One or two-digit day of month 10338 * %A Sunday Full name of the day of the week 10339 * %a Sun Abbreviated name of the day of the week 10340 * %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday) 10341 * 10342 * == Hours == 10343 * %H 23 Hours in 24-hour format (two digits) 10344 * %#H 3 Hours in 24-hour integer format (one or two digits) 10345 * %I 11 Hours in 12-hour format (two digits) 10346 * %#I 3 Hours in 12-hour integer format (one or two digits) 10347 * %p PM AM or PM 10348 * 10349 * == Minutes == 10350 * %M 09 Minutes (two digits) 10351 * %#M 9 Minutes (one or two digits) 10352 * 10353 * == Seconds == 10354 * %S 02 Seconds (two digits) 10355 * %#S 2 Seconds (one or two digits) 10356 * %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00) 10357 * 10358 * == Milliseconds == 10359 * %N 008 Milliseconds (three digits) 10360 * %#N 8 Milliseconds (one to three digits) 10361 * 10362 * == Timezone == 10363 * %O 360 difference in minutes between local time and GMT 10364 * %Z Mountain Standard Time Name of timezone as reported by browser 10365 * %G 06:00 Hours and minutes between GMT 10366 * 10367 * == Shortcuts == 10368 * %F 2008-03-26 %Y-%m-%d 10369 * %T 05:06:30 %H:%M:%S 10370 * %X 05:06:30 %H:%M:%S 10371 * %x 03/26/08 %m/%d/%y 10372 * %D 03/26/08 %m/%d/%y 10373 * %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y 10374 * %v 3-Sep-2008 %e-%b-%Y 10375 * %R 15:31 %H:%M 10376 * %r 03:31:00 PM %I:%M:%S %p 10377 * 10378 * == Characters == 10379 * %n \n Newline 10380 * %t \t Tab 10381 * %% % Percent Symbol 10382 * </pre> 10383 * 10384 * <p>Formatting shortcuts that will be translated into their longer version. 10385 * Be sure that format shortcuts do not refer to themselves: this will cause an infinite loop.</p> 10386 * 10387 * <p>Format codes and format shortcuts can be redefined after the jsDate 10388 * module is imported.</p> 10389 * 10390 * <p>Note that if you redefine the whole hash (object), you must supply a "matcher" 10391 * regex for the parser. The default matcher is:</p> 10392 * 10393 * <code>/()%(#?(%|[a-z]))/i</code> 10394 * 10395 * <p>which corresponds to the Perl syntax used by default.</p> 10396 * 10397 * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p> 10398 */ 10399 10400 jsDate.formats.perl = { 10401 codes: { 10402 // 10403 // 2-part regex matcher for format codes 10404 // 10405 // first match must be the character before the code (to account for escaping) 10406 // second match must be the format code character(s) 10407 // 10408 matcher: /()%(#?(%|[a-z]))/i, 10409 // year 10410 Y: 'FullYear', 10411 y: 'ShortYear.2', 10412 // month 10413 m: 'MonthNumber.2', 10414 '#m': 'MonthNumber', 10415 B: 'MonthName', 10416 b: 'AbbrMonthName', 10417 // day 10418 d: 'Date.2', 10419 '#d': 'Date', 10420 e: 'Date', 10421 A: 'DayName', 10422 a: 'AbbrDayName', 10423 w: 'Day', 10424 // hours 10425 H: 'Hours.2', 10426 '#H': 'Hours', 10427 I: 'Hours12.2', 10428 '#I': 'Hours12', 10429 p: 'AMPM', 10430 // minutes 10431 M: 'Minutes.2', 10432 '#M': 'Minutes', 10433 // seconds 10434 S: 'Seconds.2', 10435 '#S': 'Seconds', 10436 s: 'Unix', 10437 // milliseconds 10438 N: 'Milliseconds.3', 10439 '#N': 'Milliseconds', 10440 // timezone 10441 O: 'TimezoneOffset', 10442 Z: 'TimezoneName', 10443 G: 'GmtOffset' 10444 }, 10445 10446 shortcuts: { 10447 // date 10448 F: '%Y-%m-%d', 10449 // time 10450 T: '%H:%M:%S', 10451 X: '%H:%M:%S', 10452 // local format date 10453 x: '%m/%d/%y', 10454 D: '%m/%d/%y', 10455 // local format extended 10456 '#c': '%a %b %e %H:%M:%S %Y', 10457 // local format short 10458 v: '%e-%b-%Y', 10459 R: '%H:%M', 10460 r: '%I:%M:%S %p', 10461 // tab and newline 10462 t: '\t', 10463 n: '\n', 10464 '%': '%' 10465 } 10466 }; 10467 10468 /** 10469 * PHP format codes and shortcuts for strftime. 10470 * 10471 * A hash (object) of codes where each code must be an array where the first member is 10472 * the name of a Date.prototype or jsDate.prototype function to call 10473 * and optionally a second member indicating the number to pass to addZeros() 10474 * 10475 * <p>The following format codes are defined:</p> 10476 * 10477 * <pre class="code"> 10478 * Code Result Description 10479 * === Days === 10480 * %a Sun through Sat An abbreviated textual representation of the day 10481 * %A Sunday - Saturday A full textual representation of the day 10482 * %d 01 to 31 Two-digit day of the month (with leading zeros) 10483 * %e 1 to 31 Day of the month, with a space preceding single digits. 10484 * %j 001 to 366 Day of the year, 3 digits with leading zeros 10485 * %u 1 - 7 (Mon - Sun) ISO-8601 numeric representation of the day of the week 10486 * %w 0 - 6 (Sun - Sat) Numeric representation of the day of the week 10487 * 10488 * === Week === 10489 * %U 13 Full Week number, starting with the first Sunday as the first week 10490 * %V 01 through 53 ISO-8601:1988 week number, starting with the first week of the year 10491 * with at least 4 weekdays, with Monday being the start of the week 10492 * %W 46 A numeric representation of the week of the year, 10493 * starting with the first Monday as the first week 10494 * === Month === 10495 * %b Jan through Dec Abbreviated month name, based on the locale 10496 * %B January - December Full month name, based on the locale 10497 * %h Jan through Dec Abbreviated month name, based on the locale (an alias of %b) 10498 * %m 01 - 12 (Jan - Dec) Two digit representation of the month 10499 * 10500 * === Year === 10501 * %C 19 Two digit century (year/100, truncated to an integer) 10502 * %y 09 for 2009 Two digit year 10503 * %Y 2038 Four digit year 10504 * 10505 * === Time === 10506 * %H 00 through 23 Two digit representation of the hour in 24-hour format 10507 * %I 01 through 12 Two digit representation of the hour in 12-hour format 10508 * %l 1 through 12 Hour in 12-hour format, with a space preceeding single digits 10509 * %M 00 through 59 Two digit representation of the minute 10510 * %p AM/PM UPPER-CASE 'AM' or 'PM' based on the given time 10511 * %P am/pm lower-case 'am' or 'pm' based on the given time
10512 * %r 09:34:17 PM Same as %I:%M:%S %p 10513 * %R 00:35 Same as %H:%M 10514 * %S 00 through 59 Two digit representation of the second 10515 * %T 21:34:17 Same as %H:%M:%S 10516 * %X 03:59:16 Preferred time representation based on locale, without the date 10517 * %z -0500 or EST Either the time zone offset from UTC or the abbreviation 10518 * %Z -0500 or EST The time zone offset/abbreviation option NOT given by %z 10519 * 10520 * === Time and Date === 10521 * %D 02/05/09 Same as %m/%d/%y 10522 * %F 2009-02-05 Same as %Y-%m-%d (commonly used in database datestamps) 10523 * %s 305815200 Unix Epoch Time timestamp (same as the time() function) 10524 * %x 02/05/09 Preferred date representation, without the time 10525 * 10526 * === Miscellaneous === 10527 * %n --- A newline character (\n) 10528 * %t --- A Tab character (\t) 10529 * %% --- A literal percentage character (%) 10530 * </pre> 10531 */ 10532 10533 jsDate.formats.php = { 10534 codes: { 10535 // 10536 // 2-part regex matcher for format codes 10537 // 10538 // first match must be the character before the code (to account for escaping) 10539 // second match must be the format code character(s) 10540 // 10541 matcher: /()%((%|[a-z]))/i, 10542 // day 10543 a: 'AbbrDayName', 10544 A: 'DayName', 10545 d: 'Date.2', 10546 e: 'Date', 10547 j: 'DayOfYear.3', 10548 u: 'DayOfWeek', 10549 w: 'Day', 10550 // week 10551 U: 'FullWeekOfYear.2', 10552 V: 'IsoWeek.2', 10553 W: 'WeekOfYear.2', 10554 // month 10555 b: 'AbbrMonthName', 10556 B: 'MonthName', 10557 m: 'MonthNumber.2', 10558 h: 'AbbrMonthName', 10559 // year 10560 C: 'Century.2', 10561 y: 'ShortYear.2', 10562 Y: 'FullYear', 10563 // time 10564 H: 'Hours.2', 10565 I: 'Hours12.2', 10566 l: 'Hours12', 10567 p: 'AMPM', 10568 P: 'AmPm', 10569 M: 'Minutes.2', 10570 S: 'Seconds.2', 10571 s: 'Unix', 10572 O: 'TimezoneOffset', 10573 z: 'GmtOffset', 10574 Z: 'TimezoneAbbr' 10575 }, 10576 10577 shortcuts: { 10578 D: '%m/%d/%y', 10579 F: '%Y-%m-%d', 10580 T: '%H:%M:%S', 10581 X: '%H:%M:%S', 10582 x: '%m/%d/%y', 10583 R: '%H:%M', 10584 r: '%I:%M:%S %p', 10585 t: '\t', 10586 n: '\n', 10587 '%': '%' 10588 } 10589 }; 10590 // 10591 // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods. 10592 // I use his idea of a set of parsers which can be regular expressions or functions, 10593 // iterating through those, and then seeing if Date.parse() will create a date. 10594 // The parser expressions and functions are a little different and some bugs have been 10595 // worked out. Also, a lot of "pre-parsing" is done to fix implementation 10596 // variations of Date.parse() between browsers. 10597 // 10598 jsDate.createDate = function(date) { 10599 // if passing in multiple arguments, try Date constructor 10600 if (date == null) { 10601 return new Date(); 10602 } 10603 // If the passed value is already a date object, return it 10604 if (date instanceof Date) { 10605 return date; 10606 } 10607 // if (typeof date == 'number') return new Date(date * 1000); 10608 // If the passed value is an integer, interpret it as a javascript timestamp 10609 if (typeof date == 'number') { 10610 return new Date(date); 10611 } 10612 10613 // Before passing strings into Date.parse(), have to normalize them for certain conditions. 10614 // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent. 10615 // 10616 // For example: 10617 // * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century. 10618 // * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse. 10619 // * Both FF, Chrome and Opera will parse '1984/1/25' into localtime. 10620
10621 // remove leading and trailing spaces 10622 var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1'); 10623 10624 // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn] 10625 parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3"); 10626 10627 ///////// 10628 // Need to check for '15-Dec-09' also. 10629 // FF will not parse, but Chrome will. 10630 // Chrome will set date to 2009 as well. 10631 ///////// 10632 10633 // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010' 10634 parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3"); 10635 10636 // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to default century. 10637 var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i); 10638 if (match && match.length > 3) { 10639 var m3 = parseFloat(match[3]); 10640 var ny = jsDate.config.defaultCentury + m3; 10641 ny = String(ny); 10642 10643 // now replace 2 digit year with 4 digit year 10644 parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny); 10645 10646 } 10647 10648 // Check for '1/19/70 8:14PM' 10649 // where starts with mm/dd/yy or yy/mm/dd and have something after 10650 // Check if 1st postiion is greater than 31, assume it is year. 10651 // Assme all 2 digit years are 1900's. 10652 // Finally, change them into US style mm/dd/yyyy representations. 10653 match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/); 10654 10655 function h1(parsable, match) { 10656 var m1 = parseFloat(match[1]); 10657 var m2 = parseFloat(match[2]); 10658 var m3 = parseFloat(match[3]); 10659 var cent = jsDate.config.defaultCentury; 10660 var ny, nd, nm, str; 10661 10662 if (m1 > 31) { // first number is a year 10663 nd = m3; 10664 nm = m2; 10665 ny = cent + m1; 10666 } 10667 10668 else { // last number is the year 10669 nd = m2; 10670 nm = m1; 10671 ny = cent + m3; 10672 } 10673 10674 str = nm+'/'+nd+'/'+ny; 10675 10676 // now replace 2 digit year with 4 digit year 10677 return parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str); 10678 10679 } 10680 10681 if (match && match.length > 3) { 10682 parsable = h1(parsable, match); 10683 } 10684 10685 // Now check for '1/19/70' with nothing after and do as above 10686 var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/); 10687 10688 if (match && match.length > 3) { 10689 parsable = h1(parsable, match); 10690 } 10691 10692 10693 var i = 0; 10694 var length = jsDate.matchers.length; 10695 var pattern, 10696 ms, 10697 current = parsable, 10698 obj; 10699 while (i < length) { 10700 ms = Date.parse(current); 10701 if (!isNaN(ms)) { 10702 return new Date(ms); 10703 } 10704 pattern = jsDate.matchers[i]; 10705 if (typeof pattern == 'function') { 10706 obj = pattern.call(jsDate, current); 10707 if (obj instanceof Date) { 10708 return obj; 10709 } 10710 } else { 10711 current = parsable.replace(pattern[0], pattern[1]); 10712 } 10713 i++; 10714 } 10715 return NaN; 10716 }; 10717 10718 10719 /** 10720 * @static 10721 * Handy static utility function to return the number of days in a given month. 10722 * @param {Integer} year Year 10723 * @param {Integer} month Month (1-12) 10724 * @returns {Integer} Number of days in the month. 10725 */ 10726 // 10727 // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods. 10728 // 10729 jsDate.daysInMonth = function(year, month) { 10730 if (month == 2) { 10731 return new Date(year, 1, 29).getDate() == 29 ? 29 : 28; 10732 } 10733 return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month]; 10734 }; 10735 10736 10737 // 10738 // An Array of regular expressions or functions that will attempt to match the date string. 10739 // Functions are called with scope of a jsDate instance. 10740 // 10741 jsDate.matchers = [ 10742 // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date). 10743 [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'], 10744 // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date). 10745 [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'], 10746 // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part. 10747 function(str) { 10748 var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i); 10749 // opt. date hour opt. minute opt. second opt. msec opt. am or pm 10750 if (match) { 10751 if (match[1]) { 10752 var d = this.createDate(match[1]); 10753 if (isNaN(d)) { 10754 return; 10755 } 10756 } else { 10757 var d = new Date(); 10758 d.setMilliseconds(0); 10759 } 10760 var hour = parseFloat(match[2]); 10761 if (match[6]) { 10762 hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12); 10763 } 10764 d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000); 10765 return d; 10766 } 10767 else { 10768 return str; 10769 } 10770 }, 10771 // Handle ISO timestamp with time zone.
10772 function(str) { 10773 var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i); 10774 if (match) { 10775 if (match[1]) { 10776 var d = this.createDate(match[1]); 10777 if (isNaN(d)) { 10778 return; 10779 } 10780 } else { 10781 var d = new Date(); 10782 d.setMilliseconds(0); 10783 } 10784 var hour = parseFloat(match[2]); 10785 d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000); 10786 return d; 10787 } 10788 else { 10789 return str; 10790 } 10791 }, 10792 // Try to match ambiguous strings like 12/8/22. 10793 // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's). 10794 // This may be redundant with pre processing of date already performed. 10795 function(str) { 10796 var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/); 10797 if (match) { 10798 var d = new Date(); 10799 var cent = jsDate.config.defaultCentury; 10800 var m1 = parseFloat(match[1]); 10801 var m3 = parseFloat(match[3]); 10802 var ny, nd, nm; 10803 if (m1 > 31) { // first number is a year 10804 nd = m3; 10805 ny = cent + m1; 10806 } 10807 10808 else { // last number is the year 10809 nd = m1; 10810 ny = cent + m3; 10811 } 10812 10813 var nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNamesShort"]); 10814 10815 if (nm == -1) { 10816 nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNames"]); 10817 } 10818 10819 d.setFullYear(ny, nm, nd); 10820 d.setHours(0,0,0,0); 10821 return d; 10822 } 10823 10824 else { 10825 return str; 10826 } 10827 } 10828 ]; 10829 10830 // 10831 // I think John Reisig published this method on his blog, ejohn. 10832 // 10833 function inArray( elem, array ) { 10834 if ( array.indexOf ) { 10835 return array.indexOf( elem ); 10836 } 10837 10838 for ( var i = 0, length = array.length; i < length; i++ ) { 10839 if ( array[ i ] === elem ) { 10840 return i; 10841 } 10842 } 10843 10844 return -1; 10845 } 10846 10847 // 10848 // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method. 10849 // 10850 function get_type(thing){ 10851 if(thing===null) return "[object Null]"; // special case 10852 return Object.prototype.toString.call(thing); 10853 } 10854 10855 $.jsDate = jsDate; 10856 10857 10858 /** 10859 * JavaScript printf/sprintf functions. 10860 * 10861 * This code has been adapted from the publicly available sprintf methods 10862 * by Ash Searle. His original header follows: 10863 * 10864 * This code is unrestricted: you are free to use it however you like. 10865 * 10866 * The functions should work as expected, performing left or right alignment, 10867 * truncating strings, outputting numbers with a required precision etc. 10868 * 10869 * For complex cases, these functions follow the Perl implementations of 10870 * (s)printf, allowing arguments to be passed out-of-order, and to set the 10871 * precision or length of the output based on arguments instead of fixed 10872 * numbers. 10873 * 10874 * See http://perldoc.perl.org/functions/sprintf.html for more information. 10875 * 10876 * Implemented: 10877 * - zero and space-padding 10878 * - right and left-alignment, 10879 * - base X prefix (binary, octal and hex) 10880 * - positive number prefix 10881 * - (minimum) width 10882 * - precision / truncation / maximum width 10883 * - out of order arguments 10884 * 10885 * Not implemented (yet): 10886 * - vector flag 10887 * - size (bytes, words, long-words etc.) 10888 * 10889 * Will not implement: 10890 * - %n or %p (no pass-by-reference in JavaScript) 10891 * 10892 * @version 2007.04.27 10893 * @author Ash Searle 10894 * 10895 * You can see the original work and comments on his blog:
10896 * http://hexmen.com/blog/2007/03/printf-sprintf/ 10897 * http://hexmen.com/js/sprintf.js 10898 */ 10899 10900 /** 10901 * @Modifications 2009.05.26 10902 * @author Chris Leonello 10903 * 10904 * Added %p %P specifier 10905 * Acts like %g or %G but will not add more significant digits to the output than present in the input. 10906 * Example: 10907 * Format: '%.3p', Input: 0.012, Output: 0.012 10908 * Format: '%.3g', Input: 0.012, Output: 0.0120 10909 * Format: '%.4p', Input: 12.0, Output: 12.0 10910 * Format: '%.4g', Input: 12.0, Output: 12.00 10911 * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5 10912 * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5 10913 * 10914 * Example: 10915 * >>> $.jqplot.sprintf('%.2f, %d', 23.3452, 43.23) 10916 * "23.35, 43" 10917 * >>> $.jqplot.sprintf("no value: %n, decimal with thousands separator: %'d", 23.3452, 433524) 10918 * "no value: , decimal with thousands separator: 433,524" 10919 */ 10920 $.jqplot.sprintf = function() { 10921 function pad(str, len, chr, leftJustify) { 10922 var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr); 10923 return leftJustify ? str + padding : padding + str; 10924 10925 } 10926 10927 function thousand_separate(value) { 10928 var value_str = new String(value); 10929 for (var i=10; i>0; i--) { 10930 if (value_str == (value_str = value_str.replace(/^(\d+)(\d{3})/, "$1"+$.jqplot.sprintf.thousandsSeparator+"$2"))) break; 10931 } 10932 return value_str; 10933 } 10934 10935 function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) { 10936 var diff = minWidth - value.length; 10937 if (diff > 0) { 10938 var spchar = ' '; 10939 if (htmlSpace) { spchar = ' '; } 10940 if (leftJustify || !zeroPad) { 10941 value = pad(value, minWidth, spchar, leftJustify); 10942 } else { 10943 value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length); 10944 } 10945 } 10946 return value; 10947 } 10948 10949 function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) { 10950 // Note: casts negative numbers to positive ones 10951 var number = value >>> 0; 10952 prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || ''; 10953 value = prefix + pad(number.toString(base), precision || 0, '0', false); 10954 return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); 10955 } 10956 10957 function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) { 10958 if (precision != null) { 10959 value = value.slice(0, precision); 10960 } 10961 return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace); 10962 } 10963 10964 var a = arguments, i = 0, format = a[i++]; 10965 10966 return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) { 10967 if (substring == '%%') { return '%'; } 10968 10969 // parse flags 10970 var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false, thousandSeparation = false; 10971 for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) { 10972 case ' ': positivePrefix = ' '; break; 10973 case '+': positivePrefix = '+'; break; 10974 case '-': leftJustify = true; break; 10975 case '0': zeroPad = true; break; 10976 case '#': prefixBaseX = true; break; 10977 case '&': htmlSpace = true; break; 10978 case '\'': thousandSeparation = true; break; 10979 } 10980 10981 // parameters may be null, undefined, empty-string or real valued 10982 // we want to ignore null, undefined and empty-string values 10983 10984 if (!minWidth) { 10985 minWidth = 0; 10986 } 10987 else if (minWidth == '*') { 10988 minWidth = +a[i++]; 10989 } 10990 else if (minWidth.charAt(0) == '*') { 10991 minWidth = +a[minWidth.slice(1, -1)]; 10992 } 10993 else { 10994 minWidth = +minWidth; 10995 } 10996 10997 // Note: undocumented perl feature: 10998 if (minWidth < 0) { 10999 minWidth = -minWidth; 11000 leftJustify = true; 11001 } 11002 11003 if (!isFinite(minWidth)) { 11004 throw new Error('$.jqplot.sprintf: (minimum-)width must be finite'); 11005 } 11006 11007 if (!precision) { 11008 precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0); 11009 } 11010 else if (precision == '*') { 11011 precision = +a[i++]; 11012 } 11013 else if (precision.charAt(0) == '*') { 11014 precision = +a[precision.slice(1, -1)]; 11015 } 11016 else { 11017 precision = +precision; 11018 } 11019 11020 // grab value using valueIndex if required? 11021 var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++]; 11022 11023 switch (type) { 11024 case 's': { 11025 if (value == null) { 11026 return ''; 11027 } 11028 return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace); 11029 } 11030 case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace); 11031 case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace); 11032 case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); 11033 case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); 11034 case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase(); 11035 case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); 11036 case 'i': { 11037 var number = parseInt(+value, 10); 11038 if (isNaN(number)) { 11039 return ''; 11040 } 11041 var prefix = number < 0 ? '-' : positivePrefix; 11042 var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number)); 11043 value = prefix + pad(number_str, precision, '0', false); 11044 //value = prefix + pad(String(Math.abs(number)), precision, '0', false); 11045 return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); 11046 } 11047 case 'd': { 11048 var number = Math.round(+value); 11049 if (isNaN(number)) { 11050 return ''; 11051 } 11052 var prefix = number < 0 ? '-' : positivePrefix; 11053 var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number)); 11054 value = prefix + pad(number_str, precision, '0', false); 11055 return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); 11056 } 11057 case 'e': 11058 case 'E': 11059 case 'f': 11060 case 'F': 11061 case 'g': 11062 case 'G': 11063 { 11064 var number = +value; 11065 if (isNaN(number)) { 11066 return ''; 11067 } 11068 var prefix = number < 0 ? '-' : positivePrefix; 11069 var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())]; 11070 var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2]; 11071 var number_str = Math.abs(number)[method](precision); 11072 11073 // Apply the decimal mark properly by splitting the number by the 11074 // decimalMark, applying thousands separator, and then placing it 11075 // back in. 11076 var parts = number_str.toString().split('.'); 11077 parts[0] = thousandSeparation ? thousand_separate(parts[0]) : parts[0]; 11078 number_str = parts.join($.jqplot.sprintf.decimalMark); 11079 11080 value = prefix + number_str; 11081 var justified = justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform](); 11082 11083 return justified; 11084 } 11085 case 'p': 11086 case 'P': 11087 { 11088 // make sure number is a number 11089 var number = +value; 11090 if (isNaN(number)) { 11091 return ''; 11092 } 11093 var prefix = number < 0 ? '-' : positivePrefix; 11094 11095 var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/); 11096 var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : String(number).length; 11097 var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0; 11098 11099 if (Math.abs(number) < 1) { 11100 if (sd + zeros <= precision) { 11101 value = prefix + Math.abs(number).toPrecision(sd); 11102 } 11103 else { 11104 if (sd <= precision - 1) { 11105 value = prefix + Math.abs(number).toExponential(sd-1); 11106 } 11107 else { 11108 value = prefix + Math.abs(number).toExponential(precision-1); 11109 } 11110 } 11111 } 11112 else { 11113 var prec = (sd <= precision) ? sd : precision; 11114 value = prefix + Math.abs(number).toPrecision(prec); 11115 } 11116 var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2]; 11117 return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform](); 11118 } 11119 case 'n': return ''; 11120 default: return substring; 11121 } 11122 }); 11123 }; 11124 11125 $.jqplot.sprintf.thousandsSeparator = ','; 11126 // Specifies the decimal mark for floating point values. By default a period '.' 11127 // is used. If you change this value to for example a comma be sure to also 11128 // change the thousands separator or else this won't work since a simple String 11129 // replace is used (replacing all periods with the mark specified here). 11130 $.jqplot.sprintf.decimalMark = '.'; 11131 11132 $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g; 11133 11134 $.jqplot.getSignificantFigures = function(number) { 11135 var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/); 11136 // total significant digits 11137 var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length; 11138 var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0; 11139 // exponent 11140 var expn = parseInt(parts[1], 10); 11141 // digits to the left of the decimal place 11142 var dleft = (expn + 1 > 0) ? expn + 1 : 0; 11143 // digits to the right of the decimal place 11144 var dright = (sd <= dleft) ? 0 : sd - expn - 1; 11145 return {significantDigits: sd, digitsLeft: dleft, digitsRight: dright, zeros: zeros, exponent: expn} ; 11146 }; 11147 11148 $.jqplot.getPrecision = function(number) { 11149 return $.jqplot.getSignificantFigures(number).digitsRight; 11150 }; 11151 11152 11153 11154 11155 var backCompat = $.uiBackCompat !== false; 11156 11157 $.jqplot.effects = { 11158 effect: {} 11159 }; 11160 11161 // prefix used for storing data on .data() 11162 var dataSpace = "jqplot.storage."; 11163 11164 /******************************************************************************/ 11165 /*********************************** EFFECTS **********************************/ 11166 /******************************************************************************/ 11167 11168 $.extend( $.jqplot.effects, { 11169 version: "1.9pre", 11170 11171 // Saves a set of properties in a data storage 11172 save: function( element, set ) { 11173 for( var i=0; i < set.length; i++ ) { 11174 if ( set[ i ] !== null ) { 11175 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); 11176 } 11177 } 11178 }, 11179 11180 // Restores a set of previously saved properties from a data storage 11181 restore: function( element, set ) { 11182 for( var i=0; i < set.length; i++ ) { 11183 if ( set[ i ] !== null ) { 11184 element.css( set[ i ], element.data( dataSpace + set[ i ] ) ); 11185 } 11186 } 11187 }, 11188 11189 setMode: function( el, mode ) { 11190 if (mode === "toggle") { 11191 mode = el.is( ":hidden" ) ? "show" : "hide"; 11192 } 11193 return mode; 11194 }, 11195
vendor: 7,504 bytes, lines 11196-11407
11196 // Wraps the element around a wrapper that copies position properties 11197 createWrapper: function( element ) { 11198 11199 // if the element is already wrapped, return it 11200 if ( element.parent().is( ".ui-effects-wrapper" )) { 11201 return element.parent(); 11202 } 11203 11204 // wrap the element 11205 var props = { 11206 width: element.outerWidth(true), 11207 height: element.outerHeight(true), 11208 "float": element.css( "float" ) 11209 }, 11210 wrapper = $( "<div></div>" ) 11211 .addClass( "ui-effects-wrapper" ) 11212 .css({ 11213 fontSize: "100%", 11214 background: "transparent", 11215 border: "none", 11216 margin: 0, 11217 padding: 0 11218 }), 11219 // Store the size in case width/height are defined in % - Fixes #5245 11220 size = { 11221 width: element.width(), 11222 height: element.height() 11223 }, 11224 active = document.activeElement; 11225 11226 element.wrap( wrapper ); 11227 11228 // Fixes #7595 - Elements lose focus when wrapped. 11229 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { 11230 $( active ).focus(); 11231 } 11232 11233 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element 11234 11235 // transfer positioning properties to the wrapper 11236 if ( element.css( "position" ) === "static" ) { 11237 wrapper.css({ position: "relative" }); 11238 element.css({ position: "relative" }); 11239 } else { 11240 $.extend( props, { 11241 position: element.css( "position" ), 11242 zIndex: element.css( "z-index" ) 11243 }); 11244 $.each([ "top", "left", "bottom", "right" ], function(i, pos) { 11245 props[ pos ] = element.css( pos ); 11246 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { 11247 props[ pos ] = "auto"; 11248 } 11249 }); 11250 element.css({ 11251 position: "relative", 11252 top: 0, 11253 left: 0, 11254 right: "auto", 11255 bottom: "auto" 11256 }); 11257 } 11258 element.css(size); 11259 11260 return wrapper.css( props ).show(); 11261 }, 11262 11263 removeWrapper: function( element ) { 11264 var active = document.activeElement; 11265 11266 if ( element.parent().is( ".ui-effects-wrapper" ) ) { 11267 element.parent().replaceWith( element ); 11268 11269 // Fixes #7595 - Elements lose focus when wrapped. 11270 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { 11271 $( active ).focus(); 11272 } 11273 } 11274 11275 11276 return element; 11277 } 11278 }); 11279 11280 // return an effect options object for the given parameters: 11281 function _normalizeArguments( effect, options, speed, callback ) { 11282 11283 // short path for passing an effect options object: 11284 if ( $.isPlainObject( effect ) ) { 11285 return effect; 11286 } 11287 11288 // convert to an object 11289 effect = { effect: effect }; 11290 11291 // catch (effect) 11292 if ( options === undefined ) { 11293 options = {}; 11294 } 11295 11296 // catch (effect, callback) 11297 if ( $.isFunction( options ) ) { 11298 callback = options; 11299 speed = null; 11300 options = {}; 11301 } 11302 11303 // catch (effect, speed, ?) 11304 if ( $.type( options ) === "number" || $.fx.speeds[ options ]) { 11305 callback = speed; 11306 speed = options; 11307 options = {}; 11308 } 11309 11310 // catch (effect, options, callback) 11311 if ( $.isFunction( speed ) ) { 11312 callback = speed; 11313 speed = null; 11314 } 11315 11316 // add options to effect 11317 if ( options ) { 11318 $.extend( effect, options ); 11319 } 11320 11321 speed = speed || options.duration; 11322 effect.duration = $.fx.off ? 0 : typeof speed === "number" 11323 ? speed : speed in $.fx.speeds ? $.fx.speeds[ speed ] : $.fx.speeds._default; 11324 11325 effect.complete = callback || options.complete; 11326 11327 return effect; 11328 } 11329 11330 function standardSpeed( speed ) { 11331 // valid standard speeds 11332 if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) { 11333 return true; 11334 } 11335 11336 // invalid strings - treat as "normal" speed 11337 if ( typeof speed === "string" && !$.jqplot.effects.effect[ speed ] ) { 11338 // TODO: remove in 2.0 (#7115) 11339 if ( backCompat && $.jqplot.effects[ speed ] ) { 11340 return false; 11341 } 11342 return true; 11343 } 11344 11345 return false; 11346 } 11347 11348 $.fn.extend({ 11349 jqplotEffect: function( effect, options, speed, callback ) { 11350 var args = _normalizeArguments.apply( this, arguments ), 11351 mode = args.mode, 11352 queue = args.queue, 11353 effectMethod = $.jqplot.effects.effect[ args.effect ], 11354 11355 // DEPRECATED: remove in 2.0 (#7115) 11356 oldEffectMethod = !effectMethod && backCompat && $.jqplot.effects[ args.effect ]; 11357 11358 if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) { 11359 // delegate to the original method (e.g., .show()) if possible 11360 if ( mode ) { 11361 return this[ mode ]( args.duration, args.complete ); 11362 } else { 11363 return this.each( function() { 11364 if ( args.complete ) { 11365 args.complete.call( this ); 11366 } 11367 }); 11368 } 11369 } 11370 11371 function run( next ) { 11372 var elem = $( this ), 11373 complete = args.complete, 11374 mode = args.mode; 11375 11376 function done() { 11377 if ( $.isFunction( complete ) ) { 11378 complete.call( elem[0] ); 11379 } 11380 if ( $.isFunction( next ) ) { 11381 next(); 11382 } 11383 } 11384 11385 // if the element is hiddden and mode is hide, 11386 // or element is visible and mode is show 11387 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { 11388 done(); 11389 } else { 11390 effectMethod.call( elem[0], args, done ); 11391 } 11392 } 11393 11394 // TODO: remove this check in 2.0, effectMethod will always be true 11395 if ( effectMethod ) { 11396 return queue === false ? this.each( run ) : this.queue( queue || "fx", run ); 11397 } else { 11398 // DEPRECATED: remove in 2.0 (#7115) 11399 return oldEffectMethod.call(this, { 11400 options: args, 11401 duration: args.duration, 11402 callback: args.complete, 11403 mode: args.mode 11404 }); 11405 } 11406 } 11407 });
11408 11409 11410 11411 11412 var rvertical = /up|down|vertical/, 11413 rpositivemotion = /up|left|vertical|horizontal/; 11414 11415 $.jqplot.effects.effect.blind = function( o, done ) { 11416 // Create element 11417 var el = $( this ), 11418 props = [ "position", "top", "bottom", "left", "right", "height", "width" ], 11419 mode = $.jqplot.effects.setMode( el, o.mode || "hide" ), 11420 direction = o.direction || "up", 11421 vertical = rvertical.test( direction ), 11422 ref = vertical ? "height" : "width", 11423 ref2 = vertical ? "top" : "left", 11424 motion = rpositivemotion.test( direction ), 11425 animation = {}, 11426 show = mode === "show", 11427 wrapper, distance, top; 11428 11429 // // if already wrapped, the wrapper's properties are my property. #6245 11430 if ( el.parent().is( ".ui-effects-wrapper" ) ) { 11431 $.jqplot.effects.save( el.parent(), props ); 11432 } else { 11433 $.jqplot.effects.save( el, props ); 11434 } 11435 el.show(); 11436 top = parseInt(el.css('top'), 10); 11437 wrapper = $.jqplot.effects.createWrapper( el ).css({ 11438 overflow: "hidden" 11439 }); 11440 11441 distance = vertical ? wrapper[ ref ]() + top : wrapper[ ref ](); 11442 11443 animation[ ref ] = show ? String(distance) : '0'; 11444 if ( !motion ) { 11445 el 11446 .css( vertical ? "bottom" : "right", 0 ) 11447 .css( vertical ? "top" : "left", "" ) 11448 .css({ position: "absolute" }); 11449 animation[ ref2 ] = show ? '0' : String(distance); 11450 } 11451 11452 // // start at 0 if we are showing 11453 if ( show ) { 11454 wrapper.css( ref, 0 ); 11455 if ( ! motion ) { 11456 wrapper.css( ref2, distance ); 11457 } 11458 } 11459 11460 // // Animate 11461 wrapper.animate( animation, { 11462 duration: o.duration, 11463 easing: o.easing, 11464 queue: false, 11465 complete: function() { 11466 if ( mode === "hide" ) { 11467 el.hide(); 11468 } 11469 $.jqplot.effects.restore( el, props ); 11470 $.jqplot.effects.removeWrapper( el ); 11471 done(); 11472 } 11473 }); 11474 11475 }; 11476 11477})(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.