1/*global window:false, setTimeout:true, console:true */ 2/* 3Copyright (C) 2013-2015 Good Code and Cookie Banner contributors 4 5Permission is hereby granted, free of charge, to any person obtaining a copy 6of this software and associated documentation files (the "Software"), to deal 7in the Software without restriction, including without limitation the rights 8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9copies of the Software, and to permit persons to whom the Software is 10furnished to do so, subject to the following conditions: 11 12The above copyright notice and this permission notice shall be included in 13all copies or substantial portions of the Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21THE SOFTWARE. 22*/ 23 24(function(context) { 25 'use strict'; 26 27 var win = context, doc = win.document; 28 29 var global_instance_name = 'cbinstance'; 30 31 /*! 32 * contentloaded.js 33 * 34 * Author: Diego Perini (diego.perini at gmail.com) 35 * Summary: cross-browser wrapper for DOMContentLoaded 36 * Updated: 20101020 37 * License: MIT 38 * Version: 1.2 39 * 40 * URL: 41 * http://javascript.nwbox.com/ContentLoaded/ 42 * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE 43 * 44 */ 45 // @win window reference 46 // @fn function reference 47 function contentLoaded(win, fn) { 48 var done = false, top = true, 49 doc = win.document, root = doc.documentElement, 50 51 add = doc.addEventListener ? 'addEventListener' : 'attachEvent', 52 rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent', 53 pre = doc.addEventListener ? '' : 'on', 54 55 init = function(e) { 56 if (e.type == 'readystatechange' && doc.readyState != 'complete') return; 57 (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false); 58 if (!done && (done = true)) fn.call(win, e.type || e); 59 }, 60 61 poll = function() { 62 try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; } 63 init('poll'); 64 }; 65 66 if (doc.readyState == 'complete') fn.call(win, 'lazy'); 67 else { 68 if (doc.createEventObject && root.doScroll) { 69 try { top = !win.frameElement; } catch(e) { } 70 if (top) poll(); 71 } 72 doc[add](pre + 'DOMContentLoaded', init, false); 73 doc[add](pre + 'readystatechange', init, false); 74 win[add](pre + 'load', init, false); 75 } 76 } 77 78 var Cookies = { 79 get: function (key) { 80 return decodeURIComponent(doc.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || null; 81 }, 82 set: function (key, val, end, path, domain, secure) { 83 if (!key || /^(?:expires|max\-age|path|domain|secure)$/i.test(key)) { 84 return false; 85 } 86 var expires = ''; 87 if (end) { 88 switch (end.constructor) { 89 case Number: 90 expires = end === Infinity ? '; expires=Fri, 31 Dec 9999 23:59:59 GMT' : '; max-age=' + end; 91 break; 92 case String: 93 expires = '; expires=' + end; 94 break; 95 case Date: 96 expires = '; expires=' + end.toUTCString(); 97 break; 98 } 99 } 100 doc.cookie = encodeURIComponent(key) + '=' + encodeURIComponent(val) + expires + (domain ? '; domain=' + domain : '') + (path ? '; path=' + path : '') + (secure ? '; secure' : ''); 101 return true; 102 }, 103 has: function (key) { 104 return (new RegExp('(?:^|;\\s*)' + encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=')).test(doc.cookie); 105 }, 106 remove: function (key, path, domain) { 107 if (!key || !this.has(key)) { return false; } 108 doc.cookie = encodeURIComponent(key) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT' + ( domain ? '; domain=' + domain : '') + ( path ? '; path=' + path : ''); 109 return true; 110 } 111 }; 112 113 var Utils = { 114 115 // merge objects and whatnot 116 merge: function(){ 117 var obj = {}, 118 i = 0, 119 al = arguments.length, 120 key; 121 if (0 === al) { 122 return obj; 123 } 124 for (; i < al; i++) { 125 for (key in arguments[i]) { 126 if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { 127 obj[key] = arguments[i][key]; 128 } 129 } 130 } 131 return obj; 132 }, 133 134 str2bool: function(str) { 135 str = '' + str; 136 switch (str.toLowerCase()) { 137 case 'false': 138 case 'no': 139 case '0': 140 case '': 141 return false;
142 default: 143 return true; 144 } 145 }, 146 147 fade_in: function(el) { 148 if (el.style.opacity < 1) { 149 el.style.opacity = (parseFloat(el.style.opacity) + 0.05).toFixed(2); 150 win.setTimeout(function(){ 151 Utils.fade_in(el); 152 }, 50); 153 } 154 }, 155 156 get_data_attribs: function(script) { 157 var data = {}; 158 if (Object.prototype.hasOwnProperty.call(script, 'dataset')) { 159 data = script.dataset; 160 } else { 161 var attribs = script.attributes; 162 var key; 163 for (key in attribs) { 164 if (Object.prototype.hasOwnProperty.call(attribs, key)) { 165 var attr = attribs[key]; 166 if (/^data-/.test(attr.name)) { 167 var camelized = Utils.camelize(attr.name.substr(5)); 168 data[camelized] = attr.value; 169 } 170 } 171 } 172 } 173 return data; 174 }, 175 176 /** 177 * "Standardizes" the options keys by converting and removing 178 * any potential "dashed-property-name" into "dashedPropertyName". 179 * In case both are present, the dashedPropertyName wins. 180 */ 181 normalize_keys: function(options_object) { 182 var camelized = {}; 183 for (var key in options_object) { 184 if (Object.prototype.hasOwnProperty.call(options_object, key)) { 185 var camelized_key = Utils.camelize(key); 186 // TODO: could this break for "falsy" values within options_object? 187 // avoiding "dashed-property-name" overriding a potentially existing "dashedPropertyName" 188 camelized[camelized_key] = options_object[camelized_key] ? options_object[camelized_key] : options_object[key]; 189 } 190 } 191 return camelized; 192 }, 193 194 camelize: function(str) { 195 var separator = '-', 196 match = str.indexOf(separator); 197 while (match != -1) { 198 var last = (match === (str.length - 1)), 199 next = last ? '' : str[match + 1], 200 upnext = next.toUpperCase(), 201 sep_substr = last ? separator : separator + next; 202 str = str.replace(sep_substr, upnext); 203 match = str.indexOf(separator); 204 } 205 return str; 206 }, 207 208 find_script_by_id: function(id) { 209 var scripts = doc.getElementsByTagName('script'); 210 for (var i = 0, l = scripts.length; i < l; i++) { 211 if (id === scripts[i].id) { 212 return scripts[i]; 213 } 214 } 215 return null; 216 } 217 218 }; 219 220 var script_el_invoker = Utils.find_script_by_id('cookiebanner'); 221 222 var Cookiebanner = context.Cookiebanner = function(opts) { 223 this.init(opts); 224 }; 225 226 Cookiebanner.prototype = { 227 228 // for testing stuff from the outside mostly 229 cookiejar: Cookies, 230 231 init: function(opts) { 232 233 this.inserted = false; 234 this.closed = false; 235 this.test_mode = false; // TODO: implement 236 237 var default_text = 'We use cookies to enhance your experience. ' + 238 'By continuing to visit this site you agree to our use of cookies.'; 239 var default_link = 'Learn more'; 240 241 this.default_options = { 242 // autorun: true, 243 cookie: 'cookiebanner-accepted', 244 closeText: '✖', 245 cookiePath: '/', 246 debug: false, 247 expires: Infinity, 248 zindex: 255, 249 mask: false, 250 maskOpacity: 0.5, 251 maskBackground: '#000', 252 height: 'auto', 253 minHeight: '21px', 254 bg: '#000', 255 fg: '#ddd', 256 link: '#aaa', 257 position: 'bottom', 258 message: default_text, 259 linkmsg: default_link,
260 moreinfo: 'http://aboutcookies.org', 261 effect: null, 262 fontSize: '14px', 263 fontFamily: 'arial, sans-serif', 264 instance: global_instance_name, 265 textAlign: 'center', 266 acceptOnScroll: false 267 }; 268 269 this.options = this.default_options; 270 this.script_el = script_el_invoker; 271 272 if (this.script_el) { 273 var data_options = Utils.get_data_attribs(this.script_el); 274 this.options = Utils.merge(this.options, data_options); 275 } 276 277 // allowing opts passed to the ctor to override everything 278 if (opts) { 279 // mimics the "data-option-name" HTML attribute becoming 280 // this.options.optionName 281 opts = Utils.normalize_keys(opts); 282 this.options = Utils.merge(this.options, opts); 283 } 284 285 // allows customizing the global instance name via options too 286 global_instance_name = this.options.instance; 287 288 // TODO: parse/validate other options that can benefit 289 this.options.zindex = parseInt(this.options.zindex, 10); 290 this.options.mask = Utils.str2bool(this.options.mask); 291 292 // check for a possible global callback specified as a string 293 if ('string' === typeof this.options.expires) { 294 if ('function' === typeof context[this.options.expires]) { 295 this.options.expires = context[this.options.expires]; 296 } 297 } 298 299 // check if expires is a callback 300 if ('function' === typeof this.options.expires) { 301 // TODO: this might not always be as simple as this 302 this.options.expires = this.options.expires(); 303 } 304 305 // Proceed with our plans only if we're invoked via a <script> element 306 // that has the required id attribute. 307 // For manually created instances one must call run() explicitly. 308 if (this.script_el) { 309 this.run(); 310 } 311 }, 312 313 log: function(){ 314 if ('undefined' !== typeof console) { 315 console.log.apply(console, arguments); 316 } 317 }, 318 319 run: function() { 320 if (!this.agreed()) { 321 var self = this; 322 contentLoaded(win, function(){ 323 self.insert(); 324 }); 325 } 326 }, 327 328 build_viewport_mask: function() { 329 var mask = null; 330 if (true === this.options.mask) { 331 var mask_opacity = this.options.maskOpacity; 332 var bg = this.options.maskBackground; 333 var mask_markup = '<div id="cookiebanner-mask" style="' + 334 'position:fixed;top:0;left:0;width:100%;height:100%;' + 335 'background:' + bg + ';zoom:1;filter:alpha(opacity=' + 336 (mask_opacity * 100) +');opacity:' + mask_opacity +';' + 337 'z-index:' + this.options.zindex +';"></div>'; 338 var el = doc.createElement('div'); 339 el.innerHTML = mask_markup; 340 mask = el.firstChild; 341 } 342 return mask; 343 }, 344 345 agree: function() { 346 this.cookiejar.set(this.options.cookie, 1, this.options.expires, this.options.cookiePath); 347 return true; 348 }, 349 350 agreed: function(){ 351 return this.cookiejar.has(this.options.cookie); 352 }, 353 354 close: function() { 355 if (this.inserted) { 356 if (!this.closed) { 357 if (this.element) { 358 this.element.parentNode.removeChild(this.element); 359 } 360 if (this.element_mask) { 361 this.element_mask.parentNode.removeChild(this.element_mask); 362 } 363 this.closed = true; 364 } 365 }/* else { 366 throw new Error("Not inserted but closing?!"); 367 }*/ 368 return this.closed; 369 }, 370 371 agree_and_close:function() { 372 this.agree(); 373 return this.close(); 374 }, 375 376 // close and remove every trace of ourselves completely 377 cleanup: function() { 378 this.close(); 379 return this.unload(); 380 }, 381 382 unload: function() { 383 if (this.script_el) { 384 this.script_el.parentNode.removeChild(this.script_el); 385 } 386 context[global_instance_name] = undefined; 387 // delete context[global_instance_name]; 388 return true; 389 }, 390 391 insert: function() { 392 this.element_mask = this.build_viewport_mask(); 393 394 var zidx = this.options.zindex; 395 396 if (this.element_mask) {
397 // bump notice element's zindex so it's above the mask 398 zidx += 1; 399 } 400 401 var el = doc.createElement('div'); 402 el.className = 'cookiebanner'; 403 el.style.position = 'fixed'; 404 el.style.left = 0; 405 el.style.right = 0; 406 el.style.height = this.options.height; 407 el.style.minHeight = this.options.minHeight; 408 el.style.zIndex = zidx; 409 el.style.background = this.options.bg; 410 el.style.color = this.options.fg; 411 el.style.lineHeight = el.style.minHeight; 412 413 el.style.padding = '5px 16px'; 414 415 el.style.fontFamily = this.options.fontFamily; 416 el.style.fontSize = this.options.fontSize; 417 el.style.textAlign = this.options.textAlign; 418 419 if ('top' === this.options.position) { 420 el.style.top = 0; 421 } else { 422 el.style.bottom = 0; 423 } 424 425 el.innerHTML = '<div class="cookiebanner-close" style="float:right;padding-left:5px;">' + 426 this.options.closeText + '</div>' + 427 '<span>' + this.options.message + ' <a>' + this.options.linkmsg + '</a></span>'; 428 429 this.element = el; 430 431 var el_a = el.getElementsByTagName('a')[0]; 432 el_a.href = this.options.moreinfo; 433 el_a.target = '_blank'; 434 el_a.style.textDecoration = 'none'; 435 el_a.style.color = this.options.link; 436 437 var el_x = el.getElementsByTagName('div')[0]; 438 el_x.style.cursor = 'pointer'; 439 440 function on(el, ev, fn) { 441 var add = el.addEventListener ? 'addEventListener' : 'attachEvent', 442 pre = el.addEventListener ? '' : 'on'; 443 el[add](pre + ev, fn, false); 444 } 445 446 var self = this; 447 on(el_x, 'click', function(){ 448 self.agree_and_close(); 449 }); 450 451 if (this.element_mask) { 452 on(this.element_mask, 'click', function(){ 453 self.agree_and_close(); 454 }); 455 doc.body.appendChild(this.element_mask); 456 } 457 458 // Agree and close banner on window scroll if `acceptOnScroll` option is set `true` 459 if (this.options.acceptOnScroll) { 460 on(window, 'scroll', function(){ 461 self.agree_and_close(); 462 }); 463 } 464 465 doc.body.appendChild(this.element); 466 this.inserted = true; 467 468 if ('fade' === this.options.effect) { 469 this.element.style.opacity = 0; 470 Utils.fade_in(this.element); 471 } else { 472 this.element.style.opacity = 1; 473 } 474 } 475 476 }; 477 478 if (script_el_invoker) { 479 if (!context[global_instance_name]) { 480 context[global_instance_name] = new Cookiebanner(); 481 } 482 } 483 484})(window);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.