PageSourceSearch

https://takismarakakis.com/catalog/view/javascript/isenselabs_gdpr/journal3/cookieconsent.min.js?v=14218c54

js takismarakakis.com collected 2026-10-06 12:53:25 UTC 57,495 bytes, 1,547 lines download raw bytes

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

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.