click_heat_map 0.0.1.2 → 0.0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 299222f7ba9826c87c1ab8c5edbe6417c208aacd
4
- data.tar.gz: 515081c207b6daef14cea1d32da8c431678c6453
3
+ metadata.gz: 0378b00fb95665f9a502c3146782db8b1cbdb5bf
4
+ data.tar.gz: 4461b4f93f013577440d32e830b1469e1cd8c9bf
5
5
  SHA512:
6
- metadata.gz: 6cbd820c94476d85f2fce15174c648f998cfe7e7942418707672fed03847273634de79eba3dc3bae1bce7f85a602783fb68a3ccb106760595bfd1f5fea671e12
7
- data.tar.gz: a0faf42c2226bb19ea1c792071d39437dc71dbaa44e17426203ef9892a0a82958185908168f0882890e685de192bf6f1e9dc5bd34bc5ebe4707c886d5bd27940
6
+ metadata.gz: 9ffdc43dcd02d43f24e47b0df06f03ad9d7c733e63505859b94e84d53736230071a025b2df714616b9e562a10eeb045faa188811f9887158a04cef7c16805871
7
+ data.tar.gz: 1791f9d4145662820c352ff42c93535ef58d0701813a19ffcb322142acba5cfd16533562511fad1774e0ac2b24f90d85199d4031a17cb40b461e01783c8964cf
@@ -1,107 +1,97 @@
1
- window.ClickMapAdmin = (function() {
2
- var base;
1
+ (function() {
2
+ window.ClickMapAdmin = (function() {
3
+ var base;
3
4
 
4
- base = ClickMapAdmin;
5
+ base = ClickMapAdmin;
5
6
 
6
- function ClickMapAdmin(container) {
7
- this.container = container;
8
- this.path = window.location.pathname;
9
- this.createAdminPanel();
10
- this.createHeatmapArea();
11
- this.loadHeatmapData();
12
- }
7
+ function ClickMapAdmin(container) {
8
+ this.container = container;
9
+ this.path = window.location.pathname;
10
+ this.createAdminPanel();
11
+ this.createHeatmapArea();
12
+ this.loadHeatmapData();
13
+ }
13
14
 
14
- ClickMapAdmin.prototype.loadHeatmapData = function() {
15
- var container, containerX, containerY, dateFrom, dateTo, maxHeight, maxWidth, minHeight, minWidth, url;
16
- container = $(this.container);
17
- containerX = container.offset().left;
18
- containerY = container.offset().top;
19
- minWidth = $("#clickmap_min_width").val();
20
- maxWidth = $("#clickmap_max_width").val();
21
- minHeight = $("#clickmap_min_height").val();
22
- maxHeight = $("#clickmap_max_height").val();
23
- dateFrom = $("#clickmap_date_from").val();
24
- dateTo = $("#clickmap_date_to").val();
25
- url = "/clickmap/admin/clicks?page=" + (encodeURI(this.path)) + "&min_w=" + minWidth + "&max_w=" + maxWidth + "&min_h=" + minHeight + "&max_h=" + maxHeight + "&from=" + dateFrom + "&to=" + dateTo;
26
- return $.getJSON(url, (function(_this) {
27
- return function(data) {
28
- var d, max, _i, _len;
29
- max = Math.max.apply(Math, data.map(function(o) {
30
- return o.count;
31
- }));
32
- for (_i = 0, _len = data.length; _i < _len; _i++) {
33
- d = data[_i];
34
- d.x += containerX;
35
- d.y += containerY;
36
- }
37
- _this.heatmap.store.setDataSet({
38
- max: max,
39
- data: data
40
- });
41
- _this.heatmapArea.show();
42
- return _this.heatmap.toggleDisplay();
43
- };
44
- })(this));
45
- };
15
+ ClickMapAdmin.prototype.loadHeatmapData = function() {
16
+ var containerX, containerY, dateFrom, dateTo, maxHeight, maxWidth, minHeight, minWidth, url;
17
+ this.container = $(this.container);
18
+ containerX = this.container.offset().left;
19
+ containerY = this.container.offset().top;
20
+ minWidth = $("#clickmap_min_width").val();
21
+ maxWidth = $("#clickmap_max_width").val();
22
+ minHeight = $("#clickmap_min_height").val();
23
+ maxHeight = $("#clickmap_max_height").val();
24
+ dateFrom = $("#clickmap_date_from").val();
25
+ dateTo = $("#clickmap_date_to").val();
26
+ url = "/clickmap/admin/clicks?page=" + (encodeURI(this.path)) + "&min_w=" + minWidth + "&max_w=" + maxWidth + "&min_h=" + minHeight + "&max_h=" + maxHeight + "&from=" + dateFrom + "&to=" + dateTo;
27
+ return $.getJSON(url, (function(_this) {
28
+ return function(data) {
29
+ var max;
30
+ max = Math.max.apply(Math, data.map(function(o) {
31
+ return o.count;
32
+ }));
33
+ _this.heatmap.setData({
34
+ max: max,
35
+ data: data
36
+ });
37
+ return _this.heatmapArea.show();
38
+ };
39
+ })(this));
40
+ };
46
41
 
47
- ClickMapAdmin.prototype.createHeatmapArea = function() {
48
- var heatmapEl;
49
- this.heatmapArea = $("<div/>").attr('id', 'clickmap_heatmap_area');
50
- $("body").prepend(this.heatmapArea);
51
- heatmapEl = document.getElementById("clickmap_heatmap_area");
52
- this.heatmap = h337.create({
53
- element: heatmapEl,
54
- radius: 10,
55
- visible: false
56
- });
57
- this.heatmapArea.hide();
58
- return this.heatmapArea.click((function(_this) {
59
- return function() {
60
- if (_this.heatmap.get("visible")) {
61
- _this.heatmapArea.hide();
62
- return _this.heatmap.toggleDisplay();
63
- }
64
- };
65
- })(this));
66
- };
42
+ ClickMapAdmin.prototype.createHeatmapArea = function() {
43
+ this.heatmap = h337.create({
44
+ container: $(this.container)[0],
45
+ radius: 10
46
+ });
47
+ this.heatmapArea = $(".heatmap-canvas");
48
+ return this.heatmapArea.click((function(_this) {
49
+ return function() {
50
+ if (_this.heatmapArea.is(":visible")) {
51
+ return _this.heatmapArea.hide();
52
+ }
53
+ };
54
+ })(this));
55
+ };
67
56
 
68
- ClickMapAdmin.prototype.createAdminPanel = function() {
69
- var form, panel;
70
- panel = $("<div/>").attr('id', "clickmap_admin");
71
- form = "<form id='clickmap_form'>";
72
- form += "<label for='clickmap_min_width'>Min width: </label>";
73
- form += "<input class='size' id='clickmap_min_width'>";
74
- form += "<label for='clickmap_max_width'>Max width: </label>";
75
- form += "<input class='size' id='clickmap_max_width'>";
76
- form += "<label for='clickmap_min_height'>Min height: </label>";
77
- form += "<input class='size' id='clickmap_min_height'>";
78
- form += "<label for='clickmap_max_height'>Max height: </label>";
79
- form += "<input class='size' id='clickmap_max_height'><br>";
80
- form += "<label for='clickmap_date_from'>Date from: </label>";
81
- form += "<input class='date' id='clickmap_date_from' placeholder='yyyy-mm-dd'>";
82
- form += "<label for='clickmap_date_to'>Date to: </label>";
83
- form += "<input class='date' id='clickmap_date_to' placeholder='yyyy-mm-dd'>";
84
- form += "<input type='submit' value='reload'>";
85
- form += "<a href='#' id='clickmap_enable'>Enable clickmap</a>";
86
- form += "</form>";
87
- panel.html(form);
88
- $("body").prepend(panel);
89
- $("#clickmap_form").on('submit', (function(_this) {
90
- return function(e) {
91
- e.preventDefault();
92
- return _this.loadHeatmapData();
93
- };
94
- })(this));
95
- $("#clickmap_enable").click((function(_this) {
96
- return function(e) {
97
- e.preventDefault();
98
- _this.heatmapArea.show();
99
- return _this.heatmap.toggleDisplay();
100
- };
101
- })(this));
102
- return true;
103
- };
57
+ ClickMapAdmin.prototype.createAdminPanel = function() {
58
+ var form, panel;
59
+ panel = $("<div/>").attr('id', "clickmap_admin");
60
+ form = "<form id='clickmap_form'>";
61
+ form += "<label for='clickmap_min_width'>Min width: </label>";
62
+ form += "<input class='size' id='clickmap_min_width'>";
63
+ form += "<label for='clickmap_max_width'>Max width: </label>";
64
+ form += "<input class='size' id='clickmap_max_width'>";
65
+ form += "<label for='clickmap_min_height'>Min height: </label>";
66
+ form += "<input class='size' id='clickmap_min_height'>";
67
+ form += "<label for='clickmap_max_height'>Max height: </label>";
68
+ form += "<input class='size' id='clickmap_max_height'><br>";
69
+ form += "<label for='clickmap_date_from'>Date from: </label>";
70
+ form += "<input class='date' id='clickmap_date_from' placeholder='yyyy-mm-dd'>";
71
+ form += "<label for='clickmap_date_to'>Date to: </label>";
72
+ form += "<input class='date' id='clickmap_date_to' placeholder='yyyy-mm-dd'>";
73
+ form += "<input type='submit' value='reload'>";
74
+ form += "<a href='#' id='clickmap_enable'>Enable clickmap</a>";
75
+ form += "</form>";
76
+ panel.html(form);
77
+ $("body").prepend(panel);
78
+ $("#clickmap_form").on('submit', (function(_this) {
79
+ return function(e) {
80
+ e.preventDefault();
81
+ return _this.loadHeatmapData();
82
+ };
83
+ })(this));
84
+ $("#clickmap_enable").click((function(_this) {
85
+ return function(e) {
86
+ e.preventDefault();
87
+ return _this.heatmapArea.show();
88
+ };
89
+ })(this));
90
+ return true;
91
+ };
104
92
 
105
- return ClickMapAdmin;
93
+ return ClickMapAdmin;
106
94
 
107
- })();
95
+ })();
96
+
97
+ }).call(this);
@@ -0,0 +1,9 @@
1
+ /*
2
+ * heatmap.js v2.0.0 | JavaScript Heatmap Library
3
+ *
4
+ * Copyright 2008-2014 Patrick Wied <heatmapjs@patrick-wied.at> - All rights reserved.
5
+ * Dual licensed under MIT and Beerware license
6
+ *
7
+ * :: 2014-08-05 01:42
8
+ */
9
+ (function(a){var b={defaultRadius:40,defaultRenderer:"canvas2d",defaultGradient:{.25:"rgb(0,0,255)",.55:"rgb(0,255,0)",.85:"yellow",1:"rgb(255,0,0)"},defaultMaxOpacity:1,defaultMinOpacity:0,defaultBlur:.85,defaultXField:"x",defaultYField:"y",defaultValueField:"value",plugins:{}};var c=function i(){var a=function d(a){this._coordinator={};this._data=[];this._radi=[];this._min=0;this._max=1;this._xField=a["xField"]||a.defaultXField;this._yField=a["yField"]||a.defaultYField;this._valueField=a["valueField"]||a.defaultValueField;if(a["radius"]){this._cfgRadius=a["radius"]}};var c=b.defaultRadius;a.prototype={_organiseData:function(a,b){var d=a[this._xField];var e=a[this._yField];var f=this._radi;var g=this._data;var h=this._max;var i=this._min;var j=a[this._valueField]||1;var k=a.radius||this._cfgRadius||c;if(!g[d]){g[d]=[];f[d]=[]}if(!g[d][e]){g[d][e]=j;f[d][e]=k}else{g[d][e]+=j}if(g[d][e]>h){if(!b){this._max=g[d][e]}else{this.setDataMax(g[d][e])}return false}else{return{x:d,y:e,value:j,radius:k,min:i,max:h}}},_unOrganizeData:function(){var a=[];var b=this._data;var c=this._radi;for(var d in b){for(var e in b[d]){a.push({x:d,y:e,radius:c[d][e],value:b[d][e]})}}return{min:this._min,max:this._max,data:a}},_onExtremaChange:function(){this._coordinator.emit("extremachange",{min:this._min,max:this._max})},addData:function(){if(arguments[0].length>0){var a=arguments[0];var b=a.length;while(b--){this.addData.call(this,a[b])}}else{var c=this._organiseData(arguments[0],true);if(c){this._coordinator.emit("renderpartial",{min:this._min,max:this._max,data:[c]})}}return this},setData:function(a){var b=a.data;var c=b.length;this._max=a.max;this._min=a.min||0;this._data=[];this._radi=[];for(var d=0;d<c;d++){this._organiseData(b[d],false)}this._onExtremaChange();this._coordinator.emit("renderall",this._getInternalData());return this},removeData:function(){},setDataMax:function(a){this._max=a;this._onExtremaChange();this._coordinator.emit("renderall",this._getInternalData());return this},setDataMin:function(a){this._min=a;this._onExtremaChange();this._coordinator.emit("renderall",this._getInternalData());return this},setCoordinator:function(a){this._coordinator=a},_getInternalData:function(){return{max:this._max,min:this._min,data:this._data,radi:this._radi}},getData:function(){return this._unOrganizeData()}};return a}();var d=function j(){var a=function(a){var b=a.gradient||a.defaultGradient;var c=document.createElement("canvas");var d=c.getContext("2d");c.width=256;c.height=1;var e=d.createLinearGradient(0,0,256,1);for(var f in b){e.addColorStop(f,b[f])}d.fillStyle=e;d.fillRect(0,0,256,1);return d.getImageData(0,0,256,1).data};var b=function(a,b){var c=document.createElement("canvas");var d=c.getContext("2d");var e=a;var f=a;c.width=c.height=a*2;if(b==1){d.beginPath();d.arc(e,f,a,0,2*Math.PI,false);d.fillStyle="rgba(0,0,0,1)";d.fill()}else{var g=d.createRadialGradient(e,f,a*b,e,f,a);g.addColorStop(0,"rgba(0,0,0,1)");g.addColorStop(1,"rgba(0,0,0,0)");d.fillStyle=g;d.fillRect(0,0,2*a,2*a)}return c};var c=function(a){var b=[];var c=a.min;var d=a.max;var e=a.radi;var a=a.data;var f=Object.keys(a);var g=f.length;while(g--){var h=f[g];var i=Object.keys(a[h]);var j=i.length;while(j--){var k=i[j];var l=a[h][k];var m=e[h][k];b.push({x:h,y:k,value:l,radius:m})}}return{min:c,max:d,data:b}};function d(b){var c=b.container;var d=this.shadowCanvas=document.createElement("canvas");var e=this.canvas=b.canvas||document.createElement("canvas");var f=this._renderBoundaries=[1e4,1e4,0,0];var g=getComputedStyle(b.container)||{};e.className="heatmap-canvas";this._width=e.width=d.width=+g.width.replace(/px/,"");this._height=e.height=d.height=+g.height.replace(/px/,"");this.shadowCtx=d.getContext("2d");this.ctx=e.getContext("2d");e.style.cssText=d.style.cssText="position:absolute;left:0;top:0;";c.style.position="relative";c.appendChild(e);this._palette=a(b);this._templates={};this._setStyles(b)}d.prototype={renderPartial:function(a){this._drawAlpha(a);this._colorize()},renderAll:function(a){this._clear();this._drawAlpha(c(a));this._colorize()},_updateGradient:function(b){this._palette=a(b)},updateConfig:function(a){if(a["gradient"]){this._updateGradient(a)}this._setStyles(a)},setDimensions:function(a,b){this._width=a;this._height=b;this.canvas.width=this.shadowCanvas.width=a;this.canvas.height=this.shadowCanvas.height=b},_clear:function(){this.shadowCtx.clearRect(0,0,this._width,this._height);this.ctx.clearRect(0,0,this._width,this._height)},_setStyles:function(a){this._blur=a.blur==0?0:a.blur||a.defaultBlur;if(a.backgroundColor){this.canvas.style.backgroundColor=a.backgroundColor}this._opacity=(a.opacity||0)*255;this._maxOpacity=(a.maxOpacity||a.defaultMaxOpacity)*255;this._minOpacity=(a.minOpacity||a.defaultMinOpacity)*255},_drawAlpha:function(a){var c=this._min=a.min;var d=this._max=a.max;var a=a.data||[];var e=a.length;var f=1-this._blur;while(e--){var g=a[e];var h=g.x;var i=g.y;var j=g.radius;var k=g.value;var l=h-j;var m=i-j;var n=this.shadowCtx;var o;if(!this._templates[j]){this._templates[j]=o=b(j,f)}else{o=this._templates[j]}n.globalAlpha=k/Math.abs(d-c);n.drawImage(o,l,m);if(l<this._renderBoundaries[0]){this._renderBoundaries[0]=l}if(m<this._renderBoundaries[1]){this._renderBoundaries[1]=m}if(l+2*j>this._renderBoundaries[2]){this._renderBoundaries[2]=l+2*j}if(m+2*j>this._renderBoundaries[3]){this._renderBoundaries[3]=m+2*j}}},_colorize:function(){var a=this._renderBoundaries[0];var b=this._renderBoundaries[1];var c=this._renderBoundaries[2]-a;var d=this._renderBoundaries[3]-b;var e=this._width;var f=this._height;var g=this._opacity;var h=this._maxOpacity;var i=this._minOpacity;if(a<0){a=0}if(b<0){b=0}if(a+c>e){c=e-a}if(b+d>f){d=f-b}var j=this.shadowCtx.getImageData(a,b,c,d);var k=j.data;var l=k.length;var m=this._palette;for(var n=3;n<l;n+=4){var o=k[n];var p=o*4;if(!p){continue}var q;if(g>0){q=g}else{if(o<h){if(o<i){q=i}else{q=o}}else{q=h}}k[n-3]=m[p];k[n-2]=m[p+1];k[n-1]=m[p+2];k[n]=q}j.data=k;this.ctx.putImageData(j,a,b);this._renderBoundaries=[1e3,1e3,0,0]},getValueAt:function(a){var b;var c=this.shadowCtx;var d=c.getImageData(a.x,a.y,1,1);var e=d.data[3];var f=this._max;var g=this._min;b=Math.abs(f-g)*(e/255)>>0;return b},getDataURL:function(){return this.canvas.toDataURL()}};return d}();var e=function k(){var a=false;if(b["defaultRenderer"]==="canvas2d"){a=d}return a}();var f={merge:function(){var a={};var b=arguments.length;for(var c=0;c<b;c++){var d=arguments[c];for(var e in d){a[e]=d[e]}}return a}};var g=function l(){var a=function h(){function a(){this.cStore={}}a.prototype={on:function(a,b,c){var d=this.cStore;if(!d[a]){d[a]=[]}d[a].push(function(a){return b.call(c,a)})},emit:function(a,b){var c=this.cStore;if(c[a]){var d=c[a].length;for(var e=0;e<d;e++){var f=c[a][e];f(b)}}}};return a}();var d=function(a){var b=a._renderer;var c=a._coordinator;var d=a._store;c.on("renderpartial",b.renderPartial,b);c.on("renderall",b.renderAll,b);c.on("extremachange",function(b){a._config.onExtremaChange&&a._config.onExtremaChange({min:b.min,max:b.max,gradient:a._config["gradient"]||a._config["defaultGradient"]})});d.setCoordinator(c)};function g(){var g=this._config=f.merge(b,arguments[0]||{});this._coordinator=new a;if(g["plugin"]){var h=g["plugin"];if(!b.plugins[h]){throw new Error("Plugin '"+h+"' not found. Maybe it was not registered.")}else{var i=b.plugins[h];this._renderer=i.renderer;this._store=i.store}}else{this._renderer=new e(g);this._store=new c(g)}d(this)}g.prototype={addData:function(){this._store.addData.apply(this._store,arguments);return this},removeData:function(){this._store.removeData&&this._store.removeData.apply(this._store,arguments);return this},setData:function(){this._store.setData.apply(this._store,arguments);return this},setDataMax:function(){this._store.setDataMax.apply(this._store,arguments);return this},setDataMin:function(){this._store.setDataMin.apply(this._store,arguments);return this},configure:function(a){this._config=f.merge(this._config,a);this._renderer.updateConfig(this._config);this._coordinator.emit("renderall",this._store._getInternalData());return this},repaint:function(){this._coordinator.emit("renderall",this._store._getInternalData());return this},getData:function(){return this._store.getData()},getDataURL:function(){return this._renderer.getDataURL()},getValueAt:function(a){if(this._store.getValueAt){return this._store.getValueAt(a)}else if(this._renderer.getValueAt){return this._renderer.getValueAt(a)}else{return null}}};return g}();var h={create:function(a){return new g(a)},register:function(a,c){b.plugins[a]=c}};a["h337"]=h})(this||window);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: click_heat_map
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.2
4
+ version: 0.0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Tomecek
@@ -24,7 +24,7 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- description: Determine where your users click with simple click heat maps.
27
+ description: Determine where your users click with simple click heatmaps.
28
28
  email: adam.tomecek@gmail.com
