jumbo-jekyll-theme 1.4.5.1 → 1.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -1
  3. data/_data/authors.yml +7 -7
  4. data/assets/js/app/search.js +1 -1
  5. data/assets/js/package-blog.js +3 -3
  6. data/assets/js/package-developer-services.js +4 -4
  7. data/assets/js/package-extended.js +4 -4
  8. data/assets/js/package-home.js +4 -4
  9. data/assets/js/package-main.js +3 -3
  10. data/assets/js/package-search.js +3 -3
  11. data/assets/js/vendor/cookieconsent.js +1504 -0
  12. data/assets/js/vendor/jquery.js +10364 -0
  13. data/assets/js/vendor/{jquery.rss.min.js → jquery.rss.js} +1 -1
  14. data/assets/js/vendor/jquery.validate.js +1601 -0
  15. data/assets/js/vendor/lazysizes.js +698 -0
  16. data/assets/js/vendor/owl.carousel.js +3475 -0
  17. data/assets/js/vendor/picturefill.js +1471 -0
  18. metadata +11 -27
  19. data/_sass/custom.scss +0 -0
  20. data/assets/images/96boards-Logo.svg +0 -1
  21. data/assets/images/Linaro-Sprinkle.svg +0 -13
  22. data/assets/images/bof-devicetree.jpg +0 -0
  23. data/assets/images/css3.png +0 -0
  24. data/assets/images/device-tree-vertical-logo.png +0 -0
  25. data/assets/images/html5.png +0 -0
  26. data/assets/images/jekyll.svg +0 -1
  27. data/assets/images/js.jpeg +0 -0
  28. data/assets/js/app/developer-services.js +0 -26
  29. data/assets/js/app/mixitup.js +0 -23
  30. data/assets/js/app/openhours-timer.js +0 -23
  31. data/assets/js/app/rss.js +0 -96
  32. data/assets/js/app/sticky-tab-bar-concept.js +0 -74
  33. data/assets/js/vendor/cookieconsent.min.js +0 -8
  34. data/assets/js/vendor/filtrify.js +0 -11
  35. data/assets/js/vendor/flipclock.min.js +0 -2
  36. data/assets/js/vendor/jquery.min.js +0 -4
  37. data/assets/js/vendor/jquery.validate.min.js +0 -4
  38. data/assets/js/vendor/lazysizes.min.js +0 -2
  39. data/assets/js/vendor/loadCSS.min.js +0 -2
  40. data/assets/js/vendor/moment.min.js +0 -1
  41. data/assets/js/vendor/owl.carousel.min.js +0 -103
  42. data/assets/js/vendor/picturefill.min.js +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a3d427d780338fc6dbee2d823274d8634776ab22
4
- data.tar.gz: 040e5ae7067d027ff803d977196e61c002695be1
3
+ metadata.gz: 339e7dc076e245f36d2f225a99ae768516424f59
4
+ data.tar.gz: '04353188df885b6c72588d59c723af4498490eb1'
5
5
  SHA512:
6
- metadata.gz: b74c20d5f921176a9eeae2c09469d00430f4861122814863fb3282d012679765489e237e2d75382b8c8741e9de53e8527cce49cfb9d6a1f6016211b717e9ed2f
7
- data.tar.gz: 801310c8e2a44ab278453ff1cbbe679e0a679d75dcbbb390be85bc588b69665a780919bd743a12c05c6523c87cf9fd29202be1dfe8c98275fcd1344a7075ce8d
6
+ metadata.gz: adf9da07694434c0707b8f97a4081fa219b16e68de83860fa8b928d982621f136a4a66e128f6ed474bd5fe5b0884f75bcbe70590a1823bd1bd4f44d998a6fe7f
7
+ data.tar.gz: 272e62b5be1186fcaac6d9df29ca813af24bd6377c635a51527753f458f78fbf013119c3f2917c862f940e5573cb4a91014f8e0311fbfa307d5c3c9814a1af9a
data/README.md CHANGED
@@ -16,4 +16,11 @@ Some of the features this theme offers:
16
16
 
17
17
  * Lazy loading of content.
18
18
  * Generated breadcrumb
