1 2/** 3 * released under MIT license by Insites 4 * https://github.com/insites/cookieconsen 5 * small changes by Die Medialen 6 */ 7 8function isTrackingAllowed() { 9 if (window.cookieconsent) return cookieconsent.utils.isTrackingAllowed(); 10 else return false; 11} 12 13(function (cc) { 14 // stop from running again, if accidently included more than once. 15 if (cc.hasInitialised) return; 16 17 var util = { 18 // http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex 19 escapeRegExp: function (str) { 20 return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); 21 }, 22 23 hasClass: function (element, selector) { 24 var s = ' '; 25 return element.nodeType === 1 && 26 (s + element.className + s).replace(/[\n\t]/g, s).indexOf(s + selector + s) >= 0; 27 }, 28 29 addClass: function (element, className) { 30 element.className += ' ' + className; 31 }, 32 33 removeClass: function (element, className) { 34 var regex = new RegExp('\\b' + this.escapeRegExp(className) + '\\b'); 35 element.className = element.className.replace(regex, ''); 36 }, 37 38 interpolateString: function (str, callback) { 39 var marker = /{{([a-z][a-z0-9\-_]*)}}/ig; 40 return str.replace(marker, function (matches) { 41 return callback(arguments[1]) || ''; 42 }) 43 }, 44 45 getCookie: function (name) { 46 var value = '; ' + document.cookie; 47 var parts = value.split('; ' + name + '='); 48 return parts.length != 2 ? 49 undefined : parts.pop().split(';').shift(); 50 }, 51 52 setCookie: function (name, value, expiryDays, domain, path) { 53 var exdate = new Date(); 54 exdate.setDate(exdate.getDate() + (expiryDays || 365)); 55 56 var cookie = [ 57 name + '=' + value, 58 'expires=' + exdate.toUTCString(), 59 'path=' + (path || '/') 60 ]; 61 62 if (domain) { 63 cookie.push('domain=' + domain); 64 } 65 document.cookie = cookie.join(';'); 66 }, 67 68 // only used for extending the initial options 69 deepExtend: function (target, source) { 70 for (var prop in source) { 71 if (source.hasOwnProperty(prop)) { 72 if (prop in target && this.isPlainObject(target[prop]) && this.isPlainObject(source[prop])) { 73 this.deepExtend(target[prop], source[prop]); 74 } else { 75 target[prop] = source[prop]; 76 } 77 } 78 } 79 return target; 80 } 81 , 82 83 // only used for throttling the 'mousemove' event (used for animating the revoke button when `animateRevokable` is true) 84 throttle: function (callback, limit) { 85 var wait = false; 86 return function () { 87 if (!wait) { 88 callback.apply(this, arguments); 89 wait = true; 90 setTimeout(function () { 91 wait = false; 92 }, limit); 93 } 94 } 95 } 96 , 97 98 // only used for hashing json objects (used for hash mapping palette objects, used when custom colours are passed through JavaScript) 99 hash: function (str) { 100 var hash = 0, 101 i, chr, len; 102 if (str.length === 0) return hash; 103 for (i = 0, len = str.length; i < len; ++i) { 104 chr = str.charCodeAt(i); 105 hash = ((hash << 5) - hash) + chr; 106 hash |= 0; 107 } 108 return hash; 109 } 110 , 111 112 normaliseHex: function (hex) { 113 if (hex[0] == '#') { 114 hex = hex.substr(1); 115 } 116 if (hex.length == 3) { 117 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 118 } 119 return hex; 120 } 121 , 122 123 // used to get text colors if not set 124 getContrast: function (hex) { 125 hex = this.normaliseHex(hex); 126 var r = parseInt(hex.substr(0, 2), 16); 127 var g = parseInt(hex.substr(2, 2), 16); 128 var b = parseInt(hex.substr(4, 2), 16); 129 var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000; 130 return (yiq >= 128) ? '#000' : '#fff'; 131 } 132 , 133 134 // used to change color on highlight 135 getLuminance: function (hex) { 136 var num = parseInt(this.normaliseHex(hex), 16), 137 amt = 38, 138 R = (num >> 16) + amt, 139 B = (num >> 8 & 0x00FF) + amt, 140 G = (num & 0x0000FF) + amt; 141 var newColour = (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + (B < 255 ? B < 1 ? 0 : B : 255) * 0x100 + (G < 255 ? G < 1 ? 0 : G : 255)).toString(16).slice(1); 142 return '#' + newColour; 143 } 144 , 145 146 isMobile: function () { 147 return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); 148 } 149 , 150 151 isPlainObject: function (obj) { 152 // The code "typeof obj === 'object' && obj !== null" allows Array objects 153 return typeof obj === 'object' && obj !== null && obj.constructor == Object; 154 } 155 , 156 isTrackingAllowed: function() { 157 var cookieValue = util.getCookie("tracking_cookie_consent"); 158 return (navigator.doNotTrack != 1) && (["dismiss", "allow"].includes(cookieValue)); 159 } 160 } 161 ; 162 163 // valid cookie values 164 cc.status = { 165 deny: 'deny', 166 allow: 'allow',
167 dismiss: 'dismiss' 168 }; 169 170 // detects the `transitionend` event name 171 cc.transitionEnd = (function () { 172 var el = document.createElement('div'); 173 var trans = { 174 t: "transitionend", 175 OT: "oTransitionEnd", 176 msT: "MSTransitionEnd", 177 MozT: "transitionend", 178 WebkitT: "webkitTransitionEnd", 179 }; 180 181 for (var prefix in trans) { 182 if (trans.hasOwnProperty(prefix) && typeof el.style[prefix + 'ransition'] != 'undefined') { 183 return trans[prefix]; 184 } 185 } 186 return ''; 187 }()); 188 189 cc.hasTransition = !!cc.transitionEnd; 190 191 // array of valid regexp escaped statuses 192 var __allowedStatuses = Object.keys(cc.status).map(util.escapeRegExp); 193 194 // contains references to the custom <style> tags 195 cc.customStyles = {}; 196 197 cc.Popup = (function () { 198 199 var defaultOptions = { 200 201 // if false, this prevents the popup from showing (useful for giving to control to another piece of code) 202 enabled: true, 203 204 // optional (expecting a HTML element) if passed, the popup is appended to this element. default is `document.body` 205 container: null, 206 207 // defaults cookie options - it is RECOMMENDED to set these values to correspond with your server 208 cookie: { 209 // This is the name of this cookie - you can ignore this 210 name: 'tracking_cookie_consent', 211 212 // This is the url path that the cookie 'name' belongs to. The cookie can only be read at this location 213 path: '/', 214 215 // This is the domain that the cookie 'name' belongs to. The cookie can only be read on this domain. 216 // - Guide to cookie domains - http://erik.io/blog/2014/03/04/definitive-guide-to-cookie-domains/ 217 domain: '', 218 219 // The cookies expire date, specified in days (specify -1 for no expiry) 220 expiryDays: 365, 221 }, 222 223 // these callback hooks are called at certain points in the program execution 224 onPopupOpen: function () { 225 }, 226 onPopupClose: function () { 227 }, 228 onInitialise: function (status) { 229 }, 230 onStatusChange: function (status, chosenBefore) { 231 }, 232 onRevokeChoice: function () { 233 }, 234 // each item defines the inner text for the element that it references 235 content: { 236 header: 'Cookies used on the website!', 237 message: 'This website uses cookies to ensure you get the best experience on our website.', 238 dismiss: 'OK', 239 allow: 'OK', 240 deny: 'Decline', 241 link: 'Learn more',
242 href: 'http://cookiesandyou.com', 243 close: '❌', 244 optout_text: 'Von anonymer Nutzeranalyse abmelden', 245 checkbox_text: 'Tracking unterbinden', 246 dnt_text: 'Do Not Track ist in Ihrem Browser gesetzt' 247 }, 248 249 // This is the HTML for the elements above. The string {{header}} will be replaced with the equivalent text below. 250 // You can remove "{{header}}" and write the content directly inside the HTML if you want. 251 // 252 // - ARIA rules suggest to ensure controls are tabbable (so the browser can find the first control), 253 // and to set the focus to the first interactive control (http://w3c.github.io/aria-in-html/) 254 elements: { 255 header: '<span class="cc-header">{{header}}</span> ', 256 message: '<span id="cookieconsent:desc" class="cc-message">{{message}}</span>', 257 messagelink: '<span id="cookieconsent:desc" class="cc-message">{{message}} <a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" rel="noopener noreferrer nofollow" target="_blank">{{link}}</a></span>', 258 dismiss: '<a aria-label="dismiss cookie message" role=button tabindex="0" class="cc-btn cc-dismiss">{{dismiss}}</a>', 259 allow: '<a aria-label="allow cookies" role=button tabindex="0" class="cc-btn cc-allow">{{allow}}</a>', 260 deny: '<a aria-label="deny cookies" role=button tabindex="0" class="cc-btn cc-deny">{{deny}}</a>', 261 link: '<a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" target="_blank">{{link}}</a>', 262 close: '<span aria-label="dismiss cookie message" role=button tabindex="0" class="cc-close">{{close}}</span>', 263 slideout: '<div class="slideout"><input type="checkbox" name="cc-tracking-checkbox" id="cc-tracking-checkbox" /><label for="cc-tracking-checkbox">{{checkbox_text}}</label></div>', 264 messageoptout: '<span id="cookieconsent:desc" class="cc-message">{{message}} <a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" rel="noopener noreferrer nofollow" target="_blank">{{link}}</a><span class="cc-link cookieaccordion">{{optout_text}}</span></span>' 265 //compliance: compliance is also an element, but it is generated by the application, depending on `type` below 266 }, 267 268 // The placeholders {{classes}} and {{children}} both get replaced during initialisation: 269 // - {{classes}} is where additional classes get added 270 // - {{children}} is where the HTML children are placed 271 window: '<div role="dialog" aria-live="polite" aria-label="cookieconsent" aria-describedby="cookieconsent:desc" class="cc-window {{classes}}"><!--googleoff: all-->{{children}}<!--googleon: all--></div>', 272 273 // This is the html for the revoke button. This only shows up after the user has selected their level of consent 274 // It can be enabled of disabled using the `revokable` option 275 revokeBtn: '<div class="cc-revoke {{classes}}">Cookie Policy</div>', 276 277 // define types of 'compliance' here. '{{value}}' strings in here are linked to `elements` 278 compliance: { 279 'info': '<div class="cc-compliance">{{dismiss}}</div>', 280 'opt-in': '<div class="cc-compliance cc-highlight">{{dismiss}}{{allow}}</div>', 281 'opt-out': '<div class="cc-compliance cc-highlight">{{deny}}{{dismiss}}</div>', 282 }, 283 284 // select your type of popup here 285 type: 'info', // refers to `compliance` (in other words, the buttons that are displayed) 286 287 // define layout layouts here 288 layouts: { 289 // the 'block' layout tend to be for square floating popups 290 'basic': '{{messagelink}}{{compliance}}', 291 'basic-slideout': '{{messageoptout}}{{compliance}}{{slideout}}', 292 'basic-close': '{{messagelink}}{{compliance}}{{close}}', 293 'basic-header': '{{header}}{{message}}{{link}}{{compliance}}', 294 295 // add a custom layout here, then add some new css with the class '.cc-layout-my-cool-layout' 296 //'my-cool-layout': '<div class="my-special-layout">{{message}}{{compliance}}</div>{{close}}', 297 }, 298 299 // default layout (see above) 300 layout: 'basic-slideout', 301 302 // this refers to the popup windows position. we currently support: 303 // - banner positions: top, bottom 304 // - floating positions: top-left, top-right, bottom-left, bottom-right 305 // 306 // adds a class `cc-floating` or `cc-banner` which helps when styling 307 position: 'bottom', // default position is 'bottom' 308 309 // Available styles 310 // -block (default, no extra classes) 311 // -edgeless 312 // -classic 313 // use your own style name and use `.cc-theme-STYLENAME` class in CSS to edit. 314 // Note: style "wire" is used for the configurator, but has no CSS styles of its own, only palette is used. 315 theme: 'block', 316 317 // The popup is `fixed` by default, but if you want it to be static (inline with the page content), set this to fal
317se 318 // Note: by default, we animate the height of the popup from 0 to full size 319 static: false, 320 321 // if you want custom colours, pass them in here. this object should look like this. 322 // ideally, any custom colours/themes should be created in a separate style sheet, as this is more efficient. 323 // { 324 // popup: {background: '#000000', text: '#fff', link: '#fff'}, 325 // button: {background: 'transparent', border: '#f8e71c', text: '#f8e71c'}, 326 // highlight: {background: '#f8e71c', border: '#f8e71c', text: '#000000'}, 327 // } 328 // `highlight` is optional and extends `button`. if it exists, it will apply to the first button 329 // only background needs to be defined for every element. if not set, other colors can be calculated from it 330 palette: null, 331 332 // Some countries REQUIRE that a user can change their mind. You can configure this yourself. 333 // Most of the time this should be false, but the `cookieconsent.law` can change this to `true` if it detects that it should 334 revokable: false, 335 336 // if true, the revokable button will tranlate in and out 337 animateRevokable: true, 338 339 // used to disable link on existing layouts 340 // replaces element messagelink with message and removes content of link 341 showLink: true, 342 343 // set value as scroll range to enable 344 dismissOnScroll: false, 345 346 // set value as time in milliseconds to autodismiss after set time 347 dismissOnTimeout: false, 348 349 // The application automatically decide whether the popup should open. 350 // Set this to false to prevent this from happening and to allow you to control the behaviour yourself 351 autoOpen: true, 352 353 // By default the created HTML is automatically appended to the container (which defaults to <body>). You can prevent this behaviour 354 // by setting this to false, but if you do, you must attach the `element` yourself, which is a public property of the popup instance: 355 // 356 // var instance = cookieconsent.factory(options); 357 // document.body.appendChild(instance.element); 358 // 359 autoAttach: true, 360 361 // simple whitelist/blacklist for pages. specify page by: 362 // - using a string : '/index.html' (matches '/index.html' exactly) OR 363 // - using RegExp : /\/page_[\d]+\.html/ (matched '/page_1.html' and '/page_2.html' etc) 364 whitelistPage: [], 365 blacklistPage: [], 366 367 // If this is defined, then it is used as the inner html instead of layout. This allows for ultimate customisation. 368 // Be sure to use the classes `cc-btn` and `cc-allow`, `cc-deny` or `cc-dismiss`. They enable the app to register click 369 // handlers. You can use other pre-existing classes too. See `src/styles` folder. 370 overrideHTML: null, 371 }; 372 373 function CookiePopup() { 374 this.initialise.apply(this, arguments); 375 $('.cc-window .cc-message .cookieaccordion').on('click', function () { 376 $('.cc-window .slideout').slideToggle(); 377 }); 378 } 379 380 CookiePopup.prototype.initialise = function (options) { 381 if (this.options) { 382 this.destroy(); // already rendered 383 } 384 385 // set options back to default options 386 util.deepExtend(this.options = {}, defaultOptions); 387 388 // merge in user options 389 if (util.isPlainObject(options)) { 390 util.deepExtend(this.options, options); 391 } 392 393 // returns true if `onComplete` was called 394 if (checkCallbackHooks.call(this)) { 395 // user has already answered 396 this.options.enabled = false; 397 } 398 399 // apply blacklist / whitelist 400 if (arrayContainsMatches(this.options.blacklistPage, location.pathname)) { 401 this.options.enabled = false;
402 } 403 if (arrayContainsMatches(this.options.whitelistPage, location.pathname)) { 404 this.options.enabled = true; 405 } 406 407 // the full markup either contains the wrapper or it does not (for multiple instances) 408 var cookiePopup = this.options.window 409 .replace('{{classes}}', getPopupClasses.call(this).join(' ')) 410 .replace('{{children}}', getPopupInnerMarkup.call(this)); 411 412 // if user passes html, use it instead 413 var customHTML = this.options.overrideHTML; 414 if (typeof customHTML == 'string' && customHTML.length) { 415 cookiePopup = customHTML; 416 } 417 418 // if static, we need to grow the element from 0 height so it doesn't jump the page 419 // content. we wrap an element around it which will mask the hidden content 420 if (this.options.static) { 421 // `grower` is a wrapper div with a hidden overflow whose height is animated 422 var wrapper = appendMarkup.call(this, '<div class="cc-grower">' + cookiePopup + '</div>'); 423 424 wrapper.style.display = ''; // set it to visible (because appendMarkup hides it) 425 this.element = wrapper.firstChild; // get the `element` reference from the wrapper 426 this.element.style.display = 'none'; 427 util.addClass(this.element, 'cc-invisible'); 428 } else { 429 this.element = appendMarkup.call(this, cookiePopup); 430 } 431 432 applyAutoDismiss.call(this); 433 434 applyRevokeButton.call(this); 435 436 if (this.options.autoOpen) { 437 this.autoOpen(); 438 } 439 }; 440 441 CookiePopup.prototype.destroy = function () { 442 if (this.onButtonClick && this.element) { 443 this.element.removeEventListener('click', this.onButtonClick); 444 this.onButtonClick = null; 445 } 446 447 if (this.dismissTimeout) { 448 clearTimeout(this.dismissTimeout); 449 this.dismissTimeout = null; 450 } 451 452 if (this.onWindowScroll) { 453 window.removeEventListener('scroll', this.onWindowScroll); 454 this.onWindowScroll = null; 455 } 456 457 if (this.onMouseMove) { 458 window.removeEventListener('mousemove', this.onMouseMove); 459 this.onMouseMove = null; 460 } 461 462 if (this.element && this.element.parentNode) { 463 this.element.parentNode.removeChild(this.element); 464 } 465 this.element = null; 466 467 if (this.revokeBtn && this.revokeBtn.parentNode) { 468 this.revokeBtn.parentNode.removeChild(this.revokeBtn); 469 } 470 this.revokeBtn = null; 471 472 removeCustomStyle(this.options.palette); 473 this.options = null; 474 }; 475 476 CookiePopup.prototype.open = function (callback) { 477 if (!this.element) return; 478 479 if (!this.isOpen()) { 480 if (cc.hasTransition) { 481 this.fadeIn(); 482 } else { 483 this.element.style.display = ''; 484 } 485 486 if (this.options.revokable) { 487 this.toggleRevokeButton(); 488 } 489 this.options.onPopupOpen.call(this); 490 attachCheckboxHandler('cc-tracking-checkbox', this); 491 } 492 493 return this; 494 }; 495 CookiePopup.prototype.close = function (showRevoke) { 496 if (!this.element) return; 497 498 if (this.isOpen()) { 499 if (cc.hasTransition) { 500 this.fadeOut(); 501 } else { 502 this.element.style.display = 'none'; 503 } 504 505 if (showRevoke && this.options.revokable) { 506 this.toggleRevokeButton(true); 507 } 508 this.options.onPopupClose.call(this); 509 } 510 511 return this; 512 }; 513 514 CookiePopup.prototype.fadeIn = function () { 515 var el = this.element; 516 517 if (!cc.hasTransition || !el) 518 return; 519 520 // This should always be called AFTER fadeOut (which is governed by the 'transitionend' event). 521 // 'transitionend' isn't all that reliable, so, if we try and fadeIn before 'transitionend' has 522 // has a chance to run, then we run it ourselves 523 if (this.afterTransition) { 524 afterFadeOut.call(this, el) 525 } 526 527 if (util.hasClass(el, 'cc-invisible')) { 528 el.style.display = ''; 529 530 if (this.options.static) { 531 var height = this.element.clientHeight; 532 this.element.parentNode.style.maxHeight = '100%'; 533 } 534 535 var fadeInTimeout = 20; // (ms) DO NOT MAKE THIS VALUE SMALLER. See below 536 537 // Although most browsers can handle values less than 20ms, it should remain above this value. 538 // This is because we are waiting for a "browser redraw" before we remove the 'cc-invisible' class. 539 // If the class is remvoed before a redraw could happen, then the fadeIn effect WILL NOT work, and 540 // the popup will appear from nothing. Therefore we MUST allow enough time for the browser to do 541 // its thing. The actually difference between using 0 and 20 in a set timeout is neglegible anyway 542 this.openingTimeout = setTimeout(afterFadeIn.bind(this, el), fadeInTimeout); 543 } 544 }; 545
546 CookiePopup.prototype.fadeOut = function () { 547 var el = this.element; 548 549 if (!cc.hasTransition || !el) 550 return; 551 552 if (this.openingTimeout) { 553 clearTimeout(this.openingTimeout); 554 afterFadeIn.bind(this, el); 555 } 556 557 if (!util.hasClass(el, 'cc-invisible')) { 558 if (this.options.static) { 559 this.element.parentNode.style.maxHeight = ''; 560 } 561 562 this.afterTransition = afterFadeOut.bind(this, el); 563 el.addEventListener(cc.transitionEnd, this.afterTransition); 564 565 util.addClass(el, 'cc-invisible'); 566 } 567 }; 568 569 CookiePopup.prototype.isOpen = function () { 570 return this.element && this.element.style.display == '' && (cc.hasTransition ? !util.hasClass(this.element, 'cc-invisible') : true); 571 }; 572 573 CookiePopup.prototype.toggleRevokeButton = function (show) { 574 if (this.revokeBtn) this.revokeBtn.style.display = show ? '' : 'none'; 575 }; 576 577 CookiePopup.prototype.revokeChoice = function (preventOpen) { 578 this.options.enabled = true; 579 this.clearStatus(); 580 581 this.options.onRevokeChoice.call(this); 582 583 if (!preventOpen) { 584 this.autoOpen(); 585 } 586 }; 587 588 // returns true if the cookie has a valid value 589 CookiePopup.prototype.hasAnswered = function (options) { 590 return Object.keys(cc.status).indexOf(this.getStatus()) >= 0; 591 }; 592 593 // returns true if the cookie indicates that consent has been given 594 CookiePopup.prototype.hasConsented = function (options) { 595 var val = this.getStatus(); 596 return val == cc.status.allow || val == cc.status.dismiss; 597 }; 598 599 // opens the popup if no answer has been given 600 CookiePopup.prototype.autoOpen = function (options) { 601 !this.hasAnswered() && this.options.enabled && this.open(); 602 }; 603 604 CookiePopup.prototype.setStatus = function (status) { 605 var c = this.options.cookie; 606 var value = util.getCookie(c.name); 607 var chosenBefore = Object.keys(cc.status).indexOf(value) >= 0; 608 609 // if `status` is valid 610 if (Object.keys(cc.status).indexOf(status) >= 0) { 611 612 util.setCookie(c.name, status, c.expiryDays, c.domain, c.path); 613 614 this.options.onStatusChange.call(this, status, chosenBefore); 615 } else { 616 this.clearStatus(); 617 } 618 }; 619 620 CookiePopup.prototype.getStatus = function () { 621 return util.getCookie(this.options.cookie.name); 622 }; 623 624 CookiePopup.prototype.clearStatus = function () { 625 var c = this.options.cookie; 626 util.setCookie(c.name, '', -1, c.domain, c.path); 627 }; 628 629 // This needs to be called after 'fadeIn'. This is the code that actually causes the fadeIn to work 630 // There is a good reason why it's called in a timeout. Read 'fadeIn'; 631 function afterFadeIn(el) { 632 this.openingTimeout = null; 633 util.removeClass(el, 'cc-invisible'); 634 } 635 636 // This is called on 'transitionend' (only on the transition of the fadeOut). That's because after we've faded out, we need to 637 // set the display to 'none' (so there aren't annoying invisible popups all over the page). If for whenever reason this function 638 // is not called (lack of support), the open/close mechanism will still work. 639 function afterFadeOut(el) { 640 el.style.display = 'none'; // after close and before open, the display should be none 641 el.removeEventListener(cc.transitionEnd, this.afterTransition); 642 this.afterTransition = null; 643 } 644 645 // this function calls the `onComplete` hook and returns true (if needed) and returns false otherwise 646 function checkCallbackHooks() { 647 var complete = this.options.onInitialise.bind(this); 648 649 if (!window.navigator.cookieEnabled) { 650 complete(cc.status.deny); 651 return true; 652 } 653 654 if (window.CookiesOK || window.navigator.CookiesOK) { 655 complete(cc.status.allow); 656 return true; 657 } 658 659 var allowed = Object.keys(cc.status);
660 var answer = this.getStatus(); 661 var match = allowed.indexOf(answer) >= 0; 662 663 if (match) { 664 complete(answer); 665 } 666 return match; 667 } 668 669 function attachCheckboxHandler(checkboxId, popup) { 670 var cbox = document.getElementById(checkboxId); 671 672 if (cbox != undefined) { 673 //checkbox anhand cookies und dnt setzen 674 if (navigator.doNotTrack == 1) { 675 //opt-out mandatory with DNT set 676 cbox.checked = "checked"; 677 if (document.querySelector) { 678 var label = document.querySelector("label[for=" + "cc-tracking-checkbox" + "]"); 679 label.innerHTML = popup.options.content.dnt_text; 680 681 } 682 } 683 else if (util.getCookie("tracking_cookie_consent")!="deny" && navigator.doNotTrack != 1) { 684 cbox.checked = false; 685 } 686 cbox.onclick = function () { 687 if (navigator.doNotTrack == 1) { 688 this.checked = "checked"; //opt-out mandatory with DNT set 689 } 690 else if (this.checked) { 691 util.setCookie("tracking_cookie_consent", "deny", 365); 692 } 693 else { 694 util.setCookie("tracking_cookie_consent", "", 365); 695 } 696 // popup.close(true); 697 }; 698 } 699 }; 700 701 function getPositionClasses() { 702 var positions = this.options.position.split('-'); // top, bottom, left, right 703 var classes = []; 704 705 // top, left, right, bottom 706 positions.forEach(function (cur) { 707 classes.push('cc-' + cur); 708 }); 709 710 return classes; 711 } 712 713 function getPopupClasses() { 714 var opts = this.options; 715 var positionStyle = (opts.position == 'top' || opts.position == 'bottom') ? 'banner' : 'floating'; 716 717 if (util.isMobile()) { 718 positionStyle = 'floating'; 719 } 720 721 var classes = [ 722 'cc-' + positionStyle, // floating or banner 723 'cc-type-' + opts.type, // add the compliance type 724 'cc-theme-' + opts.theme, // add the theme 725 ]; 726 727 if (opts.static) { 728 classes.push('cc-static'); 729 } 730 731 classes.push.apply(classes, getPositionClasses.call(this)); 732 733 // we only add extra styles if `palette` has been set to a valid value 734 var didAttach = attachCustomPalette.call(this, this.options.palette); 735 736 // if we override the palette, add the class that enables this 737 if (this.customStyleSelector) { 738 classes.push(this.customStyleSelector); 739 } 740 741 return classes; 742 } 743 744 function getPopupInnerMarkup() { 745 var interpolated = {}; 746 var opts = this.options; 747 748 // removes link if showLink is false 749 if (!opts.showLink) { 750 opts.elements.link = ''; 751 opts.elements.messagelink = opts.elements.message; 752 } 753 754 Object.keys(opts.elements).forEach(function (prop) { 755 interpolated[prop] = util.interpolateString(opts.elements[prop], function (name) { 756 var str = opts.content[name]; 757 return (name && typeof str == 'string' && str.length) ? str : ''; 758 }) 759 }); 760 761 // checks if the type is valid and defaults to info if it's not 762 var complianceType = opts.compliance[opts.type]; 763 if (!complianceType) { 764 complianceType = opts.compliance.info; 765 } 766 767 // build the compliance types from the already interpolated `elements` 768 interpolated.compliance = util.interpolateString(complianceType, function (name) { 769 return interpolated[name]; 770 }); 771 772 // checks if the layout is valid and defaults to basic if it's not 773 var layout = opts.layouts[opts.layout]; 774 if (!layout) { 775 layout = opts.layouts.basic; 776 } 777 778 return util.interpolateString(layout, function (match) { 779 return interpolated[match]; 780 }); 781 } 782 783 function appendMarkup(markup) { 784 var opts = this.options; 785 var div = document.createElement('div'); 786 var cont = (opts.container && opts.container.nodeType === 1) ? opts.container : document.body; 787 788 div.innerHTML = markup; 789 790 var el = div.children[0]; 791 792 el.style.display = 'none'; 793 794 if (util.hasClass(el, 'cc-window') && cc.hasTransition) { 795 util.addClass(el, 'cc-invisible'); 796 } 797 798 // save ref to the function handle so we can unbind it later 799 this.onButtonClick = handleButtonClick.bind(this); 800 801 el.addEventListener('click', this.onButtonClick); 802 803 if (opts.autoAttach) { 804 if (!cont.firstChild) { 805 cont.appendChild(el); 806 } else { 807 cont.insertBefore(el, cont.firstChild) 808 } 809 } 810 811 return el; 812 } 813 814 function handleButtonClick(event) { 815 var targ = event.target; 816 var fin=false; 817 if (util.hasClass(targ, 'cc-btn')) { 818 var matches = targ.className.match(new RegExp("\\bcc-(" + __allowedStatuses.join('|') + ")\\b")); 819 var match = (matches && matches[1]) || false; 820 if (match == "dismiss" && util.getCookie("tracking_cookie_consent") == "deny") match = "deny"; 821 if (match) { 822 this.setStatus(match); 823 this.close(true); 824 } 825 if (match == "dismiss" && util.getCookie("tracking_cookie_consent") !== "deny") fin=true; 826 } 827 if (util.hasClass(targ, 'cc-close')) { 828 if (util.isTrackingAllowed()) { 829 this.setStatus(cc.status.allow); 830 } 831 else { 832 this.setStatus(cc.status.deny); 833 } 834 this.close(true); 835 } 836 if (util.hasClass(targ, 'cc-revoke')) { 837 this.revokeChoice(); 838 } 839 if(fin) location.reload(); 840 } 841 842 // I might change this function to use inline styles. I originally chose a stylesheet be
842cause I could select many elements with a 843 // single rule (something that happened a lot), the apps has changed slightly now though, so inline styles might be more applicable. 844 function attachCustomPalette(palette) { 845 var hash = util.hash(JSON.stringify(palette)); 846 var selector = 'cc-color-override-' + hash; 847 var isValid = util.isPlainObject(palette); 848 849 this.customStyleSelector = isValid ? selector : null; 850 851 if (isValid) { 852 addCustomStyle(hash, palette, '.' + selector); 853 } 854 return isValid; 855 } 856 857 function addCustomStyle(hash, palette, prefix) { 858 859 // only add this if a style like it doesn't exist 860 if (cc.customStyles[hash]) { 861 // custom style already exists, so increment the reference count 862 ++cc.customStyles[hash].references; 863 return; 864 } 865 var colorStyles = {}; 866 var popup = palette.popup; 867 var button = palette.button; 868 var highlight = palette.highlight; 869 var deny = palette.deny; 870 871 // needs background colour, text and link will be set to black/white if not specified 872 if (popup) { 873 // assumes popup.background is set 874 popup.text = popup.text ? popup.text : util.getContrast(popup.background); 875 popup.link = popup.link ? popup.link : popup.text; 876 colorStyles[prefix + '.cc-window'] = [ 877 'color: ' + popup.text, 878 'background-color: ' + popup.background 879 ]; 880 colorStyles[prefix + '.cc-revoke'] = [ 881 'color: ' + popup.text, 882 'background-color: ' + popup.background 883 ]; 884 colorStyles[prefix + ' .cc-link,' + prefix + ' .cc-link:active,' + prefix + ' .cc-link:visited'] = [ 885 'color: ' + popup.link 886 ]; 887 888 if (button) { 889 // assumes button.background is set 890 button.text = button.text ? button.text : util.getContrast(button.background); 891 button.border = button.border ? button.border : 'transparent'; 892 colorStyles[prefix + ' .cc-btn'] = [ 893 'color: ' + button.text, 894 'border-color: ' + button.border, 895 'background-color: ' + button.background 896 ]; 897 898 if (button.background != 'transparent') 899 colorStyles[prefix + ' .cc-btn:hover, ' + prefix + ' .cc-btn:focus'] = [ 900 'background-color: ' + getHoverColour(button.background) 901 ]; 902 903 if (highlight) { 904 //assumes highlight.background is set 905 highlight.text = highlight.text ? highlight.text : util.getContrast(highlight.background); 906 highlight.border = highlight.border ? highlight.border : 'transparent'; 907 colorStyles[prefix + ' .cc-highlight .cc-btn:first-child'] = [ 908 'color: ' + highlight.text, 909 'border-color: ' + highlight.border, 910 'background-color: ' + highlight.background 911 ]; 912 } 913 } 914 if (deny) { 915 // assumes button.background is set 916 deny.text = deny.text ? deny.text : util.getContrast(deny.background); 917 deny.border = deny.border ? deny.border : 'transparent'; 918 colorStyles[prefix + ' .cc-deny'] = [ 919 'color: ' + deny.text, 920 'border-color: ' + deny.border, 921 'background-color: ' + deny.background 922 ]; 923 924 if (deny.background != 'transparent') 925 colorStyles[prefix + ' .cc-deny:hover, ' + prefix + ' .cc-deny:focus'] = [ 926 'background-color: ' + getHoverColour(deny.background) 927 ]; 928 929 if (highlight) { 930 //assumes highlight.background is set 931 highlight.text = highlight.text ? highlight.text : util.getContrast(highlight.background); 932 highlight.border = highlight.border ? highlight.border : 'transparent'; 933 colorStyles[prefix + ' .cc-highlight .cc-btn:first-child'] = [ 934 'color: ' + highlight.text, 935 'border-color: ' + highlight.border, 936 'background-color: ' + highlight.background 937 ]; 938 } 939 } 940 941 } 942 943 // this will be interpretted as CSS. the key is the selector, and each array element is a rule 944 var style = document.createElement('style'); 945 document.head.appendChild(style); 946 947 // custom style doesn't exist, so we create it 948 cc.customStyles[hash] = { 949 references: 1, 950 element: style.sheet 951 }; 952 953 var ruleIndex = -1; 954 for (var prop in colorStyles) { 955 if (colorStyles.hasOwnProperty(prop)) { 956 style.sheet.insertRule(prop + '{' + colorStyles[prop].join(';') + '}', ++ruleIndex); 957 } 958 } 959 } 960 961 function getHoverColour(hex) { 962 hex = util.normaliseHex(hex); 963 // for black buttons 964 if (hex == '000000') { 965 return '#222'; 966 } 967 return util.getLuminance(hex); 968 } 969 970 function removeCustomStyle(palette) { 971 if (util.isPlainObject(palette)) { 972 var hash = util.hash(JSON.stringify(palette)); 973 var customStyle = cc.customStyles[hash]; 974 if (customStyle && !--customStyle.references) { 975 var styleNode = customStyle.element.ownerNode; 976 if (styleNode && styleNode.parentNode) { 977 styleNode.parentNode.removeChild(styleNode); 978 } 979 cc.customStyles[hash] = null; 980 } 981 } 982 } 983 984 function arrayContainsMatches(array, search) { 985 for (var i = 0, l = array.length; i < l; ++i) { 986 var str = array[i]; 987 // if regex matches or string is equal, return true 988 if ((str instanceof RegExp && str.test(search)) || 989 (typeof str == 'string' && str.length && str === search)) { 990 return true; 991 } 992 } 993 return false;
994 } 995 996 function applyAutoDismiss() { 997 var setStatus = this.setStatus.bind(this); 998 999 var delay = this.options.dismissOnTimeout; 1000 if (typeof delay == 'number' && delay >= 0) { 1001 this.dismissTimeout = window.setTimeout(function () { 1002 setStatus(cc.status.dismiss); 1003 }, Math.floor(delay)); 1004 } 1005 1006 var scrollRange = this.options.dismissOnScroll; 1007 if (typeof scrollRange == 'number' && scrollRange >= 0) { 1008 var onWindowScroll = function (evt) { 1009 if (window.pageYOffset > Math.floor(scrollRange)) { 1010 setStatus(cc.status.dismiss); 1011 1012 window.removeEventListener('scroll', onWindowScroll); 1013 this.onWindowScroll = null; 1014 } 1015 }; 1016 1017 this.onWindowScroll = onWindowScroll; 1018 window.addEventListener('scroll', onWindowScroll); 1019 } 1020 } 1021 1022 function applyRevokeButton() { 1023 // revokable is true if advanced compliance is selected 1024 if (this.options.type != 'info') this.options.revokable = true; 1025 // animateRevokable false for mobile devices 1026 if (util.isMobile()) this.options.animateRevokable = false; 1027 1028 if (this.options.revokable) { 1029 var classes = getPositionClasses.call(this); 1030 if (this.options.animateRevokable) { 1031 classes.push('cc-animate'); 1032 } 1033 if (this.customStyleSelector) { 1034 classes.push(this.customStyleSelector) 1035 } 1036 var revokeBtn = this.options.revokeBtn.replace('{{classes}}', classes.join(' ')); 1037 this.revokeBtn = appendMarkup.call(this, revokeBtn); 1038 1039 var btn = this.revokeBtn; 1040 if (this.options.animateRevokable) { 1041 var wait = false; 1042 var onMouseMove = util.throttle(function (evt) { 1043 var active = false; 1044 var minY = 20; 1045 var maxY = (window.innerHeight - 20); 1046 1047 if (util.hasClass(btn, 'cc-top') && evt.clientY < minY) active = true; 1048 if (util.hasClass(btn, 'cc-bottom') && evt.clientY > maxY) active = true; 1049 1050 if (active) { 1051 if (!util.hasClass(btn, 'cc-active')) { 1052 util.addClass(btn, 'cc-active'); 1053 } 1054 } else { 1055 if (util.hasClass(btn, 'cc-active')) { 1056 util.removeClass(btn, 'cc-active'); 1057 } 1058 } 1059 }, 200); 1060 1061 this.onMouseMove = onMouseMove; 1062 window.addEventListener('mousemove', onMouseMove); 1063 } 1064 } 1065 } 1066 1067 return CookiePopup 1068 }()); 1069 1070 cc.Location = (function () { 1071 1072 // An object containing all the location services we have already set up. 1073 // When using a service, it could either return a data structure in plain text (like a JSON object) or an executable script 1074 // When the response needs to be executed by the browser, then `isScript` must be set to true, otherwise it won't work. 1075 1076 // When the service uses a script, the chances are that you'll have to use the script to make additional requests. In these 1077 // cases, the services `callback` property is called with a `done` function. When performing async operations, this must be called 1078 // with the data (or Error), and `cookieconsent.locate` will take care of the rest 1079 var defaultOptions = { 1080 1081 // The default timeout is 5 seconds. This is mainly needed to catch JSONP requests that error. 1082 // Otherwise there is no easy way to catch JSONP errors. That means that if a JSONP fails, the 1083 // app will take `timeout` milliseconds to react to a JSONP network error. 1084 timeout: 5000, 1085 1086 // the order that services will be attempted in 1087 services: [ 1088 'freegeoip', 1089 'ipinfo', 1090 'maxmind' 1091 1092 /* 1093 1094 // 'ipinfodb' requires some options, so we define it using an object 1095 // this object will be passed to the function that defines the service 1096 1097 { 1098 name: 'ipinfodb', 1099 interpolateUrl: { 1100 // obviously, this is a fake key 1101 api_key: 'vOgI3748dnIytIrsJcxS7qsDf6kbJkE9lN4yEDrXAqXcKUNvjjZPox3ekXqmMMld' 1102 }, 1103 }, 1104 1105 // as well as defining an object, you can define a function that returns an object 1106 1107 function () { 1108 return {name: 'ipinfodb'}; 1109 }, 1110 1111 */ 1112 ], 1113 1114 serviceDefinitions: { 1115 1116 freegeoip: function () { 1117 return { 1118 // This service responds with JSON, but they do not have
1118CORS set, so we must use JSONP and provide a callback 1119 // The `{callback}` is automatically rewritten by the tool 1120 url: '//freegeoip.net/json/?callback={callback}', 1121 isScript: true, // this is JSONP, therefore we must set it to run as a script 1122 callback: function (done, response) { 1123 try { 1124 var json = JSON.parse(response); 1125 return json.error ? toError(json) : { 1126 code: json.country_code 1127 }; 1128 } catch (err) { 1129 return toError({error: 'Invalid response (' + err + ')'}); 1130 } 1131 } 1132 } 1133 }, 1134 1135 ipinfo: function () { 1136 return { 1137 // This service responds with JSON, so we simply need to parse it and return the country code 1138 url: '//ipinfo.io', 1139 headers: ['Accept: application/json'], 1140 callback: function (done, response) { 1141 try { 1142 var json = JSON.parse(response); 1143 return json.error ? toError(json) : { 1144 code: json.country 1145 }; 1146 } catch (err) { 1147 return toError({error: 'Invalid response (' + err + ')'}); 1148 } 1149 } 1150 } 1151 }, 1152 1153 // This service requires an option to define `key`. Options are proived using objects or functions 1154 ipinfodb: function (options) { 1155 return { 1156 // This service responds with JSON, so we simply need to parse it and return the country code 1157 url: '//api.ipinfodb.com/v3/ip-country/?key={api_key}&format=json&callback={callback}', 1158 isScript: true, // this is JSONP, therefore we must set it to run as a script 1159 callback: function (done, response) { 1160 try { 1161 var json = JSON.parse(response); 1162 return json.statusCode == 'ERROR' ? toError({error: json.statusMessage}) : { 1163 code: json.countryCode 1164 }; 1165 } catch (err) { 1166 return toError({error: 'Invalid response (' + err + ')'}); 1167 } 1168 } 1169 } 1170 }, 1171 1172 maxmind: function () { 1173 return { 1174 // This service responds with a JavaScript file which defines additional functionality. Once loaded, we must 1175 // make an additional AJAX call. Therefore we provide a `done` callback that can be called asynchronously 1176 url: '//js.maxmind.com/js/apis/geoip2/v2.1/geoip2.js', 1177 isScript: true, // this service responds with a JavaScript file, so it must be run as a script 1178 callback: function (done) { 1179 // if everything went okay then `geoip2` WILL be defined 1180 if (!window.geoip2) { 1181 done(new Error('Unexpected response format. The downloaded script should have exported `geoip2` to the global scope')); 1182 return; 1183 } 1184 1185 geoip2.country(function (location) { 1186 try { 1187 done({ 1188 code: location.country.iso_code 1189 }); 1190 } catch (err) { 1191 done(toError(err)); 1192 } 1193 }, function (err) { 1194 done(toError(err)); 1195 }); 1196 1197 // We can't return anything, because we need to wait for the second AJAX call to return. 1198 // Then we can 'complete' the service by passing data or an error to the `done` callback. 1199 } 1200 } 1201 }, 1202 }, 1203 }; 1204 1205 function Location(options) { 1206 // Set up options 1207 util.deepExtend(this.options = {}, defaultOptions); 1208 1209 if (util.isPlainObject(options)) { 1210 util.deepExtend(this.options, options); 1211 } 1212 1213 this.currentServiceIndex = -1; // the index (in options) of the service we're currently using 1214 } 1215 1216 Location.prototype.getNextService = function () { 1217 var service; 1218 1219 do {
1220 service = this.getServiceByIdx(++this.currentServiceIndex); 1221 } while (this.currentServiceIndex < this.options.services.length && !service); 1222 1223 return service; 1224 }; 1225 1226 Location.prototype.getServiceByIdx = function (idx) { 1227 // This can either be the name of a default locationService, or a function. 1228 var serviceOption = this.options.services[idx]; 1229 1230 // If it's a string, use one of the location services. 1231 if (typeof serviceOption === 'function') { 1232 var dynamicOpts = serviceOption(); 1233 if (dynamicOpts.name) { 1234 util.deepExtend(dynamicOpts, this.options.serviceDefinitions[dynamicOpts.name](dynamicOpts)); 1235 } 1236 return dynamicOpts; 1237 } 1238 1239 // If it's a string, use one of the location services. 1240 if (typeof serviceOption === 'string') { 1241 return this.options.serviceDefinitions[serviceOption](); 1242 } 1243 1244 // If it's an object, assume {name: 'ipinfo', ...otherOptions} 1245 // Allows user to pass in API keys etc. 1246 if (util.isPlainObject(serviceOption)) { 1247 return this.options.serviceDefinitions[serviceOption.name](serviceOption); 1248 } 1249 1250 return null; 1251 }; 1252 1253 // This runs the service located at index `currentServiceIndex`. 1254 // If the service fails, `runNextServiceOnError` will continue trying each service until all fail, or one completes successfully 1255 Location.prototype.locate = function (complete, error) { 1256 var service = this.getNextService(); 1257 1258 if (!service) { 1259 error(new Error('No services to run')); 1260 return; 1261 } 1262 1263 this.callbackComplete = complete; 1264 this.callbackError = error; 1265 1266 this.runService(service, this.runNextServiceOnError.bind(this)); 1267 }; 1268 1269 // Potentially adds a callback to a url for jsonp. 1270 Location.prototype.setupUrl = function (service) { 1271 var serviceOpts = this.getCurrentServiceOpts(); 1272 return service.url.replace(/\{(.*?)\}/g, function (_, param) { 1273 if (param === 'callback') { 1274 var tempName = 'callback' + Date.now(); 1275 window[tempName] = function (res) { 1276 service.__JSONP_DATA = JSON.stringify(res); 1277 } 1278 return tempName; 1279 } 1280 if (param in serviceOpts.interpolateUrl) { 1281 return serviceOpts.interpolateUrl[param]; 1282 } 1283 }); 1284 }; 1285 1286 // requires a `service` object that defines at least a `url` and `callback` 1287 Location.prototype.runService = function (service, complete) { 1288 var self = this; 1289 1290 // basic check to ensure it resembles a `service` 1291 if (!service || !service.url || !service.callback) { 1292 return; 1293 } 1294 1295 // we call either `getScript` or `makeAsyncRequest` depending on the type of resource 1296 var requestFunction = service.isScript ? getScript : makeAsyncRequest; 1297 1298 var url = this.setupUrl(service); 1299 1300 // both functions have similar signatures so we can pass the same arguments to both 1301 requestFunction(url, function (xhr) { 1302 // if `!xhr`, then `getScript` function was used, so there is no response text 1303 var responseText = xhr ? xhr.responseText : ''; 1304 1305 // if the resource is a script, then this function is called after the script has been run. 1306 // if the script is JSONP, then a time defined function `callback_{Date.now}` has already 1307 // been called (as the JSONP callback). This callback sets the __JSONP_DATA property 1308 if (service.__JSONP_DATA) { 1309 responseText = service.__JSONP_DATA; 1310 delete service.__JSONP_DATA; 1311 } 1312 1313 // call the service callback with the response text (so it can parse the response) 1314 self.runServiceCallback.call(self, complete, service, responseText); 1315 1316 }, this.options.timeout, service.data, service.headers); 1317 1318 // `service.data` and `service.headers` are optional (they only count if `!service.isScript` anyway) 1319 }; 1320 1321 // The service request has run (and possibly has a `responseText`) [no `responseText` if `isScript`] 1322 // We need to run its callback which determines if its successful or not 1323 // `complete` is called on success or failure 1324 Location.prototype.runServiceCallback = function (complete, service, responseText) { 1325 var self = this;
1326 // this is the function that is called if the service uses the async callback in its handler method 1327 var serviceResultHandler = function (asyncResult) { 1328 // if `result` is a valid value, then this function shouldn't really run 1329 // even if it is called by `service.callback` 1330 if (!result) { 1331 self.onServiceResult.call(self, complete, asyncResult) 1332 } 1333 }; 1334 1335 // the function `service.callback` will either extract a country code from `responseText` and return it (in `result`) 1336 // or (if it has to make additional requests) it will call a `done` callback with the country code when it is ready 1337 var result = service.callback(serviceResultHandler, responseText); 1338 1339 if (result) { 1340 this.onServiceResult.call(this, complete, result); 1341 } 1342 }; 1343 1344 // This is called with the `result` from `service.callback` regardless of how it provided that result (sync or async). 1345 // `result` will be whatever is returned from `service.callback`. A service callback should provide an object with data 1346 Location.prototype.onServiceResult = function (complete, result) { 1347 // convert result to nodejs style async callback 1348 if (result instanceof Error || (result && result.error)) { 1349 complete.call(this, result, null); 1350 } else { 1351 complete.call(this, null, result); 1352 } 1353 }; 1354 1355 // if `err` is set, the next service handler is called 1356 // if `err` is null, the `onComplete` handler is called with `data` 1357 Location.prototype.runNextServiceOnError = function (err, data) { 1358 if (err) { 1359 this.logError(err); 1360 1361 var nextService = this.getNextService(); 1362 1363 if (nextService) { 1364 this.runService(nextService, this.runNextServiceOnError.bind(this)); 1365 } else { 1366 this.completeService.call(this, this.callbackError, new Error('All services failed')); 1367 } 1368 } else { 1369 this.completeService.call(this, this.callbackComplete, data); 1370 } 1371 }; 1372 1373 Location.prototype.getCurrentServiceOpts = function () { 1374 var val = this.options.services[this.currentServiceIndex]; 1375 1376 if (typeof val == 'string') { 1377 return {name: val}; 1378 } 1379 1380 if (typeof val == 'function') { 1381 return val(); 1382 } 1383 1384 if (util.isPlainObject(val)) { 1385 return val; 1386 } 1387 1388 return {}; 1389 }; 1390 1391 // calls the `onComplete` callback after resetting the `currentServiceIndex` 1392 Location.prototype.completeService = function (fn, data) { 1393 this.currentServiceIndex = -1; 1394 1395 fn && fn(data); 1396 }; 1397 1398 Location.prototype.logError = function (err) { 1399 var idx = this.currentServiceIndex; 1400 var service = this.getServiceByIdx(idx); 1401 1402 console.error('The service[' + idx + '] (' + service.url + ') responded with the following error', err); 1403 }; 1404 1405 function getScript(url, callback, timeout) { 1406 var timeoutIdx, s = document.createElement('script'); 1407 1408 s.type = 'text/' + (url.type || 'javascript'); 1409 s.src = url.src || url; 1410 s.async = false; 1411 1412 s.onreadystatechange = s.onload = function () { 1413 // this code handles two scenarios, whether called by onload or onreadystatechange 1414 var state = s.readyState; 1415 1416 clearTimeout(timeoutIdx); 1417 1418 if (!callback.done && (!state || /loaded|complete/.test(state))) { 1419 callback.done = true; 1420 callback(); 1421 s.onreadystatechange = s.onload = null; 1422 } 1423 }; 1424 1425 document.body.appendChild(s); 1426 1427 // You can't catch JSONP Errors, because it's handled by the script tag 1428 // one way is to use a timeout 1429 timeoutIdx = setTimeout(function () { 1430 callback.done = true; 1431 callback(); 1432 s.onreadystatechange = s.onload = null; 1433 }, timeout); 1434 } 1435 1436 function makeAsyncRequest(url, onComplete, timeout, postData, requestHeaders) { 1437 var xhr = new (window.XMLHttpRequest || window.ActiveXObject)('MSXML2.XMLHTTP.3.0'); 1438 1439 xhr.open(postData ? 'POST' : 'GET', url, 1); 1440 1441 xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 1442 xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 1443 1444 if (Array.isArray(requestHeaders)) { 1445 for (var i = 0, l = requestHeaders.length; i < l; ++i) { 1446 var split = requestHeaders[i].split(':', 2) 1447 xhr.setRequestHeader(split[0].replace(/^\s+|\s+$/g, ''), split[1].replace(/^\s+|\s+$/g, '')); 1448 } 1449 } 1450 1451 if (typeof onComplete == 'function') { 1452 xhr.onreadystatechange = function () { 1453 if (xhr.readyState > 3) { 1454 onComplete(xhr); 1455 } 1456 }; 1457 } 1458 1459 xhr.send(postData); 1460 } 1461 1462 function toError(obj) { 1463 return new Error('Error [' + (obj.code || 'UNKNOWN') + ']: ' + obj.error); 1464 } 1465 1466 return Location; 1467 }()); 1468 1469 cc.Law = (function () { 1470 1471 var defaultOptions = { 1472 // Make this false if you want to disable all regional overrides for settings. 1473 // If true, options can differ by country, depending on their cookie law. 1474 // It does not affect hiding the popup for countries that do not have cookie law. 1475 regionalLaw: true, 1476 1477 // countries that enforce some version of a cookie law 1478 hasLaw: ['AT', 'BE', 'BG', 'HR', 'CZ', 'CY', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'SK', '
1478SI', 'ES', 'SE', 'GB', 'UK'], 1479 1480 // countries that say that all cookie consent choices must be revokable (a user must be able too change their mind) 1481 revokable: ['HR', 'CY', 'DK', 'EE', 'FR', 'DE', 'LV', 'LT', 'NL', 'PT', 'ES'], 1482 1483 // countries that say that a person can only "consent" if the explicitly click on "I agree". 1484 // in these countries, consent cannot be implied via a timeout or by scrolling down the page 1485 explicitAction: ['HR', 'IT', 'ES'], 1486 }; 1487 1488 function Law(options) { 1489 this.initialise.apply(this, arguments); 1490 } 1491 1492 Law.prototype.initialise = function (options) { 1493 // set options back to default options 1494 util.deepExtend(this.options = {}, defaultOptions); 1495 1496 // merge in user options 1497 if (util.isPlainObject(options)) { 1498 util.deepExtend(this.options, options); 1499 } 1500 }; 1501 1502 Law.prototype.get = function (countryCode) { 1503 var opts = this.options; 1504 return { 1505 hasLaw: opts.hasLaw.indexOf(countryCode) >= 0, 1506 revokable: opts.revokable.indexOf(countryCode) >= 0, 1507 explicitAction: opts.explicitAction.indexOf(countryCode) >= 0, 1508 }; 1509 }; 1510 1511 Law.prototype.applyLaw = function (options, countryCode) { 1512 var country = this.get(countryCode); 1513 1514 if (!country.hasLaw) { 1515 // The country has no cookie law 1516 options.enabled = false; 1517 } 1518 1519 if (this.options.regionalLaw) { 1520 if (country.revokable) { 1521 // We must provide an option to revoke consent at a later time 1522 options.revokable = true; 1523 } 1524 1525 if (country.explicitAction) { 1526 // The user must explicitly click the consent button 1527 options.dismissOnScroll = false; 1528 options.dismissOnTimeout = false; 1529 } 1530 } 1531 return options; 1532 }; 1533 1534 return Law; 1535 }()); 1536 1537 // This function initialises the app by combining the use of the Popup, Locator and Law modules 1538 // You can string together these three modules yourself however you want, by writing a new function. 1539 cc.initialise = function (options, complete, error) { 1540 1541 1542 var law = new cc.Law(options.law); 1543 1544 if (!complete) complete = function () { 1545 }; 1546 if (!error) error = function () { 1547 }; 1548 1549 cc.getCountryCode(options, function (result) { 1550 // don't need the law or location options anymore 1551 delete options.law; 1552 delete options.location; 1553 1554 if (result.code) { 1555 options = law.applyLaw(options, result.code); 1556 } 1557 1558 complete(new cc.Popup(options)); 1559 }, function (err) { 1560 // don't need the law or location options anymore 1561 delete options.law; 1562 delete options.location; 1563 1564 error(err, new cc.Popup(options)); 1565 }); 1566 }; 1567 1568 // This function tries to find your current location. It either grabs it from a hardcoded option in 1569 // `options.law.countryCode`, or attempts to make a location service request. This function accepts 1570 // options (which can configure the `law` and `location` modules) and fires a callback with which 1571 // passes an object `{code: countryCode}` as the first argument (which can have undefined properties) 1572 cc.getCountryCode = function (options, complete, error) { 1573 if (options.law && options.law.countryCode) { 1574 complete({ 1575 code: options.law.countryCode 1576 }); 1577 return; 1578 } 1579 if (options.location) { 1580 var locator = new cc.Location(options.location); 1581 locator.locate(function (serviceResult) { 1582 complete(serviceResult || {}); 1583 }, error); 1584 return; 1585 } 1586 complete({}); 1587 }; 1588 1589 // export utils (no point in hiding them, so we may as well expose them) 1590 cc.utils = util; 1591 1592 // prevent this code from being run twice
1593 cc.hasInitialised = true; 1594 1595 window.cookieconsent = cc; 1596 1597}(window.cookieconsent || {})); 1598window.addEventListener("load", function() { 1599 var consentType = cookieConsentType; 1600 var position = cookieConsentPosition; 1601 var static = false; 1602 var revokable = (cookieConsentOptOut == 1); 1603 var layout = (consentType == "info") ? "basic-slideout" : "basic"; 1604 window.cookieconsent.initialise({ 1605 "palette": { 1606 "popup": { 1607 "background": cookieConsentPopupBackgroundColor, 1608 "text": cookieConsentPopupTextColor 1609 }, 1610 "button": { 1611 "background": cookieConsentButtonBackgroundColor, 1612 "text": cookieConsentButtonTextColor 1613 }, 1614 "deny": { 1615 "background": cookieConsentDisagreeButtonBackgroundColor, 1616 "text": cookieConsentDisagreeButtonTextColor 1617 } 1618 }, 1619 "type": consentType, 1620 "layout": layout, 1621 "theme": "edgeless", 1622 "position": position, 1623 "static": static, 1624 "revokable": revokable, 1625 "content": { 1626 "message": cookieConsentMessage, 1627 "dismiss": cookieConsentConfirm, 1628 "deny": cookieConsentDeny, 1629 "allow": cookieConsentConfirm, 1630 "link": cookieConsentLinkText, 1631 "href": cookieConsentUrl, 1632 "optout_text": cookieConsentOptOutText, 1633 "checkbox_text": cookieConsentCheckboxText, 1634 "dnt_text": cookieConsentDoNotTrackSetText 1635 }, 1636 "slideout": "<div class='optOut-slideout'><input type='checkbox' name='cc-checkbox' id='cc-checkbox' /><label for='cc-checkbox'>{{checkbox_text}}</label></div>'" 1637 1638 }); 1639 var cclink = (document.getElementsByClassName("cc-link")) ? document.getElementsByClassName("cc-link") : []; 1640 1641 for (var i = 0; i < cclink.length; i++) { 1642 cclink[i].style.color = cookieConsentPopupTextColor; 1643 } 1644 1645}); 1646
1647var cookieConsentUrl = "/datenschutz/"; 1648 var cookieConsentMessage = "Wir würden gerne Cookies verwenden, um die Präferenzen unserer Nutzer zu erkennen und diese Website optimal gestalten zu können. Detaillierte Informationen und wie Sie Ihre Einwilligung jederzeit widerrufen können, erhalten Sie in unserer"; 1649 var cookieConsentButtonTextColor = '#213a8f'; 1650 var cookieConsentButtonBackgroundColor = '#fff'; 1651 var cookieConsentPopupTextColor = '#ffffff'; 1652 var cookieConsentPopupBackgroundColor = '#2598d5'; 1653 var cookieConsentLinkText = "Datenschutzerklärung"; 1654 var cookieConsentConfirm = "Akzeptieren"; 1655 var cookieConsentDeny = "Ablehnen"; 1656 var cookieConsentOptOut = 1; 1657 var cookieConsentPosition = 'bottom'; 1658 var cookieConsentOptOutText = "Von anonymer Nutzeranalyse abmelden"; 1659 var cookieConsentCheckboxText = "Tracking unterbinden"; 1660 var cookieConsentDoNotTrackSetText = "Do not Track ist in Ihrem Browser aktiviert"; 1661 var cookieConsentType = 'opt-out'; 1662 var cookieConsentDisagreeButtonTextColor = '#003C8D'; 1663 var cookieConsentDisagreeButtonBackgroundColor = '#fff'; 1664cookieConsentMessage = "<div class=\"Header\">Einsatz von Cookies</div><div class=\"Message\">Wir würden gerne Cookies verwenden, um die Präferenzen unserer Nutzer zu erkennen und diese Website optimal gestalten zu können. Detaillierte Informationen und wie Sie Ihre Einwilligung jederzeit widerrufen können, erhalten Sie in unserer</div>";
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.