1/* 2Axis Labels Plugin for flot. 3http://github.com/markrcote/flot-axislabels 4 5Original code is Copyright (c) 2010 Xuan Luo. 6Original code was released under the GPLv3 license by Xuan Luo, September 2010. 7Original code was rereleased under the MIT license by Xuan Luo, April 2012. 8 9Permission is hereby granted, free of charge, to any person obtaining 10a copy of this software and associated documentation files (the 11"Software"), to deal in the Software without restriction, including 12without limitation the rights to use, copy, modify, merge, publish, 13distribute, sublicense, and/or sell copies of the Software, and to 14permit persons to whom the Software is furnished to do so, subject to 15the following conditions: 16 17The above copyright notice and this permission notice shall be 18included in all copies or substantial portions of the Software. 19 20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 */ 28 29(function ($) { 30 var options = { 31 axisLabels: { 32 show: true 33 } 34 }; 35 36 function canvasSupported() { 37 return !!document.createElement('canvas').getContext; 38 } 39 40 function canvasTextSupported() { 41 if (!canvasSupported()) { 42 return false; 43 } 44 var dummy_canvas = document.createElement('canvas'); 45 var context = dummy_canvas.getContext('2d'); 46 return typeof context.fillText == 'function'; 47 } 48 49 function css3TransitionSupported() { 50 var div = document.createElement('div'); 51 return typeof div.style.MozTransition != 'undefined' // Gecko 52 || typeof div.style.OTransition != 'undefined' // Opera 53 || typeof div.style.webkitTransition != 'undefined' // WebKit 54 || typeof div.style.transition != 'undefined'; 55 } 56 57 58 function AxisLabel(axisName, position, padding, plot, opts) { 59 this.axisName = axisName; 60 this.position = position; 61 this.padding = padding; 62 this.plot = plot; 63 this.opts = opts; 64 this.width = 0; 65 this.height = 0; 66 } 67 68 AxisLabel.prototype.cleanup = function() { 69 }; 70 71 72 CanvasAxisLabel.prototype = new AxisLabel(); 73 CanvasAxisLabel.prototype.constructor = CanvasAxisLabel; 74 function CanvasAxisLabel(axisName, position, padding, plot, opts) { 75 AxisLabel.prototype.constructor.call(this, axisName, position, padding, 76 plot, opts); 77 } 78 79 CanvasAxisLabel.prototype.calculateSize = function() { 80 if (!this.opts.axisLabelFontSizePixels) 81 this.opts.axisLabelFontSizePixels = 14; 82 if (!this.opts.axisLabelFontFamily) 83 this.opts.axisLabelFontFamily = 'sans-serif'; 84 85 var textWidth = this.opts.axisLabelFontSizePixels + this.padding; 86 var textHeight = this.opts.axisLabelFontSizePixels + this.padding; 87 if (this.position == 'left' || this.position == 'right') { 88 this.width = this.opts.axisLabelFontSizePixels + this.padding; 89 this.height = 0; 90 } else { 91 this.width = 0; 92 this.height = this.opts.axisLabelFontSizePixels + this.padding; 93 } 94 }; 95 96 CanvasAxisLabel.prototype.draw = function(box) { 97 if (!this.opts.axisLabelColour) 98 this.opts.axisLabelColour = 'black'; 99 var ctx = this.plot.getCanvas().getContext('2d'); 100 ctx.save(); 101 ctx.font = this.opts.axisLabelFontSizePixels + 'px ' + 102 this.opts.axisLabelFontFamily; 103 ctx.fillStyle = this.opts.axisLabelColour; 104 var width = ctx.measureText(this.opts.axisLabel).width; 105 var height = this.opts.axisLabelFontSizePixels; 106 var x, y, angle = 0; 107 if (this.position == 'top') { 108 x = box.left + box.width/2 - width/2; 109 y = box.top + height*0.72; 110 } else if (this.position == 'bottom') { 111 x = box.left + box.width/2 - width/2; 112 y = box.top + box.height - height*0.72; 113 } else if (this.position == 'left') { 114 x = box.left + height*0.72; 115 y = box.height/2 + box.top + width/2; 116 angle = -Math.PI/2; 117 } else if (this.position == 'right') { 118 x = box.left + box.width - height*0.72; 119 y = box.height/2 + box.top - width/2; 120 angle = Math.PI/2; 121 } 122 ctx.translate(x, y); 123 ctx.rotate(angle);
124 ctx.fillText(this.opts.axisLabel, 0, 0); 125 ctx.restore(); 126 }; 127 128 129 HtmlAxisLabel.prototype = new AxisLabel(); 130 HtmlAxisLabel.prototype.constructor = HtmlAxisLabel; 131 function HtmlAxisLabel(axisName, position, padding, plot, opts) { 132 AxisLabel.prototype.constructor.call(this, axisName, position, 133 padding, plot, opts); 134 this.elem = null; 135 } 136 137 HtmlAxisLabel.prototype.calculateSize = function() { 138 var elem = $('<div class="axisLabels" style="position:absolute;">' + 139 this.opts.axisLabel + '</div>'); 140 this.plot.getPlaceholder().append(elem); 141 // store height and width of label itself, for use in draw() 142 this.labelWidth = elem.outerWidth(true); 143 this.labelHeight = elem.outerHeight(true); 144 elem.remove(); 145 146 this.width = this.height = 0; 147 if (this.position == 'left' || this.position == 'right') { 148 this.width = this.labelWidth + this.padding; 149 } else { 150 this.height = this.labelHeight + this.padding; 151 } 152 }; 153 154 HtmlAxisLabel.prototype.cleanup = function() { 155 if (this.elem) { 156 this.elem.remove(); 157 } 158 }; 159 160 HtmlAxisLabel.prototype.draw = function(box) { 161 this.plot.getPlaceholder().find('#' + this.axisName + 'Label').remove(); 162 this.elem = $('<div id="' + this.axisName + 163 'Label" " class="axisLabels" style="position:absolute;">' 164 + this.opts.axisLabel + '</div>'); 165 this.plot.getPlaceholder().append(this.elem); 166 if (this.position == 'top') { 167 this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 + 168 'px'); 169 this.elem.css('top', box.top + 'px'); 170 } else if (this.position == 'bottom') { 171 this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 + 172 'px'); 173 this.elem.css('top', box.top + box.height - this.labelHeight + 174 'px'); 175 } else if (this.position == 'left') { 176 this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 + 177 'px'); 178 this.elem.css('left', box.left + 'px'); 179 } else if (this.position == 'right') { 180 this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 + 181 'px'); 182 this.elem.css('left', box.left + box.width - this.labelWidth + 183 'px'); 184 } 185 }; 186 187 188 CssTransformAxisLabel.prototype = new HtmlAxisLabel(); 189 CssTransformAxisLabel.prototype.constructor = CssTransformAxisLabel; 190 function CssTransformAxisLabel(axisName, position, padding, plot, opts) { 191 HtmlAxisLabel.prototype.constructor.call(this, axisName, position, 192 padding, plot, opts); 193 } 194 195 CssTransformAxisLabel.prototype.calculateSize = function() { 196 HtmlAxisLabel.prototype.calculateSize.call(this); 197 this.width = this.height = 0; 198 if (this.position == 'left' || this.position == 'right') { 199 this.width = this.labelHeight + this.padding; 200 } else { 201 this.height = this.labelHeight + this.padding; 202 } 203 }; 204 205 CssTransformAxisLabel.prototype.transforms = function(degrees, x, y) { 206 var stransforms = { 207 '-moz-transform': '', 208 '-webkit-transform': '', 209 '-o-transform': '', 210 '-ms-transform': '' 211 }; 212 if (x != 0 || y != 0) { 213 var stdTranslate = ' translate(' + x + 'px, ' + y + 'px)'; 214 stransforms['-moz-transform'] += stdTranslate; 215 stransforms['-webkit-transform'] += stdTranslate; 216 stransforms['-o-transform'] += stdTranslate; 217 stransforms['-ms-transform'] += stdTranslate; 218 } 219 if (degrees != 0) { 220 var rotation = degrees / 90; 221 var stdRotate = ' rotate(' + degrees + 'deg)'; 222 stransforms['-moz-transform'] += stdRotate; 223 stransforms['-webkit-transform'] += stdRotate; 224 stransforms['-o-transform'] += stdRotate; 225 stransforms['-ms-transform'] += stdRotate; 226 } 227 var s = 'top: 0; left: 0; '; 228 for (var prop in stransforms) { 229 if (stransforms[prop]) { 230 s += prop + ':' + stransforms[prop] + ';'; 231 } 232 } 233 s += ';'; 234 return s; 235 }; 236 237 CssTransformAxisLabel.prototype.calculateOffsets = function(box) { 238 var offsets = { x: 0, y: 0, degrees: 0 }; 239 if (this.position == 'bottom') { 240 offsets.x = box.left + box.width/2 - this.labelWidth/2; 241 offsets.y = box.top + box.height - this.labelHeight; 242 } else if (this.position == 'top') { 243 offsets.x = box.left + box.width/2 - this.labelWidth/2; 244 offsets.y = box.top; 245 } else if (this.position == 'left') { 246 offsets.degrees = -90; 247 offsets.x = box.left - this.labelWidth/2 + this.labelHeight/2; 248 offsets.y = box.height/2 + box.top; 249 } else if (this.position == 'right') { 250 offsets.degrees = 90; 251 offsets.x = box.left + box.width - this.labelWidth/2 252 - this.labelHeight/2; 253 offsets.y = box.height/2 + box.top; 254 } 255 offsets.x = Math.round(offsets.x); 256 offsets.y = Math.round(offsets.y); 257 258 return offsets; 259 }; 260 261 CssTransformAxisLabel.prototype.draw = function(box) { 262 this.plot.getPlaceholder().find("." + this.axisName + "Label").remove(); 263 var offsets = this.calculateOffsets(box); 264 this.elem = $('<div class="axisLabels ' + this.axisName + 265 'Label" style="position:absolute; ' + 266 this.transforms(offsets.degrees, offsets.x, offsets.y) + 267 '">' + this.opts.axisLabel + '</div>'); 268 this.plot.getPlaceholder().append(this.elem); 269 }; 270 271 272 IeTransformAxisLabel.prototype = new CssTransformAxisLabel(); 273 IeTransformAxisLabel.prototype.constructor = IeTransformAxisLabel; 274 function IeTransformAxisLabel(axisName, position, padding, plot, opts) { 275 CssTransformAxisLabel.prototype.constructor.call(this, axisName, 276 position, padding, 277 plot, opts); 278 this.requiresResize = false;
279 } 280 281 IeTransformAxisLabel.prototype.transforms = function(degrees, x, y) { 282 // I didn't feel like learning the crazy Matrix stuff, so this uses 283 // a combination of the rotation transform and CSS positioning. 284 var s = ''; 285 if (degrees != 0) { 286 var rotation = degrees/90; 287 while (rotation < 0) { 288 rotation += 4; 289 } 290 s += ' filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=' + rotation + '); '; 291 // see below 292 this.requiresResize = (this.position == 'right'); 293 } 294 if (x != 0) { 295 s += 'left: ' + x + 'px; '; 296 } 297 if (y != 0) { 298 s += 'top: ' + y + 'px; '; 299 } 300 return s; 301 }; 302 303 IeTransformAxisLabel.prototype.calculateOffsets = function(box) { 304 var offsets = CssTransformAxisLabel.prototype.calculateOffsets.call( 305 this, box); 306 // adjust some values to take into account differences between 307 // CSS and IE rotations. 308 if (this.position == 'top') { 309 // FIXME: not sure why, but placing this exactly at the top causes 310 // the top axis label to flip to the bottom... 311 offsets.y = box.top + 1; 312 } else if (this.position == 'left') { 313 offsets.x = box.left; 314 offsets.y = box.height/2 + box.top - this.labelWidth/2; 315 } else if (this.position == 'right') { 316 offsets.x = box.left + box.width - this.labelHeight; 317 offsets.y = box.height/2 + box.top - this.labelWidth/2; 318 } 319 return offsets; 320 }; 321 322 IeTransformAxisLabel.prototype.draw = function(box) { 323 CssTransformAxisLabel.prototype.draw.call(this, box); 324 if (this.requiresResize) { 325 this.elem = this.plot.getPlaceholder().find("." + this.axisName + 326 "Label"); 327 // Since we used CSS positioning instead of transforms for 328 // translating the element, and since the positioning is done 329 // before any rotations, we have to reset the width and height 330 // in case the browser wrapped the text (specifically for the 331 // y2axis). 332 this.elem.css('width', this.labelWidth); 333 this.elem.css('height', this.labelHeight); 334 } 335 }; 336 337 338 function init(plot) { 339 plot.hooks.processOptions.push(function (plot, options) { 340 341 if (!options.axisLabels.show) 342 return; 343 344 // This is kind of a hack. There are no hooks in Flot between 345 // the creation and measuring of the ticks (setTicks, measureTickLabels 346 // in setupGrid() ) and the drawing of the ticks and plot box 347 // (insertAxisLabels in setupGrid() ). 348 // 349 // Therefore, we use a trick where we run the draw routine twice: 350 // the first time to get the tick measurements, so that we can change 351 // them, and then have it draw it again. 352 var secondPass = false; 353 354 var axisLabels = {}; 355 var axisOffsetCounts = { left: 0, right: 0, top: 0, bottom: 0 }; 356 357 var defaultPadding = 2; // padding between axis and tick labels 358 plot.hooks.draw.push(function (plot, ctx) { 359 var hasAxisLabels = false; 360 if (!secondPass) { 361 // MEASURE AND SET OPTIONS 362 $.each(plot.getAxes(), function(axisName, axis) { 363 var opts = axis.options // Flot 0.7 364 || plot.getOptions()[axisName]; // Flot 0.6 365 366 // Handle redraws initiated outside of this plug-in. 367 if (axisName in axisLabels) { 368 axis.labelHeight = axis.labelHeight - 369 axisLabels[axisName].height; 370 axis.labelWidth = axis.labelWidth - 371 axisLabels[axisName].width; 372 opts.labelHeight = axis.labelHeight; 373 opts.labelWidth = axis.labelWidth; 374 axisLabels[axisName].cleanup(); 375 delete axisLabels[axisName]; 376 } 377 378 if (!opts || !opts.axisLabel || !axis.show) 379 return; 380 381 hasAxisLabels = true; 382 var renderer = null; 383 384 if (!opts.axisLabelUseHtml && 385 navigator.appName == 'Microsoft Internet Explorer') { 386 var ua = navigator.userAgent; 387 var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); 388 if (re.exec(ua) != null) { 389 rv = parseFloat(RegExp.$1); 390 } 391 if (rv >= 9 && !opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { 392 renderer = CssTransformAxisLabel; 393 } else if (!opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { 394 renderer = IeTransformAxisLabel; 395 } else if (opts.axisLabelUseCanvas) { 396 renderer = CanvasAxisLabel; 397 } else { 398 renderer = HtmlAxisLabel; 399 } 400 } else { 401 if (opts.axisLabelUseHtml || (!css3TransitionSupported() && !canvasTextSupported()) && !opts.axisLabelUseCanvas) { 402 renderer = HtmlAxisLabel; 403 } else if (opts.axisLabelUseCanvas || !css3TransitionSupported()) { 404 renderer = CanvasAxisLabel; 405 } else { 406 renderer = CssTransformAxisLabel; 407 } 408 } 409 410 var padding = opts.axisLabelPadding === undefined ? 411 defaultPadding : opts.axisLabelPadding; 412 413 axisLabels[axisName] = new renderer(axisName, 414 axis.position, padding, 415 plot, opts); 416 417 // flot interprets axis.labelHeight and .labelWidth as 418 // the height and width of the tick labels. We increase 419 // these values to make room for the axis label and 420 // padding. 421 422 axisLabels[axisName].calculateSize(); 423 424 // AxisLabel.height and .width are the size of the 425 // axis label and padding. 426 // Just set opts here because axis will be sorted out on 427 // the redraw. 428 429 opts.labelHeight = axis.labelHeight + 430 axisLabels[axisName].height; 431 opts.labelWidth = axis.labelWidth + 432 axisLabels[axisName].width; 433 }); 434
435 // If there are axis labels, re-draw with new label widths and 436 // heights. 437 438 if (hasAxisLabels) { 439 secondPass = true; 440 plot.setupGrid(); 441 plot.draw(); 442 } 443 } else { 444 secondPass = false; 445 // DRAW 446 $.each(plot.getAxes(), function(axisName, axis) { 447 var opts = axis.options // Flot 0.7 448 || plot.getOptions()[axisName]; // Flot 0.6 449 if (!opts || !opts.axisLabel || !axis.show) 450 return; 451 452 axisLabels[axisName].draw(axis.box); 453 }); 454 } 455 }); 456 }); 457 } 458 459 460 $.plot.plugins.push({ 461 init: init, 462 options: options, 463 name: 'axisLabels', 464 version: '2.0' 465 }); 466})(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.