|
// |
|
// 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 © 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 |
|
|