Repository: InteractiveAdvertisingBureau/AdBlockDetection
Branch: master
Commit: e001ef875408
Files: 5
Total size: 39.4 KB
Directory structure:
gitextract_l8e06rrt/
├── License.md
├── README.md
├── adblockDetector.js
├── adblockDetectorWithGA.js
└── test.html
================================================
FILE CONTENTS
================================================
================================================
FILE: License.md
================================================
BSD 3-Clause License
Copyright (c) 2017,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# Introduction
Javascript to detect the presence of behavior associated with ad blocking during delivery of a page.
# Operation
The JavaScript (adblockDetector.js) has been tested to detect the behaviors associated with ad blocking in the following web browsers:
- Google Chrome
- Mozilla Firefox
- Internet Explorer (8+)
- Safari
The script does this by creating a set of DIVs that are likely to be hidden by browser-based ad blocking tools.
Additional tactics that are not yet included in this script:
- URL bait. Allow the detection of network-based ad blocking.
- Dynamic bait modification. Update DIV and URL attributes based on the existing blocking lists (Easylist and so on) to make detection more robust.
# Installation
Download the desired detection script and add it to your website. There are a few different ways to include JavaScript into HTML.
| Script Name | Description |
| ------------- |:-------------:|
| adblockDetector.js | Adblocker detection script without Google Analytics module |
| adblockDetectorWithGA.js | Adblocker detection script with Google Analytics module |
With AdBlockDetectionWithGA.js you are asked to mention your GA tracking id into the script on line no 82. When you are referencing this script, it tracks certain events regarding AdBlock on user browser. You can view the details in the Google Analytics dashboard. Here is how to check whether user are using any adblock or not.
###### Setting up Google Analytics
Firstly, we would suggest you create a different GA-Tracking id so that it might not interfere with your pageviews. Follow below steps for GA on Use of Adblock.
- Sign into your Google Analytics account -> Go to your site -> Go to “Reporting” tab -> click “User Explorer” under Audience.
- Click on “Add new segment” -> “New Segment” -> Give Segment name (ex: ‘Adblock Detected’) -> Click on “conditions” under Advanced section.
- Click on Sessions and select Users (You can create a different one for sessions too.)
- Click on the first Drop Down -> Click on “Behavior” ->Select “Event Label”.
- Click On the Text box: Type event label as below.
- Event Label- “div hidden” – this will give you all users with Ad block enable/found.Now your one segment with Users who use ad block is ready.
- Repeat all above steps with Below event label for users who do not use ad block.
- Event Label- “div visible” –this will give you all users with ad block disabled/notfound.
Unfortunately we have not figured out yet how to put it to dash board. So next time when you go to GA, you can go to User Explorer -> click Add new segment. And you will find the segments you previously created(i.e one for adblock Detected users and one for ad block NotDetected users.). You can select them and click on apply to see reported data.
It should look like below image.

