master e001ef875408 cached
5 files
39.4 KB
10.6k tokens
35 symbols
1 requests
Download .txt
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.

![alt text](https://s3.amazonaws.com/iab-tech-lab/images/ga.png "GA User Explorer")

###### 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
<script src="./adblockDetector.js"></script>
	<script>
	// Configure the adblock detector
	(function(){
		var enabledEl = document.getElementById('adb-enabled');
		var disabledEl = document.getElementById('adb-not-enabled');
		function adBlockDetected() {
			enabledEl.style.display = 'block';
			disabledEl.style.display = 'none';
		}
		function adBlockNotDetected() {
			disabledEl.style.display = 'block';
			enabledEl.style.display = 'none';
		}
		
		if(typeof window.adblockDetector === 'undefined') {
			adBlockDetected();
		} else {
			window.adblockDetector.init(
				{
					debug: true,
					found: function(){
						adBlockDetected();
					},
					notFound: function(){
						adBlockNotDetected();
					}
				}
			);
		}
	}());
	</script>
```
 
Add below code in the body of the HTML page
```html
<div class="center">
<h5 class="bg-success" id="adb-not-enabled" style="display: none;">AdBlock is disabled</h5>
<h5 class="bg-danger" id="adb-enabled" style="display: none;">AdBlock is enabled</h5>
</div>
```

# 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<opts.blockLists.length;i++){
			url = opts.blockLists[i];
			loadExecuteUrl(url);			
		}
	}
	
	function cancelRemoteDownloads(){
		var i, aj;
		
		for(i=ajaxDownloads.length-1;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<baitTriggers.nullProps.length;i++){
			t = baitNode[baitTriggers.nullProps[i]];
		}
		for(i=0;i<baitTriggers.zeroProps.length;i++){
			t = baitNode[baitTriggers.zeroProps[i]];
		}
	}
	
	/**
	* Run tests to see if browser has taken the bait and blocked the bait element
	*/
	function reelIn(bait, attemptNum){
		var i, k, v;
		var body = document.body;
		var found = false;
		
		if(baitNode == null){
			log('recast bait');
			castBait(bait || quickBait);
		}

		if(typeof(bait) == 'string'){
			log('invalid bait used', true);
			if(clearBaitNode()){
				setTimeout(function(){
					testExecuting = false;
				}, 5);
			}

			return;
		}

		if(timerIds.test > 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;i<baitTriggers.nullProps.length;i++){
			if(baitNode[baitTriggers.nullProps[i]] == null){
				if(attemptNum>4)
				found = true;
				log('found adblock null attr: ' + baitTriggers.nullProps[i]);
				break;
			}
			if(found == true){
				break;
			}
		}
		
		for(i=0;i<baitTriggers.zeroProps.length;i++){
			if(found == true){
				break;
			}
			if(baitNode[baitTriggers.zeroProps[i]] == 0){
				if(attemptNum>4)
				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<listeners.length;i++){
			funcs = listeners[i];
			try{			
				if(funcs != null){
					if(isFunc(funcs['complete'])){
						funcs['complete'](findResult);
					}
					
					if(findResult && isFunc(funcs['found'])){
						funcs['found']();
					}
					else if(findResult === false && isFunc(funcs['notfound'])){
						funcs['notfound']();
					}
				}
			}
			catch(ex){
				log('Failure in notify listeners ' + ex.Message, true);
			}
		}
	}
	
	/**
	* Attaches event listener or fires if events have already passed.
	*/
	function attachOrFire(){
		var fireNow = false;
		var fn;
		
		if(document.readyState){
			if(document.readyState == 'complete'){
				fireNow = true;
			}
		}
		
		fn = function(){
			beginTest(quickBait, false);
		}
		
		if(fireNow){
			fn();
		}
		else{
			attachEventListener(win, 'load', fn);
		}
	}
	
	
	var blockLists; // tracks external block lists
	
	/**
	* Public interface of adblock detector
	*/
	var impl = {
		/**
		* Version of the adblock detector package
		*/
		version: version,
		
		/**
		* Initialization function. See comments at top for options object
		*/
		init: function(options){
			var k, v, funcs;
			
			if(!options){
				return;
			}
			
			funcs = {
				complete: noop,
				found: noop,
				notfound: noop
			};
			
			for(k in options){
				if(options.hasOwnProperty(k)){
					if(k == 'complete' || k == 'found' || k == 'notFound'){
						funcs[k.toLowerCase()] = options[k];
					}
					else{
						_options[k] = options[k];
					}					
				}
			}
			
			listeners.push(funcs);
			
			blockLists = new BlockListTracker();
			
			attachOrFire();
		}
	}
	
	win['adblockDetector'] = impl;

})(window)	


================================================
FILE: adblockDetectorWithGA.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 blockLists: 	Array
*         Array of string URLs to additional external definition files for bait
*
*	@prop testRemoteList: boolean
*         Flag to indicate a remote list should be used for testing
*         in addition to the simple bait definition in this file.
*         true:  Download external bait definition
*         false: Only test with quick bait
*
*	@prop debug:  boolean
*         Flag to indicate additional debug output should be printed to console
*
*	@prop debug:  useLocalBait
*         Flag to turn on or off usage of the internal quick bait.
*         This is primarily used for testing of remote bait. True by default
*
*	@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(
				{
					blockLists: ['example_remote_adblockdetector_data.js'],
					found: function(){ ...},
 					notFound: function(){...}
				}
			);
*
*
*/

"use strict";
(function(win) {

	var ENABLE_ANALYTICS = true;
	var GA_TRACKING_ID = ''; // sample for http://adblockdetector.emination.com/
	var analytics = {}; // later initialized

	// Credit to Easylist https://easylist.adblockplus.org/en/
	// var blockListUrl = 'https://easylist-downloads.adblockplus.org/easylist_noadult.txt'

	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,
		testRemoteList: true,
		debug: true,
		useLocalBait: true,
		blockLists: [],
		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<opts.blockLists.length;i++){
			url = opts.blockLists[i];
			loadExecuteUrl(url);
		}
	}

	function cancelRemoteDownloads(){
		var i, aj;

		for(i=ajaxDownloads.length-1;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<baitTriggers.nullProps.length;i++){
			t = baitNode[baitTriggers.nullProps[i]];
		}
		for(i=0;i<baitTriggers.zeroProps.length;i++){
			t = baitNode[baitTriggers.zeroProps[i]];
		}
	}

	/**
	* Run tests to see if browser has taken the bait and blocked the bait element
	*/
	function reelIn(bait, attemptNum){
		var i, k, v;
		var body = document.body;
		var found = false;
		var findTrigger = null;

		if(baitNode == null){
			log('recast bait');
			castBait(bait || quickBait);
		}

		if(typeof(bait) == 'string'){
			log('invalid bait used', true);
			if(clearBaitNode()){
				setTimeout(function(){
					testExecuting = false;
				}, 5);
			}

			return;
		}

		if(timerIds.test > 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;i<baitTriggers.nullProps.length;i++){
				if(baitNode[baitTriggers.nullProps[i]] == null){
					if(attemptNum>4)
						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;i<baitTriggers.zeroProps.length;i++){
				if(found == true){
					break;
				}
				if(baitNode[baitTriggers.zeroProps[i]] == 0){
					if(attemptNum>4){
							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;i<listeners.length;i++){
			funcs = listeners[i];
			try{
				if(funcs != null){
					if(isFunc(funcs['complete'])){
						funcs['complete'](findResult);
					}

					if(findResult && isFunc(funcs['found'])){
						funcs['found']();
					}
					else if(findResult === false && isFunc(funcs['notfound'])){
						funcs['notfound']();
					}
				}
			}
			catch(ex){
				log('Failure in notify listeners ' + ex.Message, true);
			}
		}
	}

	/**
	* Attaches event listener or fires if events have already passed.
	*/
	function attachOrFire(){

		var fireNow = false;
		var fn;

		if(document.readyState){
			if(document.readyState == 'complete'){
				fireNow = true;
			}
		}

		fn = function(){
			if(_options.useLocalBait){
				beginTest(quickBait, false);
			}
			else{
				log('ignoreing local bait - download remote');
				fetchRemoteLists();
			}
		}

		if(fireNow){
			fn();
		}
		else{
			attachEventListener(win, 'load', fn);
		}
	}


	var blockLists; // tracks external block lists

	var impl = {

		init: function(options){
			var k, v, funcs;

			if(!options){
				return;
			}

			funcs = {
				complete: noop,
				found: noop,
				notfound: noop
			};

			for(k in options){
				if(options.hasOwnProperty(k)){
					if(k == 'complete' || k == 'found' || k == 'notFound'){
						funcs[k.toLowerCase()] = options[k];
					}
					else{
						_options[k] = options[k];
					}
				}
			}

			listeners.push(funcs);

			blockLists = new BlockListTracker();

			attachOrFire();
		}
	}

	win['adblockDetector'] = impl;


	// ================= ANALYTICS WRAPPER ==================
	/**
	* Object literal wrapper for the various analytics frameworks
	*/
	analytics = {
		key: {
			BLOCKER : 'AdBlocker',
			FOUND : 'Found',
			NOT_FOUND: 'NotFound',
			DETECT: 'Detect'
		},

		recordEvent: function(category, action, label, value){
			if(!ENABLE_ANALYTICS){
				return;
			}

			// send, event, CATEGORY, ACTION, LABEL, VALUE
			ga('send', 'event', category, action, label, value);

		},

		blockerDetected: function(triggerFound, attemptNum){
			var k = analytics.key;
			analytics.recordEvent(k.DETECT, k.FOUND, triggerFound, attemptNum);
		},

		blockerNotDetected: function(triggerFound, attemptNum){
			var k = analytics.key;
			analytics.recordEvent(k.DETECT, k.NOT_FOUND, triggerFound, attemptNum);
		}

	}


	// Addition of Analytics
	function initAnalytics(ga_code){

	  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
	  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
	  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
	  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

	  ga('create', ga_code, 'auto');
	  //ga('send', 'pageview');
	}


	// init analytics inline
	if(ENABLE_ANALYTICS){
		initAnalytics(GA_TRACKING_ID);
	}

})(window)


================================================
FILE: test.html
================================================
<!doctype html>
<html>
<head>
	<title>AdBlock</title>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
	<link href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.7/styles/default.min.css" rel="stylesheet">
<style>
	h5.bg-success,h5.bg-danger {
		padding: 8px;
		border: 1px solid #cccccc;
		border-radius: 4px;
		text-align: center;
	}
	.center {
	   width: 300px;
	   height: 300px;
	   position: absolute;
	   left: 50%;
	   top: 50%; 
	   margin-left: -150px;
	   margin-top: -150px;
	}
	.below {
	   width: 300px;
	   height: 300px;
	   position: absolute;
	   left: 60%;
	   top: 60%; 
	   margin-left: -150px;
	   margin-top: -150px;
	}
</style>
</head>
<body>
		<div class="center">
				<h5 class="bg-success" id="adb-not-enabled" style="display: none;">AdBlock is disabled</h5>
				<h5 class="bg-danger" id="adb-enabled" style="display: none;">AdBlock is enabled</h5>
		</div>
		<div class="below">
			<input TYPE="button" onClick="history.go(0)" VALUE="Reload Page">
		</div>

	<script src="./adblockDetector.js"></script>
	<script>
	// Configure the adblock detector
	(function(){
		var enabledEl = document.getElementById('adb-enabled');
		var disabledEl = document.getElementById('adb-not-enabled');
		function adBlockDetected() {
			enabledEl.style.display = 'block';
			disabledEl.style.display = 'none';
		}
		function adBlockNotDetected() {
			disabledEl.style.display = 'block';
			enabledEl.style.display = 'none';
		}
		
		if(typeof window.adblockDetector === 'undefined') {
			adBlockDetected();
		} else {
			window.adblockDetector.init(
				{
					debug: true,
					found: function(){
						adBlockDetected();
					},
					notFound: function(){
						adBlockNotDetected();
					}
				}
			);
		}
	}());
	</script>
		
</body>
</html>
Download .txt
gitextract_l8e06rrt/

├── License.md
├── README.md
├── adblockDetector.js
├── adblockDetectorWithGA.js
└── test.html
Download .txt
SYMBOL INDEX (35 symbols across 2 files)

FILE: adblockDetector.js
  function parseAsJson (line 74) | function parseAsJson(data){
  function stateChange (line 123) | function stateChange(vals){
  function start (line 137) | function start(){
  function isFunc (line 227) | function isFunc(fn){
  function makeEl (line 234) | function makeEl(tag, attributes){
  function attachEventListener (line 251) | function attachEventListener(dom, eventName, handler){
  function log (line 260) | function log(message, isError){
  function loadExecuteUrl (line 279) | function loadExecuteUrl(url){
  function fetchRemoteLists (line 335) | function fetchRemoteLists(){
  function cancelRemoteDownloads (line 345) | function cancelRemoteDownloads(){
  function beginTest (line 359) | function beginTest(bait){
  function castBait (line 377) | function castBait(bait){
  function reelIn (line 412) | function reelIn(bait, attemptNum){
  function clearBaitNode (line 497) | function clearBaitNode(){
  function stopFishing (line 518) | function stopFishing(){
  function notifyListeners (line 534) | function notifyListeners(){
  function attachOrFire (line 564) | function attachOrFire(){

FILE: adblockDetectorWithGA.js
  function parseAsJson (line 96) | function parseAsJson(data){
  function stateChange (line 145) | function stateChange(vals){
  function start (line 159) | function start(){
  function isFunc (line 249) | function isFunc(fn){
  function makeEl (line 256) | function makeEl(tag, attributes){
  function attachEventListener (line 273) | function attachEventListener(dom, eventName, handler){
  function log (line 282) | function log(message, isError){
  function loadExecuteUrl (line 301) | function loadExecuteUrl(url){
  function fetchRemoteLists (line 356) | function fetchRemoteLists(){
  function cancelRemoteDownloads (line 366) | function cancelRemoteDownloads(){
  function beginTest (line 380) | function beginTest(bait, isRemote){
  function castBait (line 407) | function castBait(bait){
  function reelIn (line 442) | function reelIn(bait, attemptNum){
  function clearBaitNode (line 552) | function clearBaitNode(){
  function stopFishing (line 573) | function stopFishing(){
  function notifyListeners (line 589) | function notifyListeners(){
  function attachOrFire (line 620) | function attachOrFire(){
  function initAnalytics (line 725) | function initAnalytics(ga_code){
Condensed preview — 5 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (46K chars).
[
  {
    "path": "License.md",
    "chars": 1500,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2017, \nAll rights reserved.\n\nRedistribution and use in source and binary forms, with"
  },
  {
    "path": "README.md",
    "chars": 6429,
    "preview": "# Introduction\nJavascript to detect the presence of behavior associated with ad blocking during delivery of a page.\n\n# O"
  },
  {
    "path": "adblockDetector.js",
    "chars": 13754,
    "preview": "// ===============================================\n// AdBlock detector\n//\n// Attempts to detect the presence of Ad Block"
  },
  {
    "path": "adblockDetectorWithGA.js",
    "chars": 16691,
    "preview": "// ===============================================\n// AdBlock detector\n//\n// Attempts to detect the presence of Ad Block"
  },
  {
    "path": "test.html",
    "chars": 1962,
    "preview": "<!doctype html>\n<html>\n<head>\n\t<title>AdBlock</title>\n\t<meta charset=\"utf-8\">\n\t<meta http-equiv=\"X-UA-Compatible\" conten"
  }
]

About this extraction

This page contains the full source code of the InteractiveAdvertisingBureau/AdBlockDetection GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 5 files (39.4 KB), approximately 10.6k tokens, and a symbol index with 35 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!