[ Index ]

PHP Cross Reference of vtigercrm-6.1.0

title

Body

[close]

/libraries/jquery/jqplot/plugins/ -> jqplot.categoryAxisRenderer.js (source)

   1  /**
   2   * jqPlot
   3   * Pure JavaScript plotting plugin using jQuery
   4   *
   5   * Version: 1.0.2
   6   * Revision: 1108
   7   *
   8   * Copyright (c) 2009-2011 Chris Leonello
   9   * jqPlot is currently available for use in all personal or commercial projects 
  10   * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
  11   * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
  12   * choose the license that best suits your project and use it accordingly. 
  13   *
  14   * Although not required, the author would appreciate an email letting him 
  15   * know of any substantial use of jqPlot.  You can reach the author at: 
  16   * chris at jqplot dot com or see http://www.jqplot.com/info.php .
  17   *
  18   * If you are feeling kind and generous, consider supporting the project by
  19   * making a donation at: http://www.jqplot.com/donate.php .
  20   *
  21   * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
  22   *
  23   *     version 2007.04.27
  24   *     author Ash Searle
  25   *     http://hexmen.com/blog/2007/03/printf-sprintf/
  26   *     http://hexmen.com/js/sprintf.js
  27   *     The author (Ash Searle) has placed this code in the public domain:
  28   *     "This code is unrestricted: you are free to use it however you like."
  29   * 
  30   */
  31  (function($) {   
  32      /**
  33      *  class: $.jqplot.CategoryAxisRenderer
  34      *  A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series.
  35      *  
  36      *  To use this renderer, include the plugin in your source
  37      *  > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script>
  38      *  
  39      *  and supply the appropriate options to your plot
  40      *  
  41      *  > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}}
  42      **/
  43      $.jqplot.CategoryAxisRenderer = function(options) {
  44          $.jqplot.LinearAxisRenderer.call(this);
  45          // prop: sortMergedLabels
  46          // True to sort tick labels when labels are created by merging
  47          // x axis values from multiple series.  That is, say you have
  48          // two series like:
  49          // > line1 = [[2006, 4],            [2008, 9], [2009, 16]];
  50          // > line2 = [[2006, 3], [2007, 7], [2008, 6]];
  51          // If no label array is specified, tick labels will be collected
  52          // from the x values of the series.  With sortMergedLabels
  53          // set to true, tick labels will be:
  54          // > [2006, 2007, 2008, 2009]
  55          // With sortMergedLabels set to false, tick labels will be:
  56          // > [2006, 2008, 2009, 2007]
  57          //
  58          // Note, this property is specified on the renderOptions for the 
  59          // axes when creating a plot:
  60          // > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}}
  61          this.sortMergedLabels = false;
  62      };
  63      
  64      $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
  65      $.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer;
  66      
  67      $.jqplot.CategoryAxisRenderer.prototype.init = function(options){
  68          this.groups = 1;
  69          this.groupLabels = [];
  70          this._groupLabels = [];
  71          this._grouped = false;
  72          this._barsPerGroup = null;
  73          // prop: tickRenderer
  74          // A class of a rendering engine for creating the ticks labels displayed on the plot, 
  75          // See <$.jqplot.AxisTickRenderer>.
  76          // this.tickRenderer = $.jqplot.AxisTickRenderer;
  77          // this.labelRenderer = $.jqplot.AxisLabelRenderer;
  78          $.extend(true, this, {tickOptions:{formatString:'%d'}}, options);
  79          var db = this._dataBounds;
  80          // Go through all the series attached to this axis and find
  81          // the min/max bounds for this axis.
  82          for (var i=0; i<this._series.length; i++) {
  83              var s = this._series[i];
  84              if (s.groups) {
  85                  this.groups = s.groups;
  86              }
  87              var d = s.data;
  88              
  89              for (var j=0; j<d.length; j++) { 
  90                  if (this.name == 'xaxis' || this.name == 'x2axis') {
  91                      if (d[j][0] < db.min || db.min == null) {
  92                          db.min = d[j][0];
  93                      }
  94                      if (d[j][0] > db.max || db.max == null) {
  95                          db.max = d[j][0];
  96                      }
  97                  }              
  98                  else {
  99                      if (d[j][1] < db.min || db.min == null) {
 100                          db.min = d[j][1];
 101                      }
 102                      if (d[j][1] > db.max || db.max == null) {
 103                          db.max = d[j][1];
 104                      }
 105                  }              
 106              }
 107          }
 108          
 109          if (this.groupLabels.length) {
 110              this.groups = this.groupLabels.length;
 111          }
 112      };
 113   
 114  
 115      $.jqplot.CategoryAxisRenderer.prototype.createTicks = function() {
 116          // we're are operating on an axis here
 117          var ticks = this._ticks;
 118          var userTicks = this.ticks;
 119          var name = this.name;
 120          // databounds were set on axis initialization.
 121          var db = this._dataBounds;
 122          var dim, interval;
 123          var min, max;
 124          var pos1, pos2;
 125          var tt, i;
 126  
 127          // if we already have ticks, use them.
 128          if (userTicks.length) {
 129              // adjust with blanks if we have groups
 130              if (this.groups > 1 && !this._grouped) {
 131                  var l = userTicks.length;
 132                  var skip = parseInt(l/this.groups, 10);
 133                  var count = 0;
 134                  for (var i=skip; i<l; i+=skip) {
 135                      userTicks.splice(i+count, 0, ' ');
 136                      count++;
 137                  }
 138                  this._grouped = true;
 139              }
 140              this.min = 0.5;
 141              this.max = userTicks.length + 0.5;
 142              var range = this.max - this.min;
 143              this.numberTicks = 2*userTicks.length + 1;
 144              for (i=0; i<userTicks.length; i++){
 145                  tt = this.min + 2 * i * range / (this.numberTicks-1);
 146                  // need a marker before and after the tick
 147                  var t = new this.tickRenderer(this.tickOptions);
 148                  t.showLabel = false;
 149                  // t.showMark = true;
 150                  t.setTick(tt, this.name);
 151                  this._ticks.push(t);
 152                  var t = new this.tickRenderer(this.tickOptions);
 153                  t.label = userTicks[i];
 154                  // t.showLabel = true;
 155                  t.showMark = false;
 156                  t.showGridline = false;
 157                  t.setTick(tt+0.5, this.name);
 158                  this._ticks.push(t);
 159              }
 160              // now add the last tick at the end
 161              var t = new this.tickRenderer(this.tickOptions);
 162              t.showLabel = false;
 163              // t.showMark = true;
 164              t.setTick(tt+1, this.name);
 165              this._ticks.push(t);
 166          }
 167  
 168          // we don't have any ticks yet, let's make some!
 169          else {
 170              if (name == 'xaxis' || name == 'x2axis') {
 171                  dim = this._plotDimensions.width;
 172              }
 173              else {
 174                  dim = this._plotDimensions.height;
 175              }
 176              
 177              // if min, max and number of ticks specified, user can't specify interval.
 178              if (this.min != null && this.max != null && this.numberTicks != null) {
 179                  this.tickInterval = null;
 180              }
 181              
 182              // if max, min, and interval specified and interval won't fit, ignore interval.
 183              if (this.min != null && this.max != null && this.tickInterval != null) {
 184                  if (parseInt((this.max-this.min)/this.tickInterval, 10) != (this.max-this.min)/this.tickInterval) {
 185                      this.tickInterval = null;
 186                  }
 187              }
 188          
 189              // find out how many categories are in the lines and collect labels
 190              var labels = [];
 191              var numcats = 0;
 192              var min = 0.5;
 193              var max, val;
 194              var isMerged = false;
 195              for (var i=0; i<this._series.length; i++) {
 196                  var s = this._series[i];
 197                  for (var j=0; j<s.data.length; j++) {
 198                      if (this.name == 'xaxis' || this.name == 'x2axis') {
 199                          val = s.data[j][0];
 200                      }
 201                      else {
 202                          val = s.data[j][1];
 203                      }
 204                      if ($.inArray(val, labels) == -1) {
 205                          isMerged = true;
 206                          numcats += 1;      
 207                          labels.push(val);
 208                      }
 209                  }
 210              }
 211              
 212              if (isMerged && this.sortMergedLabels) {
 213                  labels.sort(function(a,b) { return a - b; });
 214              }
 215              
 216              // keep a reference to these tick labels to use for redrawing plot (see bug #57)
 217              this.ticks = labels;
 218              
 219              // now bin the data values to the right lables.
 220              for (var i=0; i<this._series.length; i++) {
 221                  var s = this._series[i];
 222                  for (var j=0; j<s.data.length; j++) {
 223                      if (this.name == 'xaxis' || this.name == 'x2axis') {
 224                          val = s.data[j][0];
 225                      }
 226                      else {
 227                          val = s.data[j][1];
 228                      }
 229                      // for category axis, force the values into category bins.
 230                      // we should have the value in the label array now.
 231                      var idx = $.inArray(val, labels)+1;
 232                      if (this.name == 'xaxis' || this.name == 'x2axis') {
 233                          s.data[j][0] = idx;
 234                      }
 235                      else {
 236                          s.data[j][1] = idx;
 237                      }
 238                  }
 239              }
 240              
 241              // adjust with blanks if we have groups
 242              if (this.groups > 1 && !this._grouped) {
 243                  var l = labels.length;
 244                  var skip = parseInt(l/this.groups, 10);
 245                  var count = 0;
 246                  for (var i=skip; i<l; i+=skip+1) {
 247                      labels[i] = ' ';
 248                  }
 249                  this._grouped = true;
 250              }
 251          
 252              max = numcats + 0.5;
 253              if (this.numberTicks == null) {
 254                  this.numberTicks = 2*numcats + 1;
 255              }
 256  
 257              var range = max - min;
 258              this.min = min;
 259              this.max = max;
 260              var track = 0;
 261              
 262              // todo: adjust this so more ticks displayed.
 263              var maxVisibleTicks = parseInt(3+dim/10, 10);
 264              var skip = parseInt(numcats/maxVisibleTicks, 10);
 265  
 266              if (this.tickInterval == null) {
 267  
 268                  this.tickInterval = range / (this.numberTicks-1);
 269  
 270              }
 271              // if tickInterval is specified, we will ignore any computed maximum.
 272              for (var i=0; i<this.numberTicks; i++){
 273                  tt = this.min + i * this.tickInterval;
 274                  var t = new this.tickRenderer(this.tickOptions);
 275                  // if even tick, it isn't a category, it's a divider
 276                  if (i/2 == parseInt(i/2, 10)) {
 277                      t.showLabel = false;
 278                      t.showMark = true;
 279                  }
 280                  else {
 281                      if (skip>0 && track<skip) {
 282                          t.showLabel = false;
 283                          track += 1;
 284                      }
 285                      else {
 286                          t.showLabel = true;
 287                          track = 0;
 288                      } 
 289                      t.label = t.formatter(t.formatString, labels[(i-1)/2]);
 290                      t.showMark = false;
 291                      t.showGridline = false;
 292                  }
 293                  t.setTick(tt, this.name);
 294                  this._ticks.push(t);
 295              }
 296          }
 297          
 298      };
 299      
 300      // called with scope of axis
 301      $.jqplot.CategoryAxisRenderer.prototype.draw = function(ctx, plot) {
 302          if (this.show) {
 303              // populate the axis label and value properties.
 304              // createTicks is a method on the renderer, but
 305              // call it within the scope of the axis.
 306              this.renderer.createTicks.call(this);
 307              // fill a div with axes labels in the right direction.
 308              // Need to pregenerate each axis to get it's bounds and
 309              // position it and the labels correctly on the plot.
 310              var dim=0;
 311              var temp;
 312              // Added for theming.
 313              if (this._elem) {
 314                  // this._elem.empty();
 315                  // Memory Leaks patch
 316                  this._elem.emptyForce();
 317              }
 318  
 319              this._elem = this._elem || $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>');
 320              
 321              if (this.name == 'xaxis' || this.name == 'x2axis') {
 322                  this._elem.width(this._plotDimensions.width);
 323              }
 324              else {
 325                  this._elem.height(this._plotDimensions.height);
 326              }
 327              
 328              // create a _label object.
 329              this.labelOptions.axis = this.name;
 330              this._label = new this.labelRenderer(this.labelOptions);
 331              if (this._label.show) {
 332                  var elem = this._label.draw(ctx, plot);
 333                  elem.appendTo(this._elem);
 334              }
 335      
 336              var t = this._ticks;
 337              for (var i=0; i<t.length; i++) {
 338                  var tick = t[i];
 339                  if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
 340                      var elem = tick.draw(ctx, plot);
 341                      elem.appendTo(this._elem);
 342                  }
 343              }
 344          
 345              this._groupLabels = [];
 346              // now make group labels
 347              for (var i=0; i<this.groupLabels.length; i++)
 348              {
 349                  var elem = $('<div style="position:absolute;" class="jqplot-'+this.name+'-groupLabel"></div>');
 350                  elem.html(this.groupLabels[i]);
 351                  this._groupLabels.push(elem);
 352                  elem.appendTo(this._elem);
 353              }
 354          }
 355          return this._elem;
 356      };
 357      
 358      // called with scope of axis
 359      $.jqplot.CategoryAxisRenderer.prototype.set = function() { 
 360          var dim = 0;
 361          var temp;
 362          var w = 0;
 363          var h = 0;
 364          var lshow = (this._label == null) ? false : this._label.show;
 365          if (this.show) {
 366              var t = this._ticks;
 367              for (var i=0; i<t.length; i++) {
 368                  var tick = t[i];
 369                  if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
 370                      if (this.name == 'xaxis' || this.name == 'x2axis') {
 371                          temp = tick._elem.outerHeight(true);
 372                      }
 373                      else {
 374                          temp = tick._elem.outerWidth(true);
 375                      }
 376                      if (temp > dim) {
 377                          dim = temp;
 378                      }
 379                  }
 380              }
 381              
 382              var dim2 = 0;
 383              for (var i=0; i<this._groupLabels.length; i++) {
 384                  var l = this._groupLabels[i];
 385                  if (this.name == 'xaxis' || this.name == 'x2axis') {
 386                      temp = l.outerHeight(true);
 387                  }
 388                  else {
 389                      temp = l.outerWidth(true);
 390                  }
 391                  if (temp > dim2) {
 392                      dim2 = temp;
 393                  }
 394              }
 395              
 396              if (lshow) {
 397                  w = this._label._elem.outerWidth(true);
 398                  h = this._label._elem.outerHeight(true); 
 399              }
 400              if (this.name == 'xaxis') {
 401                  dim += dim2 + h;
 402                  this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
 403              }
 404              else if (this.name == 'x2axis') {
 405                  dim += dim2 + h;
 406                  this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
 407              }
 408              else if (this.name == 'yaxis') {
 409                  dim += dim2 + w;
 410                  this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
 411                  if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
 412                      this._label._elem.css('width', w+'px');
 413                  }
 414              }
 415              else {
 416                  dim += dim2 + w;
 417                  this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
 418                  if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
 419                      this._label._elem.css('width', w+'px');
 420                  }
 421              }
 422          }  
 423      };
 424      
 425      // called with scope of axis
 426      $.jqplot.CategoryAxisRenderer.prototype.pack = function(pos, offsets) {
 427          var ticks = this._ticks;
 428          var max = this.max;
 429          var min = this.min;
 430          var offmax = offsets.max;
 431          var offmin = offsets.min;
 432          var lshow = (this._label == null) ? false : this._label.show;
 433          var i;
 434          
 435          for (var p in pos) {
 436              this._elem.css(p, pos[p]);
 437          }
 438          
 439          this._offsets = offsets;
 440          // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
 441          var pixellength = offmax - offmin;
 442          var unitlength = max - min;
 443          
 444          // point to unit and unit to point conversions references to Plot DOM element top left corner.
 445          this.p2u = function(p){
 446              return (p - offmin) * unitlength / pixellength + min;
 447          };
 448          
 449          this.u2p = function(u){
 450              return (u - min) * pixellength / unitlength + offmin;
 451          };
 452                  
 453          if (this.name == 'xaxis' || this.name == 'x2axis'){
 454              this.series_u2p = function(u){
 455                  return (u - min) * pixellength / unitlength;
 456              };
 457              this.series_p2u = function(p){
 458                  return p * unitlength / pixellength + min;
 459              };
 460          }
 461          
 462          else {
 463              this.series_u2p = function(u){
 464                  return (u - max) * pixellength / unitlength;
 465              };
 466              this.series_p2u = function(p){
 467                  return p * unitlength / pixellength + max;
 468              };
 469          }
 470          
 471          if (this.show) {
 472              if (this.name == 'xaxis' || this.name == 'x2axis') {
 473                  for (i=0; i<ticks.length; i++) {
 474                      var t = ticks[i];
 475                      if (t.show && t.showLabel) {
 476                          var shim;
 477                          
 478                          if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
 479                              // will need to adjust auto positioning based on which axis this is.
 480                              var temp = (this.name == 'xaxis') ? 1 : -1;
 481                              switch (t.labelPosition) {
 482                                  case 'auto':
 483                                      // position at end
 484                                      if (temp * t.angle < 0) {
 485                                          shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
 486                                      }
 487                                      // position at start
 488                                      else {
 489                                          shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
 490                                      }
 491                                      break;
 492                                  case 'end':
 493                                      shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
 494                                      break;
 495                                  case 'start':
 496                                      shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
 497                                      break;
 498                                  case 'middle':
 499                                      shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
 500                                      break;
 501                                  default:
 502                                      shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
 503                                      break;
 504                              }
 505                          }
 506                          else {
 507                              shim = -t.getWidth()/2;
 508                          }
 509                          var val = this.u2p(t.value) + shim + 'px';
 510                          t._elem.css('left', val);
 511                          t.pack();
 512                      }
 513                  }
 514                  
 515                  var labeledge=['bottom', 0];
 516                  if (lshow) {
 517                      var w = this._label._elem.outerWidth(true);
 518                      this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
 519                      if (this.name == 'xaxis') {
 520                          this._label._elem.css('bottom', '0px');
 521                          labeledge = ['bottom', this._label._elem.outerHeight(true)];
 522                      }
 523                      else {
 524                          this._label._elem.css('top', '0px');
 525                          labeledge = ['top', this._label._elem.outerHeight(true)];
 526                      }
 527                      this._label.pack();
 528                  }
 529                  
 530                  // draw the group labels
 531                  var step = parseInt(this._ticks.length/this.groups, 10);
 532                  for (i=0; i<this._groupLabels.length; i++) {
 533                      var mid = 0;
 534                      var count = 0;
 535                      for (var j=i*step; j<=(i+1)*step; j++) {
 536                          if (this._ticks[j]._elem && this._ticks[j].label != " ") {
 537                              var t = this._ticks[j]._elem;
 538                              var p = t.position();
 539                              mid += p.left + t.outerWidth(true)/2;
 540                              count++;
 541                          }
 542                      }
 543                      mid = mid/count;
 544                      this._groupLabels[i].css({'left':(mid - this._groupLabels[i].outerWidth(true)/2)});
 545                      this._groupLabels[i].css(labeledge[0], labeledge[1]);
 546                  }
 547              }
 548              else {
 549                  for (i=0; i<ticks.length; i++) {
 550                      var t = ticks[i];
 551                      if (t.show && t.showLabel) {                        
 552                          var shim;
 553                          if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
 554                              // will need to adjust auto positioning based on which axis this is.
 555                              var temp = (this.name == 'yaxis') ? 1 : -1;
 556                              switch (t.labelPosition) {
 557                                  case 'auto':
 558                                      // position at end
 559                                  case 'end':
 560                                      if (temp * t.angle < 0) {
 561                                          shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
 562                                      }
 563                                      else {
 564                                          shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
 565                                      }
 566                                      break;
 567                                  case 'start':
 568                                      if (t.angle > 0) {
 569                                          shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
 570                                      }
 571                                      else {
 572                                          shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
 573                                      }
 574                                      break;
 575                                  case 'middle':
 576                                      // if (t.angle > 0) {
 577                                      //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
 578                                      // }
 579                                      // else {
 580                                      //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
 581                                      // }
 582                                      shim = -t.getHeight()/2;
 583                                      break;
 584                                  default:
 585                                      shim = -t.getHeight()/2;
 586                                      break;
 587                              }
 588                          }
 589                          else {
 590                              shim = -t.getHeight()/2;
 591                          }
 592                          
 593                          var val = this.u2p(t.value) + shim + 'px';
 594                          t._elem.css('top', val);
 595                          t.pack();
 596                      }
 597                  }
 598                  
 599                  var labeledge=['left', 0];
 600                  if (lshow) {
 601                      var h = this._label._elem.outerHeight(true);
 602                      this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
 603                      if (this.name == 'yaxis') {
 604                          this._label._elem.css('left', '0px');
 605                          labeledge = ['left', this._label._elem.outerWidth(true)];
 606                      }
 607                      else {
 608                          this._label._elem.css('right', '0px');
 609                          labeledge = ['right', this._label._elem.outerWidth(true)];
 610                      }   
 611                      this._label.pack();
 612                  }
 613                  
 614                  // draw the group labels, position top here, do left after label position.
 615                  var step = parseInt(this._ticks.length/this.groups, 10);
 616                  for (i=0; i<this._groupLabels.length; i++) {
 617                      var mid = 0;
 618                      var count = 0;
 619                      for (var j=i*step; j<=(i+1)*step; j++) {
 620                          if (this._ticks[j]._elem && this._ticks[j].label != " ") {
 621                              var t = this._ticks[j]._elem;
 622                              var p = t.position();
 623                              mid += p.top + t.outerHeight()/2;
 624                              count++;
 625                          }
 626                      }
 627                      mid = mid/count;
 628                      this._groupLabels[i].css({'top':mid - this._groupLabels[i].outerHeight()/2});
 629                      this._groupLabels[i].css(labeledge[0], labeledge[1]);
 630                      
 631                  }
 632              }
 633          }
 634      };    
 635      
 636      
 637  })(jQuery);


Generated: Fri Nov 28 20:08:37 2014 Cross-referenced by PHPXref 0.7.1