###### Inline
This is the recommended method of inclusion. The functions contained in the chosen detection script should be included directly into the HTML of the parent frame.
Do this by wrapping the content of the selected code in script tags in the delivered HTML.
###### External Script File
It is possible to host the selected code on your web server as an independent file, and to reference this file from the delivered HTML.
If you use an external script file, it can be blocked by ad blockers. Using a different name for the file will reduce the probability that it will be blocked by generic filters.
###### Other Methods
It is possible to integrate the functions from the selected code into an existing script library, hosted as an external script file. Doing this may result in reduced site functionality for visitors using ad blockers that are trying to avoid detection, if the ad blockers block the entire external script file.
# Configuration
@prop flags
| Option | Type | Description |
| ------------- |:-------------:| :-----:|
| debug | Boolean | Indicates additional debug output should be printed to console |
| found | String (@function) | Function to fire if adblock is detected |
| notfound | String (@function) | Function to fire if adblock is not detected. Note that this will fire each time adblock is not detected, and should provide input to action taken only after “complete” is detected. |
| complete | String (@function) | Function to fire once testing is complete. |
The test result (boolean) is included as a parameter to callback
example:
```javascript
window.adblockDetector.init(
{
found: function(){ ...},
notFound: function(){...}
}
);
```
# Usage
Add the below code in the HTML page.
```javascript
```
Add below code in the body of the HTML page
```html
AdBlock is disabled
AdBlock is enabled
```
# Contributing
Fork it!
Create your feature branch: git checkout -b my-new-feature
Commit your changes: git commit -am 'Add some feature'
Push to the branch: git push origin my-new-feature
Submit a pull request
# FAQ
- The script will not work locally. The page should get served from the server via http
- Currently the “baits” or “honey pods” used in the Javascript are hardcoded @ #218. If you want to update the file with new baits update the line #218
- Use the sample file test.html find in the repository. Host the file on HTTP server eg. apache server and request the file in browser via http with adblocker on/off.
================================================
FILE: adblockDetector.js
================================================
// ===============================================
// AdBlock detector
//
// Attempts to detect the presence of Ad Blocker software and notify listener of its existence.
// Copyright (c) 2017 IAB
//
// The BSD-3 License
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ===============================================
/**
* @name window.adblockDetector
*
* IAB Adblock detector.
* Usage: window.adblockDetector.init(options);
*
* Options object settings
*
* @prop debug: boolean
* Flag to indicate additional debug output should be printed to console
*
* @prop found: @function
* Callback function to fire if adblock is detected
*
* @prop notfound: @function
* Callback function to fire if adblock is not detected.
* NOTE: this function may fire multiple times and give false negative
* responses during a test until adblock is successfully detected.
*
* @prop complete: @function
* Callback function to fire once a round of testing is complete.
* The test result (boolean) is included as a parameter to callback
*
* example: window.adblockDetector.init(
{
found: function(){ ...},
notFound: function(){...}
}
);
*
*
*/
"use strict";
(function(win) {
var version = '1.0';
var ofs = 'offset', cl = 'client';
var noop = function(){};
var testedOnce = false;
var testExecuting = false;
var isOldIEevents = (win.addEventListener === undefined);
/**
* Options set with default options initialized
*
*/
var _options = {
loopDelay: 50,
maxLoop: 5,
debug: true,
found: noop, // function to fire when adblock detected
notfound: noop, // function to fire if adblock not detected after testing
complete: noop // function to fire after testing completes, passing result as parameter
}
function parseAsJson(data){
var result, fnData;
try{
result = JSON.parse(data);
}
catch(ex){
try{
fnData = new Function("return " + data);
result = fnData();
}
catch(ex){
log('Failed secondary JSON parse', true);
}
}
return result;
}
/**
* Ajax helper object to download external scripts.
* Initialize object with an options object
* Ex:
{
url : 'http://example.org/url_to_download',
method: 'POST|GET',
success: callback_function,
fail: callback_function
}
*/
var AjaxHelper = function(opts){
var xhr = new XMLHttpRequest();
this.success = opts.success || noop;
this.fail = opts.fail || noop;
var me = this;
var method = opts.method || 'get';
/**
* Abort the request
*/
this.abort = function(){
try{
xhr.abort();
}
catch(ex){
}
}
function stateChange(vals){
if(xhr.readyState == 4){
if(xhr.status == 200){
me.success(xhr.response);
}
else{
// failed
me.fail(xhr.status);
}
}
}
xhr.onreadystatechange = stateChange;
function start(){
xhr.open(method, opts.url, true);
xhr.send();
}
start();
}
/**
* Object tracking the various block lists
*/
var BlockListTracker = function(){
var me = this;
var externalBlocklistData = {};
/**
* Add a new external URL to track
*/
this.addUrl = function(url){
externalBlocklistData[url] = {
url: url,
state: 'pending',
format: null,
data: null,
result: null
}
return externalBlocklistData[url];
}
/**
* Loads a block list definition
*/
this.setResult = function(urlKey, state, data){
var obj = externalBlocklistData[urlKey];
if(obj == null){
obj = this.addUrl(urlKey);
}
obj.state = state;
if(data == null){
obj.result = null;
return;
}
if(typeof data === 'string'){
try{
data = parseAsJson(data);
obj.format = 'json';
}
catch(ex){
obj.format = 'easylist';
// parseEasyList(data);
}
}
obj.data = data;
return obj;
}
}
var listeners = []; // event response listeners
var baitNode = null;
var quickBait = {
cssClass: 'pub_300x250 pub_300x250m pub_728x90 text-ad textAd text_ad text_ads text-ads text-ad-links'
};
var baitTriggers = {
nullProps: [ofs + 'Parent'],
zeroProps: []
};
baitTriggers.zeroProps = [
ofs +'Height', ofs +'Left', ofs +'Top', ofs +'Width', ofs +'Height',
cl + 'Height', cl + 'Width'
];
// result object
var exeResult = {
quick: null,
remote: null
};
var findResult = null; // result of test for ad blocker
var timerIds = {
test: 0,
download: 0
};
function isFunc(fn){
return typeof(fn) == 'function';
}
/**
* Make a DOM element
*/
function makeEl(tag, attributes){
var k, v, el, attr = attributes;
var d = document;
el = d.createElement(tag);
if(attr){
for(k in attr){
if(attr.hasOwnProperty(k)){
el.setAttribute(k, attr[k]);
}
}
}
return el;
}
function attachEventListener(dom, eventName, handler){
if(isOldIEevents){
dom.attachEvent('on' + eventName, handler);
}
else{
dom.addEventListener(eventName, handler, false);
}
}
function log(message, isError){
if(!_options.debug && !isError){
return;
}
if(win.console && win.console.log){
if(isError){
console.error('[ABD] ' + message);
}
else{
console.log('[ABD] ' + message);
}
}
}
var ajaxDownloads = [];
/**
* Load and execute the URL inside a closure function
*/
function loadExecuteUrl(url){
var ajax, result;
blockLists.addUrl(url);
// setup call for remote list
ajax = new AjaxHelper(
{
url: url,
success: function(data){
log('downloaded file ' + url); // todo - parse and store until use
result = blockLists.setResult(url, 'success', data);
try{
var intervalId = 0,
retryCount = 0;
var tryExecuteTest = function(listData){
if(!testExecuting){
beginTest(listData, true);
return true;
}
return false;
}
if(findResult == true){
return;
}
if(tryExecuteTest(result.data)){
return;
}
else{
log('Pause before test execution');
intervalId = setInterval(function(){
if(tryExecuteTest(result.data) || retryCount++ > 5){
clearInterval(intervalId);
}
}, 250);
}
}
catch(ex){
log(ex.message + ' url: ' + url, true);
}
},
fail: function(status){
log(status, true);
blockLists.setResult(url, 'error', null);
}
});
ajaxDownloads.push(ajax);
}
/**
* Fetch the external lists and initiate the tests
*/
function fetchRemoteLists(){
var i, url;
var opts = _options;
for(i=0;i= 0;i--){
aj = ajaxDownloads.pop();
aj.abort();
}
}
// =============================================================================
/**
* Begin execution of the test
*/
function beginTest(bait){
log('start beginTest');
if(findResult == true){
return; // we found it. don't continue executing
}
testExecuting = true;
castBait(bait);
exeResult.quick = 'testing';
timerIds.test = setTimeout(
function(){ reelIn(bait, 1); },
5);
}
/**
* Create the bait node to see how the browser page reacts
*/
function castBait(bait){
var i, d = document, b = d.body;
var t;
var baitStyle = 'width: 1px !important; height: 1px !important; position: absolute !important; left: -10000px !important; top: -1000px !important;'
if(bait == null || typeof(bait) == 'string'){
log('invalid bait being cast');
return;
}
if(bait.style != null){
baitStyle += bait.style;
}
baitNode = makeEl('div', {
'class': bait.cssClass,
'style': baitStyle
});
log('adding bait node to DOM');
b.appendChild(baitNode);
// touch these properties
for(i=0;i 0){
clearTimeout(timerIds.test);
timerIds.test = 0;
}
// test for issues
if(body.getAttribute('abp') !== null){
log('found adblock body attribute');
found = true;
}
for(i=0;i4)
found = true;
log('found adblock null attr: ' + baitTriggers.nullProps[i]);
break;
}
if(found == true){
break;
}
}
for(i=0;i4)
found = true;
log('found adblock zero attr: ' + baitTriggers.zeroProps[i]);
}
}
if(window.getComputedStyle !== undefined) {
var baitTemp = window.getComputedStyle(baitNode, null);
if(baitTemp.getPropertyValue('display') == 'none'
|| baitTemp.getPropertyValue('visibility') == 'hidden') {
if(attemptNum>4)
found = true;
log('found adblock computedStyle indicator');
}
}
testedOnce = true;
if(found || attemptNum++ >= _options.maxLoop){
findResult = found;
log('exiting test loop - value: ' + findResult);
notifyListeners();
if(clearBaitNode()){
setTimeout(function(){
testExecuting = false;
}, 5);
}
}
else{
timerIds.test = setTimeout(function(){
reelIn(bait, attemptNum);
}, _options.loopDelay);
}
}
function clearBaitNode(){
if(baitNode === null){
return true;
}
try{
if(isFunc(baitNode.remove)){
baitNode.remove();
}
document.body.removeChild(baitNode);
}
catch(ex){
}
baitNode = null;
return true;
}
/**
* Halt the test and any pending timeouts
*/
function stopFishing(){
if(timerIds.test > 0){
clearTimeout(timerIds.test);
}
if(timerIds.download > 0){
clearTimeout(timerIds.download);
}
cancelRemoteDownloads();
clearBaitNode();
}
/**
* Fire all registered listeners
*/
function notifyListeners(){
var i, funcs;
if(findResult === null){
return;
}
for(i=0;i 5){
clearInterval(intervalId);
}
}, 250);
}
}
catch(ex){
log(ex.message + ' url: ' + url, true);
}
},
fail: function(status){
log(status, true);
blockLists.setResult(url, 'error', null);
}
});
ajaxDownloads.push(ajax);
}
/**
* Fetch the external lists and initiate the tests
*/
function fetchRemoteLists(){
var i, url;
var opts = _options;
for(i=0;i= 0;i--){
aj = ajaxDownloads.pop();
aj.abort();
}
}
// =============================================================================
/**
* Begin execution of the test
*/
function beginTest(bait, isRemote){
if(findResult == true){
return; // we found it. don't continue executing
}
testExecuting = true;
castBait(bait);
if(!isRemote){
exeResult.quick = 'testing';
}
else {
exeResult.remote = 'testing';
}
timerIds.test = setTimeout(
function(){ reelIn(bait, 1); },
5);
if(!isRemote && _options.testRemoteList){
fetchRemoteLists();
}
}
/**
* Create the bait node to see how the browser page reacts
*/
function castBait(bait){
var i, d = document, b = d.body;
var t;
var baitStyle = 'width: 1px !important; height: 1px !important; position: absolute !important; left: -10000px !important; top: -1000px !important;'
if(bait == null || typeof(bait) == 'string'){
log('invalid bait being cast');
return;
}
if(bait.style != null){
baitStyle += bait.style;
}
baitNode = makeEl('div', {
'class': bait.cssClass,
'style': baitStyle
});
log('adding bait node to DOM');
b.appendChild(baitNode);
// touch these properties to insure initialization
for(i=0;i 0){
clearTimeout(timerIds.test);
timerIds.test = 0;
}
// test for issues
if(body.getAttribute('abp') !== null){
log('found adblock body attribute');
found = true;
findTrigger = 'body_abp_attr';
}
if(!found){
for(i=0;i4)
found = true;
log('found adblock null attr: ' + baitTriggers.nullProps[i]);
findTrigger = 'div hidden with attribute: null attr-' + baitTriggers.nullProps[i];
break;
}
if(found == true){
break;
}
}
for(i=0;i4){
found = true;
}
findTrigger = 'div hidden with attribute: zero_attr-' + baitTriggers.zeroProps[i];
log('found adblock zero attr: ' + baitTriggers.zeroProps[i]);
}
if(baitNode[baitTriggers.zeroProps[i]] == 1){
findTrigger = 'div visible with attribute: zero_attr-' + baitTriggers.zeroProps[i];
log('found adblock zero attr: ' + baitTriggers.zeroProps[i]);
}
}
}
if(!found){
if(window.getComputedStyle !== undefined) {
var baitTemp = window.getComputedStyle(baitNode, null);
if(baitTemp.getPropertyValue('display') == 'none'
|| baitTemp.getPropertyValue('visibility') == 'hidden') {
if(attemptNum>4)
found = true;
findTrigger = 'computedStyle indicator';
log('found adblock computedStyle indicator');
}
}
}
testedOnce = true;
if(found || attemptNum++ >= _options.maxLoop){
findResult = found;
try{
if(found){
analytics.blockerDetected(findTrigger, attemptNum);
}
else{
analytics.blockerNotDetected(findTrigger, attemptNum);
}
}
catch(ex){
log(ex.message);
}
log('exiting test loop - value: ' + findResult);
notifyListeners();
if(clearBaitNode()){
setTimeout(function(){
testExecuting = false;
}, 5);
}
}
else{
timerIds.test = setTimeout(function(){
reelIn(bait, attemptNum);
}, _options.loopDelay);
}
}
function clearBaitNode(){
if(baitNode === null){
return true;
}
try{
if(isFunc(baitNode.remove)){
baitNode.remove();
}
document.body.removeChild(baitNode);
}
catch(ex){
}
baitNode = null;
return true;
}
/**
* Halt the test and any pending timeouts
*/
function stopFishing(){
if(timerIds.test > 0){
clearTimeout(timerIds.test);
}
if(timerIds.download > 0){
clearTimeout(timerIds.download);
}
cancelRemoteDownloads();
clearBaitNode();
}
/**
* Fire all registered listeners
*/
function notifyListeners(){
var i, funcs;
if(findResult === null){
return;
}
for(i=0;iAdBlock