29
29
  executables: []
30
30
  extensions: []
@@ -32,7 +32,7 @@ extra_rdoc_files: []
32
32
  files:
33
33
  - lib/click_heat_map.rb
34
34
  - lib/models/click.rb
35
- - lib/public/js/heatmap.js
35
+ - lib/public/js/heatmap.min.js
36
36
  - lib/public/js/clickmap.js
37
37
  - lib/public/js/clickmap_admin.js
38
38
  - lib/public/css/clickmap.css
@@ -1,653 +0,0 @@
1
- /*
2
- * heatmap.js 1.0 - JavaScript Heatmap Library
3
- *
4
- * Copyright (c) 2011, Patrick Wied (http://www.patrick-wied.at)
5
- * Dual-licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
6
- * and the Beerware (http://en.wikipedia.org/wiki/Beerware) license.
7
- */
8
-
9
- (function(w){
10
- // the heatmapFactory creates heatmap instances
11
- var heatmapFactory = (function(){
12
-
13
- // store object constructor
14
- // a heatmap contains a store
15
- // the store has to know about the heatmap in order to trigger heatmap updates when datapoints get added
16
- var store = function store(hmap){
17
-
18
- var _ = {
19
- // data is a two dimensional array
20
- // a datapoint gets saved as data[point-x-value][point-y-value]
21
- // the value at [point-x-value][point-y-value] is the occurrence of the datapoint
22
- data: [],
23
- // tight coupling of the heatmap object
24
- heatmap: hmap
25
- };
26
- // the max occurrence - the heatmaps radial gradient alpha transition is based on it
27
- this.max = 1;
28
-
29
- this.get = function(key){
30
- return _[key];
31
- };
32
- this.set = function(key, value){
33
- _[key] = value;
34
- };
35
- }
36
-
37
- store.prototype = {
38
- // function for adding datapoints to the store
39
- // datapoints are usually defined by x and y but could also contain a third parameter which represents the occurrence
40
- addDataPoint: function(x, y){
41
- if(x < 0 || y < 0)
42
- return;
43
-
44
- var me = this,
45
- heatmap = me.get("heatmap"),
46
- data = me.get("data");
47
-
48
- if(!data[x])
49
- data[x] = [];
50
-
51
- if(!data[x][y])
52
- data[x][y] = 0;
53
-
54
- // if count parameter is set increment by count otherwise by 1
55
- data[x][y]+=(arguments.length<3)?1:arguments[2];
56
-
57
- me.set("data", data);
58
- // do we have a new maximum?
59
- if(me.max < data[x][y]){
60
- // max changed, we need to redraw all existing(lower) datapoints
61
- heatmap.get("actx").clearRect(0,0,heatmap.get("width"),heatmap.get("height"));
62
- me.setDataSet({ max: data[x][y], data: data }, true);
63
- return;
64
- }
65
- heatmap.drawAlpha(x, y, data[x][y], true);
66
- },
67
- setDataSet: function(obj, internal){
68
- var me = this,
69
- heatmap = me.get("heatmap"),
70
- data = [],
71
- d = obj.data,
72
- dlen = d.length;
73
- // clear the heatmap before the data set gets drawn
74
- heatmap.clear();
75
- this.max = obj.max;
76
- // if a legend is set, update it
77
- heatmap.get("legend") && heatmap.get("legend").update(obj.max);
78
-
79
- if(internal != null && internal){
80
- for(var one in d){
81
- // jump over undefined indexes
82
- if(one === undefined)
83
- continue;
84
- for(var two in d[one]){
85
- if(two === undefined)
86
- continue;
87
- // if both indexes are defined, push the values into the array
88
- heatmap.drawAlpha(one, two, d[one][two], false);
89
- }
90
- }
91
- }else{
92
- while(dlen--){
93
- var point = d[dlen];
94
- heatmap.drawAlpha(point.x, point.y, point.count, false);
95
- if(!data[point.x])
96
- data[point.x] = [];
97
-
98
- if(!data[point.x][point.y])
99
- data[point.x][point.y] = 0;
100
-
101
- data[point.x][point.y] = point.count;
102
- }
103
- }
104
- heatmap.colorize();
105
- this.set("data", d);
106
- },
107
- exportDataSet: function(){
108
- var me = this,
109
- data = me.get("data"),
110
- exportData = [];
111
-
112
- for(var one in data){
113
- // jump over undefined indexes
114
- if(one === undefined)
115
- continue;
116
- for(var two in data[one]){
117
- if(two === undefined)
118
- continue;
119
- // if both indexes are defined, push the values into the array
120
- exportData.push({x: parseInt(one, 10), y: parseInt(two, 10), count: data[one][two]});
121
- }
122
- }
123
-
124
- return { max: me.max, data: exportData };
125
- },
126
- generateRandomDataSet: function(points){
127
- var heatmap = this.get("heatmap"),
128
- w = heatmap.get("width"),
129
- h = heatmap.get("height");
130
- var randomset = {},
131
- max = Math.floor(Math.random()*1000+1);
132
- randomset.max = max;
133
- var data = [];
134
- while(points--){
135
- data.push({x: Math.floor(Math.random()*w+1), y: Math.floor(Math.random()*h+1), count: Math.floor(Math.random()*max+1)});
136
- }
137
- randomset.data = data;
138
- this.setDataSet(randomset);
139
- }
140
- };
141
-
142
- var legend = function legend(config){
143
- this.config = config;
144
-
145
- var _ = {
146
- element: null,
147
- labelsEl: null,
148
- gradientCfg: null,
149
- ctx: null
150
- };
151
- this.get = function(key){
152
- return _[key];
153
- };
154
- this.set = function(key, value){
155
- _[key] = value;
156
- };
157
- this.init();
158
- };
159
- legend.prototype = {
160
- init: function(){
161
- var me = this,
162
- config = me.config,
163
- title = config.title || "Legend",
164
- position = config.position,
165
- offset = config.offset || 10,
166
- gconfig = config.gradient,
167
- labelsEl = document.createElement("ul"),
168
- labelsHtml = "",
169
- grad, element, gradient, positionCss = "";
170
-
171
- me.processGradientObject();
172
-
173
- // Positioning
174
-
175
- // top or bottom
176
- if(position.indexOf('t') > -1){
177
- positionCss += 'top:'+offset+'px;';
178
- }else{
179
- positionCss += 'bottom:'+offset+'px;';
180
- }
181
-
182
- // left or right
183
- if(position.indexOf('l') > -1){
184
- positionCss += 'left:'+offset+'px;';
185
- }else{
186
- positionCss += 'right:'+offset+'px;';
187
- }
188
-
189
- element = document.createElement("div");
190
- element.style.cssText = "border-radius:5px;position:absolute;"+positionCss+"font-family:Helvetica; width:256px;z-index:10000000000; background:rgba(255,255,255,1);padding:10px;border:1px solid black;margin:0;";
191
- element.innerHTML = "<h3 style='padding:0;margin:0;text-align:center;font-size:16px;'>"+title+"</h3>";
192
- // create gradient in canvas
193
- labelsEl.style.cssText = "position:relative;font-size:12px;display:block;list-style:none;list-style-type:none;margin:0;height:15px;";
194
-
195
-
196
- // create gradient element
197
- gradient = document.createElement("div");
198
- gradient.style.cssText = ["position:relative;display:block;width:256px;height:15px;border-bottom:1px solid black; background-image:url(",me.createGradientImage(),");"].join("");
199
-
200
- element.appendChild(labelsEl);
201
- element.appendChild(gradient);
202
-
203
- me.set("element", element);
204
- me.set("labelsEl", labelsEl);
205
-
206
- me.update(1);
207
- },
208
- processGradientObject: function(){
209
- // create array and sort it
210
- var me = this,
211
- gradientConfig = this.config.gradient,
212
- gradientArr = [];
213
-
214
- for(var key in gradientConfig){
215
- if(gradientConfig.hasOwnProperty(key)){
216
- gradientArr.push({ stop: key, value: gradientConfig[key] });
217
- }
218
- }
219
- gradientArr.sort(function(a, b){
220
- return (a.stop - b.stop);
221
- });
222
- gradientArr.unshift({ stop: 0, value: 'rgba(0,0,0,0)' });
223
-
224
- me.set("gradientArr", gradientArr);
225
- },
226
- createGradientImage: function(){
227
- var me = this,
228
- gradArr = me.get("gradientArr"),
229
- length = gradArr.length,
230
- canvas = document.createElement("canvas"),
231
- ctx = canvas.getContext("2d"),
232
- grad;
233
- // the gradient in the legend including the ticks will be 256x15px
234
- canvas.width = "256";
235
- canvas.height = "15";
236
-
237
- grad = ctx.createLinearGradient(0,5,256,10);
238
-
239
- for(var i = 0; i < length; i++){
240
- grad.addColorStop(1/(length-1) * i, gradArr[i].value);
241
- }
242
-
243
- ctx.fillStyle = grad;
244
- ctx.fillRect(0,5,256,10);
245
- ctx.strokeStyle = "black";
246
- ctx.beginPath();
247
-
248
- for(var i = 0; i < length; i++){
249
- ctx.moveTo(((1/(length-1)*i*256) >> 0)+.5, 0);
250
- ctx.lineTo(((1/(length-1)*i*256) >> 0)+.5, (i==0)?15:5);
251
- }
252
- ctx.moveTo(255.5, 0);
253
- ctx.lineTo(255.5, 15);
254
- ctx.moveTo(255.5, 4.5);
255
- ctx.lineTo(0, 4.5);
256
-
257
- ctx.stroke();
258
-
259
- // we re-use the context for measuring the legends label widths
260
- me.set("ctx", ctx);
261
-
262
- return canvas.toDataURL();
263
- },
264
- getElement: function(){
265
- return this.get("element");
266
- },
267
- update: function(max){
268
- var me = this,
269
- gradient = me.get("gradientArr"),
270
- ctx = me.get("ctx"),
271
- labels = me.get("labelsEl"),
272
- labelText, labelsHtml = "", offset;
273
-
274
- for(var i = 0; i < gradient.length; i++){
275
-
276
- labelText = max*gradient[i].stop >> 0;
277
- offset = (ctx.measureText(labelText).width/2) >> 0;
278
-
279
- if(i == 0){
280
- offset = 0;
281
- }
282
- if(i == gradient.length-1){
283
- offset *= 2;
284
- }
285
- labelsHtml += '<li style="position:absolute;left:'+(((((1/(gradient.length-1)*i*256) || 0)) >> 0)-offset+.5)+'px">'+labelText+'</li>';
286
- }
287
- labels.innerHTML = labelsHtml;
288
- }
289
- };
290
-
291
- // heatmap object constructor
292
- var heatmap = function heatmap(config){
293
- // private variables
294
- var _ = {
295
- radius : 40,
296
- element : {},
297
- canvas : {},
298
- acanvas: {},
299
- ctx : {},
300
- actx : {},
301
- legend: null,
302
- visible : true,
303
- width : 0,
304
- height : 0,
305
- max : false,
306
- gradient : false,
307
- opacity: 180,
308
- premultiplyAlpha: false,
309
- bounds: {
310
- l: 1000,
311
- r: 0,
312
- t: 1000,
313
- b: 0
314
- },
315
- debug: false
316
- };
317
- // heatmap store containing the datapoints and information about the maximum
318
- // accessible via instance.store
319
- this.store = new store(this);
320
-
321
- this.get = function(key){
322
- return _[key];
323
- };
324
- this.set = function(key, value){
325
- _[key] = value;
326
- };
327
- // configure the heatmap when an instance gets created
328
- this.configure(config);
329
- // and initialize it
330
- this.init();
331
- };
332
-
333
- // public functions
334
- heatmap.prototype = {
335
- configure: function(config){
336
- var me = this,
337
- rout, rin;
338
-
339
- me.set("radius", config["radius"] || 40);
340
- me.set("element", (config.element instanceof Object)?config.element:document.getElementById(config.element));
341
- me.set("visible", (config.visible != null)?config.visible:true);
342
- me.set("max", config.max || false);
343
- me.set("gradient", config.gradient || { 0.45: "rgb(0,0,255)", 0.55: "rgb(0,255,255)", 0.65: "rgb(0,255,0)", 0.95: "yellow", 1.0: "rgb(255,0,0)"}); // default is the common blue to red gradient
344
- me.set("opacity", parseInt(255/(100/config.opacity), 10) || 180);
345
- me.set("width", config.width || 0);
346
- me.set("height", config.height || 0);
347
- me.set("debug", config.debug);
348
-
349
- if(config.legend){
350
- var legendCfg = config.legend;
351
- legendCfg.gradient = me.get("gradient");
352
- me.set("legend", new legend(legendCfg));
353
- }
354
-
355
- },
356
- resize: function () {
357
- var me = this,
358
- element = me.get("element"),
359
- canvas = me.get("canvas"),
360
- acanvas = me.get("acanvas");
361
- canvas.width = acanvas.width = me.get("width") || element.style.width.replace(/px/, "") || me.getWidth(element);
362
- this.set("width", canvas.width);
363
- canvas.height = acanvas.height = me.get("height") || element.style.height.replace(/px/, "") || me.getHeight(element);
364
- this.set("height", canvas.height);
365
- },
366
-
367
- init: function(){
368
- var me = this,
369
- canvas = document.createElement("canvas"),
370
- acanvas = document.createElement("canvas"),
371
- ctx = canvas.getContext("2d"),
372
- actx = acanvas.getContext("2d"),
373
- element = me.get("element");
374
-
375
-
376
- me.initColorPalette();
377
-
378
- me.set("canvas", canvas);
379
- me.set("ctx", ctx);
380
- me.set("acanvas", acanvas);
381
- me.set("actx", actx);
382
-
383
- me.resize();
384
- canvas.style.cssText = acanvas.style.cssText = "position:absolute;top:0;left:0;z-index:10000000;";
385
-
386
- if(!me.get("visible"))
387
- canvas.style.display = "none";
388
-
389
- element.appendChild(canvas);
390
- if(me.get("legend")){
391
- element.appendChild(me.get("legend").getElement());
392
- }
393
-
394
- // debugging purposes only
395
- if(me.get("debug"))
396
- document.body.appendChild(acanvas);
397
-
398
- actx.shadowOffsetX = 15000;
399
- actx.shadowOffsetY = 15000;
400
- actx.shadowBlur = 15;
401
- },
402
- initColorPalette: function(){
403
-
404
- var me = this,
405
- canvas = document.createElement("canvas"),
406
- gradient = me.get("gradient"),
407
- ctx, grad, testData;
408
-
409
- canvas.width = "1";
410
- canvas.height = "256";
411
- ctx = canvas.getContext("2d");
412
- grad = ctx.createLinearGradient(0,0,1,256);
413
-
414
- // Test how the browser renders alpha by setting a partially transparent pixel
415
- // and reading the result. A good browser will return a value reasonably close
416
- // to what was set. Some browsers (e.g. on Android) will return a ridiculously wrong value.
417
- testData = ctx.getImageData(0,0,1,1);
418
- testData.data[0] = testData.data[3] = 64; // 25% red & alpha
419
- testData.data[1] = testData.data[2] = 0; // 0% blue & green
420
- ctx.putImageData(testData, 0, 0);
421
- testData = ctx.getImageData(0,0,1,1);
422
- me.set("premultiplyAlpha", (testData.data[0] < 60 || testData.data[0] > 70));
423
-
424
- for(var x in gradient){
425
- grad.addColorStop(x, gradient[x]);
426
- }
427
-
428
- ctx.fillStyle = grad;
429
- ctx.fillRect(0,0,1,256);
430
-
431
- me.set("gradient", ctx.getImageData(0,0,1,256).data);
432
- },
433
- getWidth: function(element){
434
- var width = element.offsetWidth;
435
- if(element.style.paddingLeft){
436
- width+=element.style.paddingLeft;
437
- }
438
- if(element.style.paddingRight){
439
- width+=element.style.paddingRight;
440
- }
441
-
442
- return width;
443
- },
444
- getHeight: function(element){
445
- var height = element.offsetHeight;
446
- if(element.style.paddingTop){
447
- height+=element.style.paddingTop;
448
- }
449
- if(element.style.paddingBottom){
450
- height+=element.style.paddingBottom;
451
- }
452
-
453
- return height;
454
- },
455
- colorize: function(x, y){
456
- // get the private variables
457
- var me = this,
458
- width = me.get("width"),
459
- radius = me.get("radius"),
460
- height = me.get("height"),
461
- actx = me.get("actx"),
462
- ctx = me.get("ctx"),
463
- x2 = radius * 3,
464
- premultiplyAlpha = me.get("premultiplyAlpha"),
465
- palette = me.get("gradient"),
466
- opacity = me.get("opacity"),
467
- bounds = me.get("bounds"),
468
- left, top, bottom, right,
469
- image, imageData, length, alpha, offset, finalAlpha;
470
-
471
- if(x != null && y != null){
472
- if(x+x2>width){
473
- x=width-x2;
474
- }
475
- if(x<0){
476
- x=0;
477
- }
478
- if(y<0){
479
- y=0;
480
- }
481
- if(y+x2>height){
482
- y=height-x2;
483
- }
484
- left = x;
485
- top = y;
486
- right = x + x2;
487
- bottom = y + x2;
488
-
489
- }else{
490
- if(bounds['l'] < 0){
491
- left = 0;
492
- }else{
493
- left = bounds['l'];
494
- }
495
- if(bounds['r'] > width){
496
- right = width;
497
- }else{
498
- right = bounds['r'];
499
- }
500
- if(bounds['t'] < 0){
501
- top = 0;
502
- }else{
503
- top = bounds['t'];
504
- }
505
- if(bounds['b'] > height){
506
- bottom = height;
507
- }else{
508
- bottom = bounds['b'];
509
- }
510
- }
511
-
512
- image = actx.getImageData(left, top, right-left, bottom-top);
513
- imageData = image.data;
514
- length = imageData.length;
515
- // loop thru the area
516
- for(var i=3; i < length; i+=4){
517
-
518
- // [0] -> r, [1] -> g, [2] -> b, [3] -> alpha
519
- alpha = imageData[i],
520
- offset = alpha*4;
521
-
522
- if(!offset)
523
- continue;
524
-
525
- // we ve started with i=3
526
- // set the new r, g and b values
527
- finalAlpha = (alpha < opacity)?alpha:opacity;
528
- imageData[i-3]=palette[offset];
529
- imageData[i-2]=palette[offset+1];
530
- imageData[i-1]=palette[offset+2];
531
-
532
- if (premultiplyAlpha) {
533
- // To fix browsers that premultiply incorrectly, we'll pass in a value scaled
534
- // appropriately so when the multiplication happens the correct value will result.
535
- imageData[i-3] /= 255/finalAlpha;
536
- imageData[i-2] /= 255/finalAlpha;
537
- imageData[i-1] /= 255/finalAlpha;
538
- }
539
-
540
- // we want the heatmap to have a gradient from transparent to the colors
541
- // as long as alpha is lower than the defined opacity (maximum), we'll use the alpha value
542
- imageData[i] = finalAlpha;
543
- }
544
- // the rgb data manipulation didn't affect the ImageData object(defined on the top)
545
- // after the manipulation process we have to set the manipulated data to the ImageData object
546
- image.data = imageData;
547
- ctx.putImageData(image, left, top);
548
- },
549
- drawAlpha: function(x, y, count, colorize){
550
- // storing the variables because they will be often used
551
- var me = this,
552
- radius = me.get("radius"),
553
- ctx = me.get("actx"),
554
- max = me.get("max"),
555
- bounds = me.get("bounds"),
556
- xb = x - (1.5 * radius) >> 0, yb = y - (1.5 * radius) >> 0,
557
- xc = x + (1.5 * radius) >> 0, yc = y + (1.5 * radius) >> 0;
558
-
559
- ctx.shadowColor = ('rgba(0,0,0,'+((count)?(count/me.store.max):'0.1')+')');
560
-
561
- ctx.shadowOffsetX = 15000;
562
- ctx.shadowOffsetY = 15000;
563
- ctx.shadowBlur = 15;
564
-
565
- ctx.beginPath();
566
- ctx.arc(x - 15000, y - 15000, radius, 0, Math.PI * 2, true);
567
- ctx.closePath();
568
- ctx.fill();
569
- if(colorize){
570
- // finally colorize the area
571
- me.colorize(xb,yb);
572
- }else{
573
- // or update the boundaries for the area that then should be colorized
574
- if(xb < bounds["l"]){
575
- bounds["l"] = xb;
576
- }
577
- if(yb < bounds["t"]){
578
- bounds["t"] = yb;
579
- }
580
- if(xc > bounds['r']){
581
- bounds['r'] = xc;
582
- }
583
- if(yc > bounds['b']){
584
- bounds['b'] = yc;
585
- }
586
- }
587
- },
588
- toggleDisplay: function(){
589
- var me = this,
590
- visible = me.get("visible"),
591
- canvas = me.get("canvas");
592
-
593
- if(!visible)
594
- canvas.style.display = "block";
595
- else
596
- canvas.style.display = "none";
597
-
598
- me.set("visible", !visible);
599
- },
600
- // dataURL export
601
- getImageData: function(){
602
- return this.get("canvas").toDataURL();
603
- },
604
- clear: function(){
605
- var me = this,
606
- w = me.get("width"),
607
- h = me.get("height");
608
-
609
- me.store.set("data",[]);
610
- // @TODO: reset stores max to 1
611
- //me.store.max = 1;
612
- me.get("ctx").clearRect(0,0,w,h);
613
- me.get("actx").clearRect(0,0,w,h);
614
- },
615
- cleanup: function(){
616
- var me = this;
617
- me.get("element").removeChild(me.get("canvas"));
618
- }
619
- };
620
-
621
- return {
622
- create: function(config){
623
- return new heatmap(config);
624
- },
625
- util: {
626
- mousePosition: function(ev){
627
- // this doesn't work right
628
- // rather use
629
- /*
630
- // this = element to observe
631
- var x = ev.pageX - this.offsetLeft;
632
- var y = ev.pageY - this.offsetTop;
633
-
634
- */
635
- var x, y;
636
-
637
- if (ev.layerX) { // Firefox
638
- x = ev.layerX;
639
- y = ev.layerY;
640
- } else if (ev.offsetX) { // Opera
641
- x = ev.offsetX;
642
- y = ev.offsetY;
643
- }
644
- if(typeof(x)=='undefined')
645
- return;
646
-
647
- return [x,y];
648
- }
649
- }
650
- };
651
- })();
652
- w.h337 = w.heatmapFactory = heatmapFactory;
653
- })(window);