PageSourceSearch

https://www.en06.fr/wa_js/cookieconsent.js

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