Add analytics
[bus.git] / busui / owa / modules / base / js / owa.heatmap.js
blob:a/busui/owa/modules/base/js/owa.heatmap.js -> blob:b/busui/owa/modules/base/js/owa.heatmap.js
  //
  // Open Web Analytics - An Open Source Web Analytics Framework
  //
  // Copyright 2010 Peter Adams. All rights reserved.
  //
  // Licensed under GPL v2.0 http://www.gnu.org/copyleft/gpl.html
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  //
  // $Id$
  //
   
  /**
  * Javascript Heatmap Library
  *
  * @author Peter Adams <peter@openwebanalytics.com>
  * @web <a href="http://www.openwebanalytcs.com">Open Web Analytics</a>
  * @copyright Copyright &copy; 2006-2010 Peter Adams <peter@openwebanalytics.com>
  * @license http://www.gnu.org/copyleft/gpl.html GPL v2.0
  * @category owa
  * @package owa
  * @version $Revision$
  * @since owa 1.2.1
  */
  OWA.heatmap = function(w, h) {
   
  this.docDimensions = this.getDim(document);
   
  w = w || this.docDimensions.w;
  h = h || this.docDimensions.h;
  OWA.debug("Canvas size: %s by %s", w, h);
  this.createCanvas(w,h);
  this.canvas = document.getElementById('owa_heatmap');
  this.context = this.canvas.getContext('2d');
  this.calcRegions();
   
  };
   
  OWA.heatmap.prototype = {
   
  options: {
  dotSize: 12,
  numRegions: 40,
  alphaIncrement:50,
  demoMode: false,
  liveMode: false,
  mapInterval: 1000,
  randomDataCount: 200,
  rowsPerFetch: 100,
  strokeRegions: false,
  svgUrl: OWA.getSetting('baseUrl')+'/modules/base/i/test.svg#f1',
  baseUrl: '',
  apiUrl: ''
  },
  canvas: null,
  context: null,
  docDimensions: null,
  regions: new Array(),
  regionsMap: new Array(),
  regionWidth: null,
  regionHeight: null,
  dirtyRegions: new Object(),
  timer: '',
  clicks: '',
  nextPage: 1,
  more: true,
  lock: false,
   
  /**
  * Marks a region as dirty so that it can be re-rendered
  */
  markRegionDirty: function(region_num) {
  if (region_num >= 0) {
  this.dirtyRegions[region_num] = true;
  OWA.debug("marking region dirty: %s", region_num);
  } else {
  OWA.debug("no region to mark dirty!");
  }
  },
   
  showControlPanel: function() {
  var that = this;
  jQuery('body').append('<div id="owa_overlay"></div>');
  jQuery('#owa_overlay').append('<div id="owa_overlay_logo"></div>');
  jQuery('#owa_overlay').append('<div class="owa_overlay_control" id="owa_overlay_start">Start</div>');
  jQuery('#owa_overlay_start').toggleClass('active');
  jQuery('#owa_overlay').append('<div class="owa_overlay_control" id="owa_overlay_stop">Stop</div>');
  jQuery('#owa_overlay').append('<div class="owa_overlay_control" id="owa_overlay_end">X</div>');
  jQuery('#owa_overlay_start').click(function(){that.startTimer()});
  jQuery('#owa_overlay_stop').click(function(){that.stopTimer()});
  jQuery('.owa_overlay_control').bind('click', function(){
  jQuery(".owa_overlay_control").removeClass('active');
  jQuery(this).addClass('active');
  });
  jQuery('#owa_overlay_end').click(function(){that.endSession()});
  //eliminate session cookie when window closes.
  jQuery(window).unload(function() {OWA.endOverlaySession()});
  },
   
  /**
  * Main generation method. kicks off the timer if in liveMode
  */
  generate: function() {
   
  this.showControlPanel();
  this.applyBlur();
   
  if (this.options.liveMode === true) {
   
  this.startTimer();
   
  } else {
   
  this.map();
  }
   
   
  },
   
  endSession: function() {
   
  OWA.util.eraseCookie('owa_overlay', document.domain);
  window.close();
  },
   
  startTimer: function() {
  var that = this;
  this.timer = setInterval(function(){that.map()}, this.options.mapInterval);
  },
   
  stopTimer: function() {
  if (!this.timer) return false;
  clearInterval(this.timer);
  },
   
  /**
  * Gets data and plots it
  */
  map: function() {
   
  if (this.lock == true) {
  OWA.debug("skipping data fetch due to lock.");
  return;
  } else {
  this.lock = true;
  }
   
  if (this.options.liveMode === true) {
   
  var more = this.checkForMoreClicks();
  if (more === true) {
  OWA.debug('there are more clicks to fetch.');
  var data = this.getData();
  } else {
  OWA.debug('there are no more clicks to fetch.');
  this.stopTimer();
  }
  } else {
  var data = this.getData();
  }
  },
   
  /**
  * Gets data, random if in demoMode
  */
  getData: function() {
   
  // get data
  if (this.options.demoMode === true) {
  return this.getRandomData(this.options.randomDataCount);
  } else {
  var data = this.fetchData(this.getNextPage());
   
  return;
  }
  },
   
  checkForMoreClicks: function() {
   
  return this.more;
  },
   
  getNextPage: function() {
   
  return this.nextPage;
  },
   
  setNextPage: function(page) {
  OWA.debug("setNextpage received page as %d", page);
  this.nextPage++;
  OWA.debug("setNextpage is setting page as %d", this.nextPage);
  },
   
  setMore: function(bool) {
   
  this.more = bool;
  },
   
  /**
  * Fetches data via ajax request
  */
  fetchData: function(page) {
   
  var p = OWA.util.readCookie('owa_overlay');
  //alert(unescape(p));
  var params = OWA.util.parseCookieStringToJson(p);
  //params.action = 'base.reportOverlay';
  //params.document_url = OWA.util.urlEncode(document.location);
  params.action = 'getDomClicks';
  params.pageUrl = OWA.util.urlEncode(document.location);
  //params.document_url = document.location;
  //OWA.debug('encoded url: '+OWA.util.urlEncode(document.location));
  params.resultsPerPage = this.options.rowsPerFetch;
  params.format = 'jsonp';
   
  // add page number if one was passed in
  if (page) {
  OWA.debug("fetchData will fetch page %s", page);
  params.page = page;
  }
   
  //closure
  var that = this;
   
  jQuery.ajax({
  url: OWA.getApiEndpoint(),
  data: OWA.util.nsParams(params),
  dataType: 'jsonp',
  jsonp: 'owa_jsonpCallback',
  success: function(data) {
  that.plotClickData(data);
  }
  });
  },
   
  plotClickData: function(data) {
   
  if (data) {
  //OWA.debug('setClicks says data is defined');
  this.clicks = data;
   
  //set more flag
  if (data.more === true && data.more != null) {
  OWA.debug("plotClickData says more flag was set to true");
  this.setMore(true);
  //set next page
  this.setNextPage(data.page);
  } else {
  OWA.debug("plotClickData says more flag was set to false");
  this.setMore(false);
  }
   
  //plot dots
  //this.plotDots(this.getClicks());
  this.plotDotsRound(this.getClicks());
  this.lock = false;
  return true;
  } else {
  return false;
  }
   
  },
   
  getClicks: function() {
  //OWA.debug("getClicks is logging %s", this.clicks['page']);
  return this.clicks.rows;
  },
   
  /**
  * Looks up the a region's top lower right corner plot points
  */
  getRegion: function(num) {
  //OWA.debug("Getting dims for region %s", num);
  return this.regions[num];
  },
   
  /**
  * Sets the color of a pixels a region based on their alpha values
  */
  setColor: function(num) {
  OWA.debug("About to set color for region %s", num);
  var dims = this.getRegion(num);
  OWA.debug("set color coords %s %s", dims.x, dims.y);
   
  // get the actual pixel data from the region
  var canvasData = this.context.getImageData(dims.x, dims.y, this.regionWidth, this.regionHeight);
  var pix = canvasData.data;
   
  // Loop over each pixel and invert the color.
  for (var i = 0, n = pix.length; i < n; i += 4) {
  var rgb = this.getRgbFromAlpha(pix[i+3]);
  pix[i ] = Math.round(parseInt(rgb.r)); // red
  pix[i+1] = Math.round(parseInt(rgb.g)); // green
  pix[i+2] = Math.round(parseInt(rgb.b)); // blue
   
  }
   
  // Draw the ImageData object at the given (x,y) coordinates.
  this.context.putImageData(canvasData,dims.x,dims.y);
  },
   
  getRgbFromAlpha : function(alpha) {
   
  var rgb = {'r': null, 'g': null, 'b': null};
   
  // set colors based on current alpha value
  if( alpha <= 255 && alpha >= 235 ){
  tmp = 255 - alpha;
  rgb.r = 255 - tmp;
  rgb.g = tmp * 12;
  } else if ( alpha <= 234 && alpha >= 200 ){
  tmp = 234 - alpha;
  rgb.r = 255 - ( tmp * 8 );
  rgb.g = 255;
  } else if ( alpha <= 199 && alpha >= 150 ){
  tmp = 199 - alpha;
  rgb.g = 255;
  rgb.b = tmp * 5;
  } else if ( alpha <= 149 && alpha >= 100 ){
  tmp = 149 - alpha;
  rgb.g = 255 - ( tmp * 5 );
  rgb.b = 255;
  } else {
  rgb.b = 255;
  }
   
  return rgb;
  },
   
  /**
  * Fills a region with grey
  * DEPRICATED
  */
  fillRegion: function(num) {
   
  this.fillRectangle(this.regions[num].x, this.regions[num].y, this.regionWidth, this.regionHeight, "rgba(0,0,0, 0.5)");
  },
   
  strokeRegion: function(num) {
   
  this.context.strokeRect(this.regions[num].x, this.regions[num].y, this.regionWidth, this.regionHeight);
   
  },
   
  /**
  * Fills a rectangle with an rgba value
  */
  fillRectangle: function(x,y,w,h,rgba) {
   
  this.context.fillStyle = rgba;
  this.context.fillRect(x, y, w, h);
  },
   
  /**
  * Fils all regions
  * DEPRICATED
  */
  fillAllRegions: function() {
   
  for (var i=0, n = this.regions.length; i < n; i++) {
  //OWA.debug("region %s", i);
  this.fillRegion(i);
  }
   
  },
   
  /**
  * Find the region that a set of coordinates falls into
  */
  findRegion: function(x, y) {
  x = parseFloat(x);
  y = parseFloat(y);
  // walk the outer x map in ascending order
  OWA.debug("finding region for %s", x,y);
  for (i in this.regionsMap) {
  // look for the first value that is greater that or equals to the x coordinate
  if (this.regionsMap.hasOwnProperty(i)) {
  OWA.debug("regionmap i: %s", i);
  if (x <= i) {
  // For that x coordinate walk the inner map in ascending order
  OWA.debug("regionmap x chosen: %s. x was: %s", i, x);
  for ( n in this.regionsMap[i]) {
  // find the first value that is greater than or equals to the y coordinate
  if (this.regionsMap[i].hasOwnProperty(n)) {
  //OWA.debug("what is this %s", n);
  if (y <= n) {
  // Return the region number