├── README.md ├── public ├── images │ ├── caddi_132.png │ ├── caddi_200.png │ ├── caddi_screenshot.png │ ├── slider-image-1.png │ ├── slider-image-2.png │ ├── slider-image-3.png │ ├── slider-image-4.png │ └── slider-image-5.png └── javascripts │ ├── iframe_proxy.min.js │ ├── iframe_proxy.js │ └── caddi.js ├── .github └── workflows │ └── semgrep.yml ├── cloudflare.md └── cloudflare.json /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Not ready for primetime -- work in progress. 4 | -------------------------------------------------------------------------------- /public/images/caddi_132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cfapp-caddi/master/public/images/caddi_132.png -------------------------------------------------------------------------------- /public/images/caddi_200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cfapp-caddi/master/public/images/caddi_200.png -------------------------------------------------------------------------------- /public/images/caddi_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cfapp-caddi/master/public/images/caddi_screenshot.png -------------------------------------------------------------------------------- /public/images/slider-image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cfapp-caddi/master/public/images/slider-image-1.png -------------------------------------------------------------------------------- /public/images/slider-image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cfapp-caddi/master/public/images/slider-image-2.png -------------------------------------------------------------------------------- /public/images/slider-image-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cfapp-caddi/master/public/images/slider-image-3.png -------------------------------------------------------------------------------- /public/images/slider-image-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cfapp-caddi/master/public/images/slider-image-4.png -------------------------------------------------------------------------------- /public/images/slider-image-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cfapp-caddi/master/public/images/slider-image-5.png -------------------------------------------------------------------------------- /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: {} 3 | workflow_dispatch: {} 4 | push: 5 | branches: 6 | - main 7 | - master 8 | schedule: 9 | - cron: '0 0 * * *' 10 | name: Semgrep config 11 | jobs: 12 | semgrep: 13 | name: semgrep/ci 14 | runs-on: ubuntu-latest 15 | env: 16 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 17 | SEMGREP_URL: https://cloudflare.semgrep.dev 18 | SEMGREP_APP_URL: https://cloudflare.semgrep.dev 19 | SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version 20 | container: 21 | image: semgrep/semgrep 22 | steps: 23 | - uses: actions/checkout@v4 24 | - run: semgrep ci 25 | -------------------------------------------------------------------------------- /public/javascripts/iframe_proxy.min.js: -------------------------------------------------------------------------------- 1 | var cf={adurl:"http://ib.adnxs.com/ttj"+window.location.search,ts:Date.now(),ttl:1200,debug:0,ack:window.location.search.match("ext_inv_code=")?"wait":"cancel",loc:location.protocol+"//"+window.location.host,evMsg:function(a){D("evMsg from="+a.origin+" data="+a.data+" our ack="+cf.ack);a.origin===cf.loc&&(cf.debug||(cf.debug=a.data.match("debug")),Date.now()-cf.ts>cf.ttl&&("ok"!==cf.ack&&(cf.ack="cancel"),D(" TTL expires; set to cancel; ack="+cf.ack)),a.source.postMessage("slider:"+cf.ack,event.origin))}, 2 | loader:function(){D("read_ad_content",cf);var a=0,b=document.getElementsByTagName("body")[0];b&&(b.removeChild(document.getElementById("ads")),a=b.children.length);cf.ack=0');"onload"in a?a.onload=cf.loader:a.onreadystatechange=function(){"complete"===this.readyState&&cf.loader()}}}; 4 | -------------------------------------------------------------------------------- /public/javascripts/iframe_proxy.js: -------------------------------------------------------------------------------- 1 | var cf = { 2 | adurl: 'http://ib.adnxs.com/ttj' + window.location.search, 3 | ts: Date.now(), 4 | ttl: 1200, 5 | debug: 0, 6 | ack: window.location.search.match('ext_inv_code=') ? 'wait' : 'cancel', 7 | loc: location.protocol + '//' + window.location.host, 8 | evMsg: function(e) { 9 | D( 'evMsg from=' + e.origin + ' data='+e.data + ' our ack='+cf.ack ); 10 | if ( e.origin !== cf.loc ) return; 11 | if ( ! cf.debug) cf.debug = e.data.match('debug'); 12 | 13 | if ( (Date.now() - cf.ts) > cf.ttl ) { 14 | if ( cf.ack !== 'ok' ) cf.ack = 'cancel'; // timeout condition (eg. 404) 15 | D( ' TTL expires; set to cancel; ack='+cf.ack ); 16 | } 17 | e.source.postMessage('slider:'+cf.ack, event.origin); 18 | }, 19 | loader: function(){ 20 | D('read_ad_content', cf ); 21 | 22 | var ct = 0; 23 | var body = document.getElementsByTagName('body')[0]; 24 | if (body) { 25 | body.removeChild( document.getElementById("ads")); 26 | ct = body.children.length; 27 | } 28 | if ( ct > 0) { 29 | cf.ack = 'ok'; 30 | }else { 31 | cf.ack = 'cancel'; 32 | } 33 | D(' loader -- setting ack=' + cf.ack ); 34 | if (cf.evMsg) window.addEventListener("message", cf.evMsg, false); // re-add if nuked? TODO: detect nukeage 35 | } 36 | }, 37 | D = function(m,o) { if (cf.debug) console.log( (Date.now() - cf.ts) +"ms. slider IFRAME: "+m, o); }; 38 | 39 | window.onload = function (){ 40 | D( 'onLoad is begins: '+window.location.search,cf); 41 | 42 | window.addEventListener("message", cf.evMsg, false); 43 | 44 | if (cf.ack == 'cancel') return; 45 | 46 | var ad = document.createElement('script'); ad.type = 'text/javascript'; ad.async = true; 47 | ad.src = cf.adurl; 48 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ad, s); 49 | document.write('
'); 50 | 51 | if ('onload' in ad){ 52 | ad.onload = cf.loader; 53 | }else{ 54 | ad.onreadystatechange = function(){ 55 | if( this.readyState === 'complete') { 56 | cf.loader(); 57 | } 58 | }; 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /cloudflare.md: -------------------------------------------------------------------------------- 1 | # Slider (Beta) 2 | 3 | The Slider is an innovative ad unit that allows any publisher to generate incremental revenue with little effort. It is complementary to existing ad units, can be enabled with a single click and is available exclusively to CloudFlare publishers (subject to approval). The Slider also includes numerous configuration options that allow you to control the level of interaction with your site’s visitors. 4 | 5 | ![](/images/apps/caddi/slider-image-2.png "Page Preview") 6 | 7 | ## Features 8 | 9 | 10 | **Generate Incremental revenue with minimal effort.** The Slider—an IAB standard 300x250 medium rectangle—elegantly slides onto the page after one second of visitor engagement drawing visitor attention and generating engagement rates several times that of similar native ad units. The end result is higher yield and more revenue for publishers. 11 | 12 | 13 | **Access to Top Advertisers.** Get instantaneous access to many of the world’s largest marketplaces for advertisers. No matter how small or large, or broad or niche your website is—we have advertisers appropriate for your content. 14 | 15 | 16 | **Yield Optimization.** Our advertiser partners compete in real-time within our ad decisioning engine to ensure that your inventory captures substantial demand at the highest possible yield. Multiple relevant advertisers participate in real-time auctions to deliver the highest paying ads for every single impression. 17 | 18 | 19 | Note: the Slider will not appear on pages viewed on mobile browsers 20 | 21 | 22 | ## How It Works 23 | 24 | 1. Enable the Slider within the CloudFlare Apps Manager 25 | 26 | 2. Configure when and where the Slider appears. You have complete control over: 27 | 28 | a. **Orientation:** position the Slider on the top left or right, bottom left or right, or you may also choose to display the Slider only when a visitor reaches the end of your content. 29 | 30 | b. **Format:** choose between static ads (images and flash), and static and video ads. Video ads tend to pay more, but don’t suit every site. 31 | 32 | c. **Duration of Appearance:** you can choose to minimize the Slider after a predetermined period of time after it appears. Choose between 20, 30 or 45 seconds, or select “Do not minimize” if you would like the Slider to remain fully visible on the page. 33 | 34 | d. **Minimum Window Size:** Limit display of the Slider to certain window sizes. This option can be used to prevent the slider from covering your site’s content when viewed at low window resolutions. Do note that the Slider will not appear on pages viewed by a mobile browser 35 | 36 | e. **Scrollability:** Turn Sticky Scroll “on” to allow the Slider to scroll with your content 37 | 38 | f. **Impressions per Session:** Limit the number of ads shown to your visitors during a single session / site visit (20 minutes). 39 | 40 | g. **Visitor Opt-out Periods:** After a visitor closes the Slider (by clicking the X close icon), you can toggle the period of time to wait before showing another ad. 41 | 42 | 3. The Slider works instantly! You will start to earn incremental revenue immediately. 43 | 44 | 45 | ## Restrictions 46 | 47 | The Slider is open to all CloudFlare publishers subject to approval. We reserve the right to deny approval or remove the Slider from any site with content that, in our belief, may be deemed negative in nature, illegal or offensive in any way. 48 | 49 | 50 | ## Payments 51 | 52 | Earnings are available to publishers on a net-60 day basis. For example, earnings accrued in March are available for payout at the end of May, subject to minimum payout thresholds (varies by payment method). 53 | 54 | 55 | ## Other Things You Should Know 56 | 57 | * The Slider will not appear on pages viewed in a mobile browser 58 | * Once active, the Slider will appear on every page of any site in which the Slider is enabled; a tool to control where the Slider appears on your site will be available shortly 59 | * Reporting of ad performance and earnings will be available by periodic e-mail 60 | * To report a bug or make a suggestion during the beta period, please send correspondence to 61 | -------------------------------------------------------------------------------- /cloudflare.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Slider", 3 | "description": "The Slider is an innovative ad unit that earns you revenue with every impression. It is complementary to your existing ads, can be enabled with one click and is exclusively for CloudFlare users.", 4 | "keywords" : [ 5 | "advertising", 6 | "monetization", 7 | "ads" 8 | ], 9 | "version": "0.7.0", 10 | "category":"monetization", 11 | "contributors": [ 12 | { 13 | "name" : "greg lee coleman", 14 | "email" : "apps+slider@cloudflare.com" 15 | } 16 | ], 17 | "repository" : { 18 | "type" : "git", 19 | "url" : "https://github.com/gleecology/cfapp-caddi.git" 20 | }, 21 | "main" : "./public/javascripts/caddi.js", 22 | 23 | "account": { 24 | "callback_url": "http://127.0.0.1:40420/api/partners/cloudflare/apps/slider", 25 | "user_fields": ["email"] 26 | }, 27 | 28 | "dependencies" : { 29 | }, 30 | "licenses" : [ 31 | { 32 | "type": "MIT", 33 | "url": "http://www.opensource.org/licenses/MIT" 34 | } 35 | ], 36 | "implements" : "AMD", 37 | "scripts" : { 38 | }, 39 | "config" : { 40 | "assets" : { 41 | "logos" : { 42 | "200px": "./public/images/caddi_200.png", 43 | "132px": "./public/images/caddi_132.png" 44 | }, 45 | "detail_page" : [ 46 | "./public/images/caddi_screenshot.png" 47 | ] 48 | }, 49 | "payment": {}, 50 | 51 | "details": { 52 | "Category": "Content Monetization", 53 | "Language": "n/a", 54 | "Payout": "Impressions served", 55 | "Restrictions": "We reserve the right to deny approval or remove access to the Slider from any site with content that in our belief may be deemed negative in nature, illegal or offensive in any way, or as a result of misuse or abuse." 56 | 57 | }, 58 | 59 | "interface": [ 60 | { 61 | "type" : "hidden", 62 | "id" : "LYRM_id", 63 | "name" : "[a publisher id will be issued by CloudFlare]", 64 | "value": "-1", 65 | "domain_request" : true 66 | }, 67 | { 68 | "type" : "hidden", 69 | "id" : "publisher_id", 70 | "name" : "[a publisher id will be issued by CloudFlare]", 71 | "value": "-1", 72 | "domain_request" : true 73 | }, 74 | { 75 | "type" : "hidden", 76 | "id" : "appnexus_placement_id", 77 | "name" : "[issued by CloudFlare]", 78 | "value": "-1", 79 | "domain_request" : true 80 | }, 81 | { 82 | "type" : "hidden", 83 | "id" : "ext_inv_code", 84 | "name" : "[issued by CloudFlare]", 85 | "value": "-1", 86 | "domain_request" : true 87 | }, 88 | { 89 | "id": "orient", 90 | "name": "Ad Orientation", 91 | "description" : "Select where to place the Slider on your site", 92 | "type": "select", 93 | "options": [ 94 | { "value": "left", "label": "Left Top" }, 95 | { "value": "right", "label": "Right Top", "selected": "true" }, 96 | { "value": "left_bottom", "label": "Left Bottom" }, 97 | { "value": "right_bottom", "label": "Right Bottom" } 98 | ] 99 | }, 100 | 101 | { 102 | "domain_request" : true, 103 | "id": "text_only", 104 | "name": "Ad Format", 105 | "description" : "Choose to display only static ads, or static and video ads", 106 | "type": "select", 107 | "options": [ 108 | { 109 | "label": "Static and Video", 110 | "value": 0 111 | }, 112 | { 113 | "label": "Static only", 114 | "value": 1, 115 | "selected": true 116 | } 117 | ] 118 | }, 119 | 120 | { 121 | "id": "view_ttl", 122 | "name": "Slide Out", 123 | "description" : "Minimize Slider after a the selected period of time", 124 | "type": "select", 125 | "options": [ 126 | { "value": 0, "label": "Do not minimize", "selected": "true" }, 127 | { "value": "45", "label": "45 seconds" }, 128 | { "value": "30", "label": "30 seconds" }, 129 | { "value": "20", "label": "20 seconds" } 130 | ] 131 | }, 132 | 133 | { 134 | "id": "min_resolution", 135 | "name": "Minimum Resolution", 136 | "description" : "Limit display of the Slider to the following window sizes", 137 | 138 | "type": "select", 139 | "options": [ 140 | { "value": 0, "label": "All window sizes", "selected": "true" }, 141 | { "value": "1024x0", "label": "wider than 1024" }, 142 | { "value": "1600x0", "label": "wider than 1600" } 143 | ] 144 | }, 145 | 146 | { 147 | "id": "scroll", 148 | "name": "Sticky Scroll", 149 | "description" : "Allow the Slider to scroll within the browser window with Sticky Scroll", 150 | "type": "select", 151 | "options": [ 152 | { "value": 0, "label": "Off", "selected": "true" }, 153 | { "value": 1, "label": "On" } 154 | ] 155 | }, 156 | 157 | 158 | { 159 | "id": "ss_view_max_ct", 160 | "name": "Impressions per Session", 161 | "description" : "Limit the number of impressions shown per session (20 min)", 162 | "type": "select", 163 | "options": [ 164 | { 165 | "label": "No limit", 166 | "value": 0 167 | }, 168 | { "value": 50, "label": "50 impressions" }, 169 | { "value": 25, "label": "25 impressions", "selected": true }, 170 | { "value": 10, "label": "10 impressions" }, 171 | { "value": 5, "label": "5 impressions" }, 172 | { "value": 1, "label": "1 impressions" } 173 | ] 174 | }, 175 | 176 | { 177 | "id": "user_pause_ttl", 178 | "name": "Visitor Opt-out Period", 179 | "description" : "Select the period of time to pause the Slider after it is closed by a visitor", 180 | "type": "select", 181 | "options": [ 182 | { 183 | "label": "Do not pause", 184 | "selected": true, 185 | "value": 0 186 | }, 187 | { "value": 60, "label": "1 minute" }, 188 | { "value": 360, "label": "10 minutes" }, 189 | { "value": 3600, "label": "1 hour" }, 190 | { "value": 14400, "label": "4 hours" }, 191 | { "value": 43200, "label": "12 hours" }, 192 | { "value": 86400, "label": "24 hours" }, 193 | { "value": 259200, "label": "72 hours" }, 194 | { "value": 315360000, "label": "Never show again" } 195 | ] 196 | } 197 | ] 198 | } 199 | } 200 | 201 | -------------------------------------------------------------------------------- /public/javascripts/caddi.js: -------------------------------------------------------------------------------- 1 | CloudFlare.define( 'caddi', [ 'caddi/config', 'cloudflare/dom', 'cloudflare/user', 'cloudflare/owl', 'cloudflare/jquery1.7', 'cloudflare/console' ], 2 | function(cfg, dom, user, owl, $, console ) { 3 | 4 | /* config vars: 5 | * text_only [ 0 | 1 ] 6 | * scroll [ 0 | 1 ] 7 | * debug [ 1 | 0 ] 8 | * user_pause_ttl [ -1 | 0 | INT ] seconds 9 | * orient [ left | right | left_bottom | right_bottom ] 10 | * ss_view_max_ct [ 0 | INT ] 11 | * view_ttl [ 0 | INT ] seconds; but used in MS timer 12 | * min_resolution [ 0 | 1024x0 | 1600x0 ] 13 | * http_only [ 0 | 1 ] <-- 14 | */ 15 | 16 | // integer-gize! 17 | [ 'text_only', 'scroll', 'debug', 'user_pause_ttl', 'ss_view_max_ct', 'http_only', 'view_ttl' ].map(function(k){ 18 | cfg[k] = parseInt(cfg[k], 10) || 0; 19 | }); 20 | 21 | /* 22 | * setup vars 23 | */ 24 | 25 | var delim = '|', 26 | sessionTTL = 1200, 27 | cookieCol = ['timeFirst','sessionStart','N','sessionCt','sessionViewCt','pauseUntil','pauseSkipCt','impCt'], 28 | currTs = function(){ return parseInt( (+(new Date()) / 1000 ), 10 ); }, 29 | currTime = currTs(), 30 | httpOnly = 1, 31 | ext_inv_code= ( cfg.ext_inv_code && cfg.ext_inv_code != '_disabled_' ) ? cfg.ext_inv_code : null, 32 | placement_id= cfg.appnexus_placement_id, 33 | V = cfg.version || '0.6.9', 34 | D = cfg.debug || (window.location.hash.match('debug_view') ? 1 : 0), 35 | psa_disable = 1, 36 | cVal = '', 37 | 38 | Dbg = function(m,o){ if (! D) return; if(o!==null){ console.log(m,o); }else{ console.log(m);} }, 39 | 40 | installCookie = function(name,val,ttl) { 41 | var exp = new Date(); 42 | if ( ttl ) { 43 | exp.setTime( exp.getTime() + (ttl * 1000) ); 44 | } 45 | Dbg( 'installCookie name=' + name + ' val=' + val ); 46 | document.cookie = name + "=" + val + (ttl ? ";expires=" + exp.toUTCString() : '' ); 47 | }, 48 | 49 | readCookieAttrs = function(str) { 50 | var C = {}, 51 | arr = str ? str.split(delim) : []; 52 | Dbg( "readCookieAttrs starts on str", str, arr ); 53 | 54 | for ( var i = 0; i < cookieCol.length; i++ ){ 55 | C[ cookieCol[i] ] = arr[i] ? parseInt(arr[i], 10) : 0; 56 | } 57 | ( C.timeFirst && parseInt(C.timeFirst, 10) && C.timeFirst > 1354151978 ) || ( C.timeFirst = currTime ); 58 | if ( ! C.sessionStart ) C.sessionStart = currTime; 59 | 60 | Dbg( "readCookieAttrs returns", C ); 61 | return C; 62 | }, 63 | 64 | writeCookie = function(cName, C, ttl){ 65 | var vals = []; 66 | for ( var i = 0; i < cookieCol.length; i++){ 67 | vals.push( C[cookieCol[i]] || 0); 68 | } 69 | cVal = vals.join(delim); 70 | installCookie( cName, cVal, ttl ); 71 | }, 72 | 73 | orient = cfg.orient || 'left', 74 | isLeft = orient.indexOf('left') >= 0 ? true : false, 75 | isBottom = orient.indexOf('bottom') >= 0 ? true : false, 76 | useScroll = cfg.scroll ? true : false, 77 | minRes = ( cfg.min_resolution && cfg.min_resolution.indexOf('x') > 0 ) ? cfg.min_resolution.split('x') : null, 78 | 79 | cookieName = 'cfapp_caddi', 80 | cookie = readCookieAttrs( user.getCookie(cookieName) ), 81 | inSession = (( currTime - cookie.sessionStart ) < sessionTTL ) ? 1 : 0, 82 | viewport = dom.getViewport(), 83 | terminate = false; 84 | 85 | /* 86 | * logic: eligibility, cookie, etc. 87 | */ 88 | Dbg( "caddi starts; version="+V+"config:", cfg ); 89 | 90 | cookie.N++; 91 | 92 | if (dom.ios || dom.android ){ 93 | terminate++; 94 | } 95 | if ( window.cf_slider_disable ) { 96 | terminate++; 97 | Dbg( "cf_slider_disable by publisher; terminate="+terminate); 98 | } 99 | if ( httpOnly && window.location.protocol === 'https:' ){ 100 | terminate++; 101 | Dbg( "httpOnly; terminate="+terminate); 102 | } 103 | 104 | if( minRes && viewport ) { 105 | ( minRes[0] && viewport.width ) && ( minRes[0] <= viewport.width || terminate++ ); 106 | ( minRes[1] && viewport.height ) && ( minRes[1] <= viewport.height || terminate++ ); 107 | Dbg( "minRes check; terminate=" + terminate, minRes, viewport ); 108 | } 109 | if( cookie.pauseUntil && cookie.pauseUntil >= currTime ){ 110 | cookie.pauseSkipCt++; 111 | terminate++; 112 | Dbg( 'Ad serving is paused; seconds left=' + ( cookie.pauseUntil - currTime ) ); 113 | } 114 | else if ( cookie.pauseUntil !== 0 ) { 115 | Dbg( 'Ad serving was paused; but active again. Removing cookie setting? ' + cookie.pauseUntil ); 116 | cookie.pauseUntil = 0; 117 | } 118 | 119 | if (! inSession ){ 120 | cookie.sessionCt++; 121 | cookie.sessionStart = currTime; 122 | cookie.sessionViewCt = 0; 123 | } 124 | 125 | 126 | if ( cfg.ss_view_max_ct && cookie.sessionViewCt >= cfg.ss_view_max_ct ) { 127 | terminate++; 128 | }else{ 129 | cookie.sessionViewCt++; 130 | cookie.impCt++; 131 | } 132 | 133 | writeCookie(cookieName,cookie); 134 | 135 | if ( terminate ) { 136 | Dbg( 'TERMINATE; val='+ terminate ); 137 | return; 138 | } 139 | 140 | if (! placement_id || ! ext_inv_code ){ 141 | Dbg( 'no placement or ext_inv_code' ); 142 | return; 143 | } 144 | 145 | var cfOwl = owl.createDispatcher('caddi'); 146 | 147 | Dbg( 'owl created cfOwl' , cfOwl ); 148 | 149 | /* 150 | * create HTML 151 | */ 152 | 153 | 154 | var a = 'cfad', // id="cfad" 155 | ar = '#'+a, // reference of id; #cfad 156 | b = a + 'b', 157 | br = '#'+b, 158 | x = a + 'x', // x=close 159 | xr = '#'+x, 160 | f = a + 'f', // f=frame 161 | fr = '#'+f, 162 | tx = 1000, // slider slide time 163 | fullWidth = '310px', 164 | css = 165 | ' #cfad { height: 280px; width:0px; padding: 2px 0; position: absolute; z-index: 99999; line-height: 1px; overflow: hidden; } ' + 166 | ' #cfadb { position:relative }' + 167 | ' #cfadf { height: 250px; width: 300px; margin: 0px; padding: 3px; background-color: #ffffff; border: 1px solid #404040; } ' + 168 | ' #cfadx { background-color: #ffffff; margin-top: -1px; color: #404040; font-weight: bold; font: 16px Helvetica,Arial,Sans-serif; padding: 0px 5px 0.6px 4px; text-decoration: none; border: 0; border-bottom: 1px solid #404040; position: absolute; display: block; } ' + 169 | ' .cfad-l { left: 0px; } .cfad-r { right: 0px; text-align:right} ' + 170 | ' .cfadf-l { border-left: 0px ! important; } .cfadf-r { border-right:0px ! important; } ' + 171 | ' .cfadx-l { border-right: 1px solid #404040 ! important; left : 0 ! important; } .cfadx-r { border-left: 1px solid #404040 ! important; right: 0 ! important; } ' + 172 | ar + '.cfad-y-bot { bottom: 15px; } ' + 173 | ar + '.cfad-y-top { top: 15px; } ', 174 | 175 | timeoutId = null, 176 | currHost = window.location.host.toLowerCase(), 177 | adParam = '/cdn-cgi/nexp/acv=' + currTime + '/apps/caddi/slider_iframe.html?' + 178 | 'size=300x250' + '&id=' + placement_id + 179 | '&ext_inv_code=' + ext_inv_code + 180 | ( ( isBottom && ! useScroll ) ? '' : '&position=above' ) + 181 | ( psa_disable ? '&psa=0' : '' ) + 182 | '&referrer=' + currHost + '&_t=' + currTime, 183 | 184 | adUrl = window.location.protocol + '//' + currHost + adParam, 185 | 186 | iframe = '', 187 | 188 | viewTTL = cfg.view_ttl ? ( cfg.view_ttl * 1000 ) : 0, 189 | isOpen = false, 190 | onIf = false, // cursor on iframe 191 | isAttached = false, 192 | showCycles = 0, 193 | delay = 0, // for lazyload bottom time delta 194 | bottomBuffer= 2000, 195 | 196 | removeOp = function(err){ 197 | if ( cfg.user_pause_ttl ){ 198 | Dbg( 'adding user_pause_ttl = ' + cfg.user_pause_ttl ); 199 | cookie.pauseUntil = currTime + cfg.user_pause_ttl; 200 | writeCookie(cookieName,cookie); 201 | } 202 | Dbg( 'removeOp fires'); 203 | window.clearTimeout(timeoutId); 204 | $(ar).remove(); 205 | onIf = false; 206 | cfOwl.dispatch( {action: err? err : 'close', orient: orient, c: cVal, ext_inv_code: ext_inv_code, placement_id: placement_id }); 207 | }, 208 | 209 | maximizeOp = function(){ 210 | $(fr).css( { width: '300px' }); 211 | $(ar).animate( { width: fullWidth } , 'slow', function() { 212 | Dbg( 'maximizeOp '); 213 | $(xr).html('x'); 214 | $(xr).unbind('click').click( removeOp ); 215 | showCycles++; 216 | // do we allow it to minimize again? do we go longer later? 217 | Dbg( showCycles + ' showCycles; installing setTimeout for minimizeOp; viewTTL='+viewTTL ); 218 | $(ar).unbind('hover').hover( function(){ onIf = true; }, function(){ onIf = false; } ); 219 | isOpen = true; 220 | timeoutId = window.setTimeout( minimizeOp, viewTTL ); 221 | }); 222 | }, 223 | 224 | minimizeOp = function(){ 225 | Dbg( 'starting minimizeOp (rollback)' ); 226 | if (! $(ar).length ) { 227 | Dbg( '--bailing out of minimizeOp -- element was removed' ); 228 | return; // element has been removed via close click 229 | } 230 | if ( onIf ){ 231 | Dbg('-- bailing out of minimizeOp; hover cancels and reschedules' ); 232 | timeoutId = window.setTimeout( minimizeOp, viewTTL ); 233 | return; 234 | } 235 | 236 | $(fr).animate( { width: '22px' } , 'slow', function(){ 237 | Dbg( 'installing hover handler....' ); 238 | $(ar).css('width','32px'); 239 | $(xr).html( isLeft ? '>' : '<' ); 240 | $(xr).unbind('click').click( maximizeOp ); 241 | $(ar).unbind('hover').hover( function(){ onIf = true; maximizeOp(); }, function(){ onIf = false; } ); 242 | }); 243 | }, 244 | 245 | frLoad = function(){ 246 | var H = $(fr).contents().find("html").height(); 247 | 248 | Dbg("frame content is ready(?); dispatching owl viewTTL=" + viewTTL +" height=" + H + " src=" + $(fr).prop('src') ); 249 | 250 | if (H === null || H < 1 ){ 251 | return removeOp('cancel_empty'); 252 | } 253 | 254 | if (viewTTL) { 255 | window.setTimeout( minimizeOp, viewTTL ); 256 | } 257 | 258 | $(ar).delay(1600).animate( { width: fullWidth }, tx ); 259 | $(xr).click( removeOp ); 260 | cfOwl.dispatch( { action: 'load', orient: orient, c: cVal, delay: delay, ext_inv_code: ext_inv_code, placement_id: placement_id }); 261 | 262 | $(ar).hover( function(){ onIf = true; }, function(){ onIf = false; } ); 263 | 264 | $(window).blur( function() { 265 | Dbg( " BLUR EVENT click=" + onIf ); 266 | if( onIf ) { 267 | cfOwl.dispatch( {action: 'click', orient: orient, c: cVal, ext_inv_code: ext_inv_code, placement_id: placement_id }); 268 | } 269 | }); 270 | 271 | }, 272 | attach = function(){ 273 | Dbg( "attach() is running after t delta=" + ( currTs() - currTime ) ); 274 | 275 | $('head').append( '' ); 276 | $('
').attr('id', a).appendTo('body'); 277 | $('
').attr('id', b).html(iframe).appendTo(ar); 278 | $('x').attr('id',x).appendTo(br); 279 | 280 | $(ar).addClass( isLeft ? 'cfad-l' : 'cfad-r' ); 281 | $(fr).addClass( isLeft ? 'cfadf-l' : 'cfadf-r' ); 282 | $(xr).addClass( isLeft ? 'cfadx-l' : 'cfadx-r' ); 283 | 284 | if ( useScroll ) $(ar).css('position', 'fixed'); 285 | 286 | if ( isBottom ){ 287 | if ( useScroll ){ 288 | $(ar).addClass('cfad-y-bot'); 289 | }else{ 290 | $(ar).css('top', $(document).height() - 300 ); 291 | } 292 | }else{ 293 | $(ar).addClass('cfad-y-top'); 294 | } 295 | isAttached = true; 296 | $(fr).on("load", frLoad); 297 | }, 298 | 299 | sinceLast = currTime, // debug only 300 | timex = null, 301 | scrollWatch = function(){ 302 | if (timex) window.clearTimeout(timex); 303 | timex = window.setTimeout( function(){ 304 | timex = null; 305 | 306 | if (isAttached) { 307 | Dbg( "scrollWatch about to be destroyed .... " ); 308 | $(window).off('scroll', scrollWatch); 309 | return; 310 | } 311 | var end = $(document).height(), 312 | at = $(window).height() + $(window).scrollTop(), 313 | toEnd = end - at, 314 | since = +(new Date()) / 1000; 315 | 316 | Dbg( 'scrollWatch end=' + end + ' at=' + at + ' distance toEnd=' + toEnd + ' since=' + (since - sinceLast) ); 317 | sinceLast = since; 318 | if ( toEnd <= bottomBuffer ){ 319 | delay = currTs() - currTime; 320 | $(ar).show(); 321 | attach(); 322 | } 323 | }, 70); 324 | }; 325 | 326 | Dbg( "vars were set: isLeft=" + isLeft + ' isBottom=' + isBottom + ' useScroll='+useScroll); 327 | 328 | $(document).ready(function(){ 329 | if ( isBottom ){ 330 | var currPos = $(window).height() + $(window).scrollTop(); 331 | Dbg( "checking curr_pos=" + currPos + " and doc.height=" + $(document).height() ); 332 | 333 | if ( currPos > ( $(document).height() - bottomBuffer) ) { 334 | Dbg( "skipping scrollWatch handler; already within range at curr_pos=" + currPos ); 335 | attach(); 336 | } 337 | else{ 338 | Dbg( "installing scrollWatch handler; doc_size=" + $(document).height() + " curr_pos=" + currPos ); 339 | $(window).on('scroll', scrollWatch ); 340 | } 341 | }else{ 342 | attach(); 343 | } 344 | }); 345 | 346 | Dbg('caddi code complete' ); 347 | 348 | } ); 349 | --------------------------------------------------------------------------------