19
- * Easy navigation / footer management using YAML Data files.
19
+ * Easy navigation / footer management using YAML Data files.
20
+
21
+ # The Docs
22
+ The documentation for this theme is currently available through the Collaborate space. I will be adding to readthedocs/github in due course.
23
+
24
+ # Feature Requests / Bug Fixes
25
+
26
+ If anyone that uses the theme has any useful bug fixes / feature requests that may be of interest then please feel free to fork/submit a PR with your fixes/features.
@@ -1,7 +1,7 @@
1
- entries:
2
- - shortname: kylekirkby
3
- avatar: https://en.gravatar.com/userimage/54786027/fc975171b4e83cd1c19b20697a1d4329.jpeg
4
- first_name: Kyle
5
- second_name: Kirkby
6
- website: https://kylekirkby.co.uk
7
- github: https://github.com/kylekirkby
1
+ # entries:
2
+ # - shortname: kylekirkby
3
+ # avatar: https://en.gravatar.com/userimage/54786027/fc975171b4e83cd1c19b20697a1d4329.jpeg
4
+ # first_name: Kyle
5
+ # second_name: Kirkby
6
+ # website: https://kylekirkby.co.uk
7
+ # github: https://github.com/kylekirkby
@@ -37,4 +37,4 @@ function getAllUrlParams(url) {
37
37
 
38
38
  var searchQuery = getAllUrlParams().s;
39
39
 
40
- $('#searchIframe').attr('src', "https://search.linaro.org/search/?fields.label=Linaro&q=" + searchQuery);
40
+ $('#searchIframe').attr('src', "https://search.linaro.org/search/&q=" + searchQuery);
@@ -1,7 +1,7 @@
1
- //= require vendor/jquery.min
1
+ //= require vendor/jquery
2
2
  //= require vendor/bootstrap
3
- //= require vendor/cookieconsent.min
4
- //= require vendor/lazysizes.min
3
+ //= require vendor/cookieconsent
4
+ //= require vendor/lazysizes
5
5
 
6
6
 
7
7
  //= require vendor/lightbox
@@ -1,11 +1,11 @@
1
- //= require vendor/jquery.min
1
+ //= require vendor/jquery
2
2
  //= require vendor/bootstrap
3
- //= require vendor/cookieconsent.min
4
- //= require vendor/lazysizes.min
3
+ //= require vendor/cookieconsent
4
+ //= require vendor/lazysizes
5
5
 
6
6
  //= require app/main
7
7
  //= require app/tables
8
8
  //= require vendor/mc-validate
9
- //= require vendor/jquery.validate.min
9
+ //= require vendor/jquery.validate
10
10
  //= require app/developer-services
11
11
  //= require app/custom
@@ -1,8 +1,8 @@
1
- //= require vendor/jquery.min
1
+ //= require vendor/jquery
2
2
  //= require vendor/bootstrap
3
- //= require vendor/cookieconsent.min
4
- //= require vendor/owl.carousel.min
5
- //= require vendor/lazysizes.min
3
+ //= require vendor/cookieconsent
4
+ //= require vendor/owl.carousel
5
+ //= require vendor/lazysizes
6
6
  //= require vendor/ls.unveilhooks
7
7
  //= require vendor/lightbox
8
8
  //= require app/sticky-tab-bar
@@ -1,8 +1,8 @@
1
- //= require vendor/jquery.min
1
+ //= require vendor/jquery
2
2
  //= require vendor/bootstrap
3
- //= require vendor/cookieconsent.min
4
- //= require vendor/lazysizes.min
5
- //= require vendor/owl.carousel.min
3
+ //= require vendor/cookieconsent
4
+ //= require vendor/lazysizes
5
+ //= require vendor/owl.carousel
6
6
 
7
7
  //= require app/main
8
8
  //= require app/sticky-tab-bar
@@ -1,7 +1,7 @@
1
- //= require vendor/jquery.min
1
+ //= require vendor/jquery
2
2
  //= require vendor/bootstrap
3
- //= require vendor/cookieconsent.min
4
- //= require vendor/lazysizes.min
3
+ //= require vendor/cookieconsent
4
+ //= require vendor/lazysizes
5
5
 
6
6
  //= require app/main
7
7
  //= require app/scroll-to-anchors
@@ -1,7 +1,7 @@
1
- //= require vendor/jquery.min
1
+ //= require vendor/jquery
2
2
  //= require vendor/bootstrap
3
- //= require vendor/cookieconsent.min
4
- //= require vendor/lazysizes.min
3
+ //= require vendor/cookieconsent
4
+ //= require vendor/lazysizes
5
5
 
6
6
  //= require app/main
7
7
  //= require app/search
@@ -0,0 +1,1504 @@
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="_blank">{{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="_blank">{{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 false
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'] = [
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', 'SI', '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 || {}));
1496
+
1497
+
1498
+ window.addEventListener("load", function(){
1499
+ window.cookieconsent.initialise({
1500
+ "palette": {
1501
+ },
1502
+ "theme": "classic",
1503
+ "position": "bottom-right"
1504
+ })});