| 1 | /** |
|---|
| 2 | ClickHeat : Suivi et analyse des clics / Tracking and clicks analysis |
|---|
| 3 | |
|---|
| 4 | @author Yvan Taviaud - LabsMedia - www.labsmedia.com/clickheat/ |
|---|
| 5 | @since 27/10/2006 |
|---|
| 6 | @update 01/03/2007 - Yvan Taviaud : correctif Firefox (Károly Marton) |
|---|
| 7 | @update 23/03/2007 - Yvan Taviaud : protection de 2 secondes entre chaque clic, et X clics maximum par page |
|---|
| 8 | @update 18/05/2007 - Yvan Taviaud : suppression de clickHeatPage, ajout de clickHeatGroup et clickHeatSite |
|---|
| 9 | @update 27/08/2007 - Yvan Taviaud : changement du systÚme de débug |
|---|
| 10 | @update 28/09/2007 - Yvan Taviaud : ajout de quelques messages de débug |
|---|
| 11 | @update 16/03/2008 - Yvan Taviaud : utilisation des Listeners - ajout d'un délai pour enregistrer le clic correctement - correctif JSLint |
|---|
| 12 | @update 05/07/2010 - Yvan Taviaud : ajout de Chrome, ajout du test non-Ajax pour libérer le clic plus rapidement |
|---|
| 13 | @update 13/08/2010 - Yvan Taviaud : gestion de IE 8 qui posait des soucis sur l'event |
|---|
| 14 | |
|---|
| 15 | Tested under : |
|---|
| 16 | Windows 2000 - IE 6.0 |
|---|
| 17 | Linux - Firefox 2.0.0.1, Konqueror 3.5.5, IE 7 |
|---|
| 18 | */ |
|---|
| 19 | |
|---|
| 20 | /** Main variables */ |
|---|
| 21 | var clickHeatGroup = ''; |
|---|
| 22 | var clickHeatSite = ''; |
|---|
| 23 | var clickHeatServer = ''; |
|---|
| 24 | var clickHeatLastIframe = -1; |
|---|
| 25 | var clickHeatTime = 0; |
|---|
| 26 | var clickHeatQuota = -1; |
|---|
| 27 | var clickHeatBrowser = ''; |
|---|
| 28 | var clickHeatDocument = ''; |
|---|
| 29 | var clickHeatWait = 500; |
|---|
| 30 | var clickHeatLocalWait = 0; |
|---|
| 31 | var clickHeatDebug = (window.location.href.search(/debugclickheat/) != -1); |
|---|
| 32 | |
|---|
| 33 | /** |
|---|
| 34 | * Shows a debug string |
|---|
| 35 | **/ |
|---|
| 36 | function showClickHeatDebug(str) |
|---|
| 37 | { |
|---|
| 38 | if (clickHeatDebug === true) |
|---|
| 39 | { |
|---|
| 40 | document.getElementById('clickHeatDebuggerSpan').innerHTML = str; |
|---|
| 41 | document.getElementById('clickHeatDebuggerDiv').style.display = 'block'; |
|---|
| 42 | } |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | /** Main function */ |
|---|
| 46 | function catchClickHeat(e) |
|---|
| 47 | { |
|---|
| 48 | /** Use a try{} to avoid showing errors to users */ |
|---|
| 49 | try |
|---|
| 50 | { |
|---|
| 51 | showClickHeatDebug('Gathering click data...'); |
|---|
| 52 | if (clickHeatQuota === 0) |
|---|
| 53 | { |
|---|
| 54 | showClickHeatDebug('Click not logged: quota reached'); |
|---|
| 55 | return true; |
|---|
| 56 | } |
|---|
| 57 | if (clickHeatGroup === '') |
|---|
| 58 | { |
|---|
| 59 | showClickHeatDebug('Click not logged: group name empty (clickHeatGroup)'); |
|---|
| 60 | return true; |
|---|
| 61 | } |
|---|
| 62 | /** Look for the real event */ |
|---|
| 63 | if (e === undefined) |
|---|
| 64 | { |
|---|
| 65 | e = window.event; |
|---|
| 66 | } |
|---|
| 67 | c = e.which || e.button; |
|---|
| 68 | element = e.srcElement || null; |
|---|
| 69 | if (c === 0) |
|---|
| 70 | { |
|---|
| 71 | showClickHeatDebug('Click not logged: no button pressed'); |
|---|
| 72 | return true; |
|---|
| 73 | } |
|---|
| 74 | /** Filter for same iframe (focus on iframe => popup ad => close ad => new focus on same iframe) */ |
|---|
| 75 | if (element !== null && element.tagName.toLowerCase() == 'iframe') |
|---|
| 76 | { |
|---|
| 77 | if (element.sourceIndex == clickHeatLastIframe) |
|---|
| 78 | { |
|---|
| 79 | showClickHeatDebug('Click not logged: same iframe (a click on iframe opens a popup and popup is closed => iframe gets the focus again)'); |
|---|
| 80 | return true; |
|---|
| 81 | } |
|---|
| 82 | clickHeatLastIframe = element.sourceIndex; |
|---|
| 83 | } |
|---|
| 84 | else |
|---|
| 85 | { |
|---|
| 86 | clickHeatLastIframe = -1; |
|---|
| 87 | } |
|---|
| 88 | var x = e.clientX; |
|---|
| 89 | var y = e.clientY; |
|---|
| 90 | var w = clickHeatDocument.clientWidth !== undefined ? clickHeatDocument.clientWidth : window.innerWidth; |
|---|
| 91 | var h = clickHeatDocument.clientHeight !== undefined ? clickHeatDocument.clientHeight : window.innerHeight; |
|---|
| 92 | var scrollx = window.pageXOffset === undefined ? clickHeatDocument.scrollLeft : window.pageXOffset; |
|---|
| 93 | var scrolly = window.pageYOffset === undefined ? clickHeatDocument.scrollTop : window.pageYOffset; |
|---|
| 94 | /** Is the click in the viewing area? Not on scrollbars. The problem still exists for FF on the horizontal scrollbar */ |
|---|
| 95 | if (x > w || y > h) |
|---|
| 96 | { |
|---|
| 97 | showClickHeatDebug('Click not logged: out of document (should be a click on scrollbars)'); |
|---|
| 98 | return true; |
|---|
| 99 | } |
|---|
| 100 | /** Check if last click was at least 1 second ago */ |
|---|
| 101 | clickTime = new Date(); |
|---|
| 102 | if (clickTime.getTime() - clickHeatTime < 1000) |
|---|
| 103 | { |
|---|
| 104 | showClickHeatDebug('Click not logged: at least 1 second between clicks'); |
|---|
| 105 | return true; |
|---|
| 106 | } |
|---|
| 107 | clickHeatTime = clickTime.getTime(); |
|---|
| 108 | if (clickHeatQuota > 0) |
|---|
| 109 | { |
|---|
| 110 | clickHeatQuota = clickHeatQuota - 1; |
|---|
| 111 | } |
|---|
| 112 | params = 's=' + clickHeatSite + '&g=' + clickHeatGroup + '&x=' + (x + scrollx) + '&y=' + (y + scrolly) + '&w=' + w + '&b=' + clickHeatBrowser + '&c=' + c + '&random=' + Date(); |
|---|
| 113 | showClickHeatDebug('Ready to send click data...'); |
|---|
| 114 | /** Local request? Try an ajax call */ |
|---|
| 115 | var sent = false; |
|---|
| 116 | if (clickHeatServer.substring(0, 4) != 'http') |
|---|
| 117 | { |
|---|
| 118 | var xmlhttp = false; |
|---|
| 119 | try |
|---|
| 120 | { |
|---|
| 121 | xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); |
|---|
| 122 | } |
|---|
| 123 | catch (er) |
|---|
| 124 | { |
|---|
| 125 | try |
|---|
| 126 | { |
|---|
| 127 | xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); |
|---|
| 128 | } |
|---|
| 129 | catch (oc) |
|---|
| 130 | { |
|---|
| 131 | xmlhttp = null; |
|---|
| 132 | } |
|---|
| 133 | } |
|---|
| 134 | if (!xmlhttp && typeof XMLHttpRequest !== undefined) |
|---|
| 135 | { |
|---|
| 136 | xmlhttp = new XMLHttpRequest(); |
|---|
| 137 | } |
|---|
| 138 | if (xmlhttp) |
|---|
| 139 | { |
|---|
| 140 | if (clickHeatDebug === true) |
|---|
| 141 | { |
|---|
| 142 | xmlhttp.onreadystatechange = function() |
|---|
| 143 | { |
|---|
| 144 | if (xmlhttp.readyState == 4) |
|---|
| 145 | { |
|---|
| 146 | if (xmlhttp.status == 200) |
|---|
| 147 | { |
|---|
| 148 | showClickHeatDebug('Click recorded at ' + clickHeatServer + ' with the following parameters:<br/>x = ' + (x + scrollx) + ' (' + x + 'px from left + ' + scrollx + 'px of horizontal scrolling)<br/>y = ' + (y + scrolly) + ' (' + y + 'px from top + ' + scrolly + 'px of vertical scrolling)<br/>width = ' + w + '<br/>browser = ' + clickHeatBrowser + '<br/>click = ' + c + '<br/>site = ' + clickHeatSite + '<br/>group = ' + clickHeatGroup + '<br/><br/>Server answer: ' + xmlhttp.responseText); |
|---|
| 149 | } |
|---|
| 150 | else if (xmlhttp.status == 404) |
|---|
| 151 | { |
|---|
| 152 | showClickHeatDebug('click.php was not found at: ' + (clickHeatServer !== '' ? clickHeatServer : '/clickheat/click.php') + ' please set clickHeatServer value'); |
|---|
| 153 | } |
|---|
| 154 | else |
|---|
| 155 | { |
|---|
| 156 | showClickHeatDebug('click.php returned a status code ' + xmlhttp.status + ' with the following error: ' + xmlhttp.responseText); |
|---|
| 157 | } |
|---|
| 158 | /** Stop waiting */ |
|---|
| 159 | clickHeatLocalWait = 0; |
|---|
| 160 | } |
|---|
| 161 | }; |
|---|
| 162 | } |
|---|
| 163 | xmlhttp.open('GET', clickHeatServer + '?' + params, true); |
|---|
| 164 | xmlhttp.setRequestHeader('Connection', 'close'); |
|---|
| 165 | xmlhttp.send(null); |
|---|
| 166 | sent = true; |
|---|
| 167 | } |
|---|
| 168 | } |
|---|
| 169 | if (sent === false) |
|---|
| 170 | { |
|---|
| 171 | /** This test is needed, as it includes the call to click.php in the iframe */ |
|---|
| 172 | if (clickHeatDebug === true) |
|---|
| 173 | { |
|---|
| 174 | showClickHeatDebug('Click recorded at ' + clickHeatServer + ' with the following parameters:<br/>x = ' + (x + scrollx) + ' (' + x + 'px from left + ' + scrollx + 'px of horizontal scrolling)<br/>y = ' + (y + scrolly) + ' (' + y + 'px from top + ' + scrolly + 'px of vertical scrolling)<br/>width = ' + w + '<br/>browser = ' + clickHeatBrowser + '<br/>click = ' + c + '<br/>site = ' + clickHeatSite + '<br/>group = ' + clickHeatGroup + '<br/><br/>Server answer:<br/>' + '<iframe src="' + clickHeatServer + '?' + params + '" width="700" height="60"></iframe>'); |
|---|
| 175 | } |
|---|
| 176 | else |
|---|
| 177 | { |
|---|
| 178 | var clickHeatImg = new Image(); |
|---|
| 179 | clickHeatImg.src = clickHeatServer + '?' + params; |
|---|
| 180 | // clickHeatImg.onload = function() { clickHeatLocalWait = 0; } |
|---|
| 181 | } |
|---|
| 182 | } |
|---|
| 183 | /** Little waiting cycle: default is to wait until Ajax sent or until the end of the time if no Ajax is available */ |
|---|
| 184 | var now = new Date(); |
|---|
| 185 | clickHeatLocalWait = now.getTime() + clickHeatWait; |
|---|
| 186 | while (clickHeatLocalWait > now.getTime()) |
|---|
| 187 | { |
|---|
| 188 | now = new Date(); |
|---|
| 189 | } |
|---|
| 190 | } |
|---|
| 191 | catch(err) |
|---|
| 192 | { |
|---|
| 193 | showClickHeatDebug('An error occurred while processing click (Javascript error): ' + err.message); |
|---|
| 194 | } |
|---|
| 195 | return true; |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | function initClickHeat() |
|---|
| 199 | { |
|---|
| 200 | /** Debug Window */ |
|---|
| 201 | if (clickHeatDebug === true) |
|---|
| 202 | { |
|---|
| 203 | document.write('<div id="clickHeatDebuggerDiv" style="padding:5px; display:none; position:absolute; top:200px; left:200px; border:1px solid #888; background-color:#eee; z-index:99;"><strong>ClickHeat debug: <a href="#" onmouseover="document.getElementById(\'clickHeatDebuggerDiv\').style.display = \'none\'; return false">Rollover to close</a></strong><br/><br/><span id="clickHeatDebuggerSpan"></span></div>'); |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | if (clickHeatGroup === '' || clickHeatServer === '') |
|---|
| 207 | { |
|---|
| 208 | showClickHeatDebug('ClickHeat NOT initialised: either clickHeatGroup or clickHeatServer is empty'); |
|---|
| 209 | return false; |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | /** If current website has the same domain as the script, we remove the domain so that the call is made using Ajax */ |
|---|
| 213 | domain = window.location.href.match(/http:\/\/[^\/]+\//); |
|---|
| 214 | if (domain !== null && clickHeatServer.substring(0, domain[0].length) == domain[0]) |
|---|
| 215 | { |
|---|
| 216 | clickHeatServer = clickHeatServer.substring(domain[0].length - 1, clickHeatServer.length); |
|---|
| 217 | } |
|---|
| 218 | /** Add onmousedown event using listeners */ |
|---|
| 219 | if (document.addEventListener) |
|---|
| 220 | { |
|---|
| 221 | document.addEventListener('mousedown', catchClickHeat, false); |
|---|
| 222 | } |
|---|
| 223 | else if (document.attachEvent) |
|---|
| 224 | { |
|---|
| 225 | document.attachEvent('onmousedown', catchClickHeat); |
|---|
| 226 | } |
|---|
| 227 | /** Add onfocus event on iframes (mostly ads) - Does NOT work with Gecko-powered browsers, because onfocus doesn't exist on iframes */ |
|---|
| 228 | iFrames = document.getElementsByTagName('iframe'); |
|---|
| 229 | for (var i = 0; i < iFrames.length; i++) |
|---|
| 230 | { |
|---|
| 231 | if (document.addEventListener) |
|---|
| 232 | { |
|---|
| 233 | iFrames[i].addEventListener('focus', catchClickHeat, false); |
|---|
| 234 | } |
|---|
| 235 | else if (document.attachEvent) |
|---|
| 236 | { |
|---|
| 237 | iFrames[i].attachEvent('onfocus', catchClickHeat); |
|---|
| 238 | } |
|---|
| 239 | } |
|---|
| 240 | /** Preparing main variables */ |
|---|
| 241 | clickHeatDocument = (document.documentElement !== undefined && document.documentElement.clientHeight !== 0) ? document.documentElement : document.body; |
|---|
| 242 | /** Also the User-Agent is not the best value to use, it's the only one that gives the real browser */ |
|---|
| 243 | var b = navigator.userAgent !== undefined ? navigator.userAgent.toLowerCase().replace(/-/g, '') : ''; |
|---|
| 244 | /** Always test Chrome before Safari */ |
|---|
| 245 | var browsers = ['chrome', 'firefox', 'safari', 'msie', 'opera']; |
|---|
| 246 | clickHeatBrowser = 'unknown'; |
|---|
| 247 | for (var j = 0; j < browsers.length; j++) |
|---|
| 248 | { |
|---|
| 249 | if (b.indexOf(browsers[j]) != -1) |
|---|
| 250 | { |
|---|
| 251 | clickHeatBrowser = browsers[j]; |
|---|
| 252 | break; |
|---|
| 253 | } |
|---|
| 254 | } |
|---|
| 255 | showClickHeatDebug('ClickHeat initialised with:<br/>site = ' + clickHeatSite + '<br/>group = ' + clickHeatGroup + '<br/>server = ' + clickHeatServer + '<br/>quota = ' + (clickHeatQuota == -1 ? 'unlimited' : clickHeatQuota) + '<br/><br/>browser = ' + clickHeatBrowser + '<br/><strong>Click in a blank area (not on a link) to test ClickHeat</strong>'); |
|---|
| 256 | } |
|---|