PageSourceSearch

https://www.lacers.org/sites/main/files/js/js_7b5c2a254a445a3d216da1b37c0d0e85.js

js lacers.org collected 2026-10-03 23:43:17 UTC 113,985 bytes, 3,067 lines download raw bytes

1/* $Id: lightbox.js,v 1.5.2.6.2.136 2010/09/24 08:39:40 snpower Exp $ */
2
3/**
4 * jQuery Lightbox
5 * @author
6 *   Stella Power, <http://drupal.org/user/66894>
7 *
8 * Based on Lightbox v2.03.3 by Lokesh Dhakar
9 * <http://www.huddletogether.com/projects/lightbox2/>
10 * Also partially based on the jQuery Lightbox by Warren Krewenki
11 *   <http://warren.mesozen.com>
12 *
13 * Permission has been granted to Mark Ashmead & other Drupal Lightbox2 module
14 * maintainers to distribute this file via Drupal.org
15 * Under GPL license.
16 *
17 * Slideshow, iframe and video functionality added by Stella Power.
18 */
19
20var Lightbox = {
21  auto_modal : false,
22  overlayOpacity : 0.8, // Controls transparency of shadow overlay.
23  overlayColor : '000', // Controls colour of shadow overlay.
24  disableCloseClick : true,
25  // Controls the order of the lightbox resizing animation sequence.
26  resizeSequence: 0, // 0: simultaneous, 1: width then height, 2: height then width.
27  resizeSpeed: 'normal', // Controls the speed of the lightbox resizing animation.
28  fadeInSpeed: 'normal', // Controls the speed of the image appearance.
29  slideDownSpeed: 'slow', // Controls the speed of the image details appearance.
30  minWidth: 240,
31  borderSize : 10,
32  boxColor : 'fff',
33  fontColor : '000',
34  topPosition : '',
35  infoHeight: 20,
36  alternative_layout : false,
37  imageArray : [],
38  imageNum : null,
39  total : 0,
40  activeImage : null,
41  inprogress : false,
42  disableResize : false,
43  disableZoom : false,
44  isZoomedIn : false,
45  rtl : false,
46  loopItems : false,
47  keysClose : ['c', 'x', 27],
48  keysPrevious : ['p', 37],
49  keysNext : ['n', 39],
50  keysZoom : ['z'],
51  keysPlayPause : [32],
52
53  // Slideshow options.
54  slideInterval : 5000, // In milliseconds.
55  showPlayPause : true,
56  autoStart : true,
57  autoExit : true,
58  pauseOnNextClick : false, // True to pause the slideshow when the "Next" button is clicked.
59  pauseOnPrevClick : true, // True to pause the slideshow when the "Prev" button is clicked.
60  slideIdArray : [],
61  slideIdCount : 0,
62  isSlideshow : false,
63  isPaused : false,
64  loopSlides : false,
65
66  // Iframe options.
67  isLightframe : false,
68  iframe_width : 600,
69  iframe_height : 400,
70  iframe_border : 1,
71
72  // Video and modal options.
73  enableVideo : false,
74  flvPlayer : '/flvplayer.swf',
75  flvFlashvars : '',
76  isModal : false,
77  isVideo : false,
78  videoId : false,
79  modalWidth : 400,
80  modalHeight : 400,
81  modalHTML : null,
82
83
84  // initialize()
85  // Constructor runs on completion of the DOM loading.
86  // The function inserts html at the bottom of the page which is used
87  // to display the shadow overlay and the image container.
88  initialize: function() {
89
90    var s = Drupal.settings.lightbox2;
91    Lightbox.overlayOpacity = s.overlay_opacity;
92    Lightbox.overlayColor = s.overlay_color;
93    Lightbox.disableCloseClick = s.disable_close_click;
94    Lightbox.resizeSequence = s.resize_sequence;
95    Lightbox.resizeSpeed = s.resize_speed;
96    Lightbox.fadeInSpeed = s.fade_in_speed;
97    Lightbox.slideDownSpeed = s.slide_down_speed;
98    Lightbox.borderSize = s.border_size;
99    Lightbox.boxColor = s.box_color;
100    Lightbox.fontColor = s.font_color;
101    Lightbox.topPosition = s.top_position;
102    Lightbox.rtl = s.rtl;
103    Lightbox.loopItems = s.loop_items;
104    Lightbox.keysClose = s.keys_close.split(" ");
105    Lightbox.keysPrevious = s.keys_previous.split(" ");
106    Lightbox.keysNext = s.keys_next.split(" ");
107    Lightbox.keysZoom = s.keys_zoom.split(" ");
108    Lightbox.keysPlayPause = s.keys_play_pause.split(" ");
109    Lightbox.disableResize = s.disable_resize;
110    Lightbox.disableZoom = s.disable_zoom;
111    Lightbox.slideInterval = s.slideshow_interval;
112    Lightbox.showPlayPause = s.show_play_pause;
113    Lightbox.showCaption = s.show_caption;
114    Lightbox.autoStart = s.slideshow_automatic_start;
115    Lightbox.autoExit = s.slideshow_automatic_exit;
116    Lightbox.pauseOnNextClick = s.pause_on_next_click;
117    Lightbox.pauseOnPrevClick = s.pause_on_previous_click;
118    Lightbox.loopSlides = s.loop_slides;
119    Lightbox.alternative_layout = s.use_alt_layout;
120    Lightbox.iframe_width = s.iframe_width;
121    Lightbox.iframe_height = s.iframe_height;
122    Lightbox.iframe_border = s.iframe_border;
123    Lightbox.enableVideo = s.enable_video;
124    if (s.enable_video) {
125      Lightbox.flvPlayer = s.flvPlayer;
126      Lightbox.flvFlashvars = s.flvFlashvars;
127    }
128
129    // Make the lightbox divs.
130    var layout_class = (s.use_alt_layout ? 'lightbox2-alt-layout' : 'lightbox2-orig-layout');
131    var output = '<div id="lightbox2-overlay" style="display: none;"></div>\
132      <div id="lightbox" style="display: none;" class="' + layout_class + '">\
133        <div id="outerImageContainer"></div>\
134        <div id="imageDataContainer" class="clearfix">\
135          <div id="imageData"></div>\
136        </div>\
137      </div>';
138    var loading = '<div id="loading"><a href="#" id="loadingLink"></a></div>';
139    var modal = '<div id="modalContainer" style="display: none;"></div>';
140    var frame = '<div id="frameContainer" style="display: none;"></div>';
141    var imageContainer = '<div id="imageContainer" style="display: none;"></div>';
142    var details = '<div id="imageDetails"></div>';
143    var bottomNav = '<div id="bottomNav"></div>';
144    var image = '<img id="lightboxImage" alt="" />';
145    var hoverNav = '<div id="hoverNav"><a id="prevLink" href="#"></a><a id="nextLink" href="#"></a></div>';
146    var frameNav = '<div id="frameHoverNav"><a id="framePrevLink" href="#"></a><a id="frameNextLink" href="#"></a></div>';
147    var hoverNav = '<div id="hoverNav"><a id="prevLink" title="' + Drupal.t('Previous') + '" href="#"></a><a id="nextLink" title="' + Drupal.t('Next') + '" href="#"></a></div>';
148    var frameNav = '<div id="frameHoverNav"><a id="framePrevLink" title="' + Drupal.t('Previous') + '" href="#"></a><a id="frameNextLink" title="' + Drupal.t('Next') + '" href="#"></a></div>';
149    var caption = '<span id="caption"></span>';
150    var numberDisplay = '<span id="numberDisplay"></span>';
151    var close = '<a id="bottomNavClose" title="' + Drupal.t('Close') + '" href="#"></a>';
152    var zoom = '<a id="bottomNavZoom" href="#"></a>';
153    var zoomOut = '<a id="bottomNavZoomOut" href="#"></a>';
154    var pause = '<a id="lightshowPause" title="' + Drupal.t('Pause Slideshow') + '" href="#" style="display: none;"></a>';
155    var play = '<a id="lightshowPlay" title="' + Drupal.t('Play Slideshow') + '" href="#" style="display: none;"></a>';
156
157    $("body").append(output);
158    $('#outerImageContainer').append(modal + frame + imageContainer + loading);
159    if (!s.use_alt_layout) {
160      $('#imageContainer').append(image + hoverNav);
161      $('#imageData').append(details + bottomNav);
162      $('#imageDetails').append(caption + numberDisplay);
163      $('#bottomNav').append(frameNav + close + zoom + zoomOut + pause + play);
164    }
165    else {
166      $('#outerImageContainer').append(bottomNav);
167      $('#imageContainer').append(image);
168      $('#bottomNav').append(close + zoom + zoomOut);
169      $('#imageData').append(hoverNav + details);
170      $('#imageDetails').append(caption + numberDisplay + pause + play);
171    }
172
173    // Setup onclick handlers.
174    if (Lightbox.disableCloseClick) {
175      $('#lightbox2-overlay').click(function() { Lightbox.end(); return false; } ).hide();
176    }
177    $('#loadingLink, #bottomNavClose').click(function() { Lightbox.end('forceClose'); return false; } );
178    $('#prevLink, #framePrevLink').click(function() { Lightbox.changeData(Lightbox.activeImage - 1); return false; } );
179    $('#nextLink, #frameNextLink').click(function() { Lightbox.changeData(Lightbox.activeImage + 1); return false; } );
180    $('#bottomNavZoom').click(function() { Lightbox.changeData(Lightbox.activeImage, true); return false; } );
181    $('#bottomNavZoomOut').click(function() { Lightbox.changeData(Lightbox.activeImage, false); return false; } );
182    $('#lightshowPause').click(function() { Lightbox.togglePlayPause("lightshowPause", "lightshowPlay"); return false; } );
183    $('#lightshowPlay').click(function() { Lightbox.togglePlayPause("lightshowPlay", "lightshowPause"); return false; } );
184
185    // Fix positioning.
186    $('#prevLink, #nextLink, #framePrevLink, #frameNextLink').css({ 'paddingTop': Lightbox.borderSize + 'px'});
187    $('#imageContainer, #frameContainer, #modalContainer').css({ 'padding': Lightbox.borderSize + 'px'});
188    $('#outerImageContainer, #imageDataContainer, #bottomNavClose').css({'backgroundColor': '#' + Lightbox.boxColor, 'color': '#'+Lightbox.fontColor});
189    if (Lightbox.alternative_layout) {
190      $('#bottomNavZoom, #bottomNavZoomOut').css({'bottom': Lightbox.borderSize + 'px', 'right': Lightbox.borderSize + 'px'});
191    }
192    else if (Lightbox.rtl == 1 && $.browser.msie) {
193      $('#bottomNavZoom, #bottomNavZoomOut').css({'left': '0px'});
194    }
195
196    // Force navigation links to always be displayed
197    if (s.force_show_nav) {
198      $('#prevLink, #nextLink').addClass("force_show_nav");
199    }
200
201  },
202
203  // initList()
204  // Loops through anchor tags looking for 'lightbox', 'lightshow' and
205  // 'lightframe', etc, references and applies onclick events to appropriate
206  // links. You can rerun after dynamically adding images w/ajax.
207  initList : function(context) {
208
209    if (context == undefined || context == null) {
210      context = document;
211    }
212
213    // Attach lightbox to any links with rel 'lightbox', 'lightshow' or
214    // 'lightframe', etc.
215    $("a[rel^='lightbox']:not(.lightbox-processed), area[rel^='lightbox']:not(.lightbox-processed)", context).addClass('lightbox-processed').click(function(e) {
216      if (Lightbox.disableCloseClick) {
217        $('#lightbox').unbind('click');
218        $('#lightbox').click(function() { Lightbox.end('forceClose'); } );
219      }
220      Lightbox.start(this, false, false, false, false);
221      if (e.preventDefault) { e.preventDefault(); }
222      return false;
223    });
224    $("a[rel^='lightshow']:not(.lightbox-processed), area[rel^='lightshow']:not(.lightbox-processed)", context).addClass('lightbox-processed').click(function(e) {
225      if (Lightbox.disableCloseClick) {
226        $('#lightbox').unbind('click');
227        $('#lightbox').click(function() { Lightbox.end('forceClose'); } );
228      }
229      Lightbox.start(this, true, false, false, false);
230      if (e.preventDefault) { e.preventDefault(); }
231      return false;
232    });
233    $("a[rel^='lightframe']:not(.lightbox-processed), area[rel^='lightframe']:not(.lightbox-processed)", context).addClass('lightbox-processed').click(function(e) {
234      if (Lightbox.disableCloseClick) {
235        $('#lightbox').unbind('click');
236        $('#lightbox').click(function() { Lightbox.end('forceClose'); } );
237      }
238      Lightbox.start(this, false, true, false, false);
239      if (e.preventDefault) { e.preventDefault(); }
240      return false;
241    });
242    if (Lightbox.enableVideo) {
243      $("a[rel^='lightvideo']:not(.lightbox-processed), area[rel^='lightvideo']:not(.lightbox-processed)", context).addClass('lightbox-processed').click(function(e) {
244        if (Lightbox.disableCloseClick) {
245          $('#lightbox').unbind('click');
246          $('#lightbox').click(function() { Lightbox.end('forceClose'); } );
247        }
248        Lightbox.start(this, false, false, true, false);
249        if (e.preventDefault) { e.preventDefault(); }
250        return false;
251      });
252    }
253    $("a[rel^='lightmodal']:not(.lightbox-processed), area[rel^='lightmodal']:not(.lightbox-processed)", context).addClass('lightbox-processed').click(function(e) {
254      $('#lightbox').unbind('click');
255      // Add classes from the link to the lightbox div - don't include lightbox-processed
256      $('#lightbox').addClass($(this).attr('class'));
257      $('#lightbox').removeClass('lightbox-processed');
258      Lightbox.start(this, false, false, false, true);
259      if (e.preventDefault) { e.preventDefault(); }
260      return false;
261    });
262    $("#lightboxAutoModal:not(.lightbox-processed)", context).addClass('lightbox-processed').click(function(e) {
263      Lightbox.auto_modal = true;
264      $('#lightbox').unbind('click');
265      Lightbox.start(this, false, false, false, true);
266      if (e.preventDefault) { e.preventDefault(); }
267      return false;
268    });
269  },
270
271  // start()
272  // Display overlay and lightbox. If image is part of a set, add siblings to
273  // imageArray.
274  start: function(imageLink, slideshow, lightframe, lightvideo, lightmodal) {
275
276    Lightbox.isPaused = !Lightbox.autoStart;
277
278    // Replaces hideSelectBoxes() and hideFlash() calls in original lightbox2.
279    Lightbox.toggleSelectsFlash('hide');
280
281    // Stretch overlay to fill page and fade in.
282    var arrayPageSize = Lightbox.getPageSize();
283    $("#lightbox2-overlay").hide().css({
284      'width': '100%',
285      'zIndex': '10090',
286      'height': arrayPageSize[1] + 'px',
287      'backgroundColor' : '#' + Lightbox.overlayColor
288    });
289    // Detect OS X FF2 opacity + flash issue.
290    if (lightvideo && this.detectMacFF2()) {
291      $("#lightbox2-overlay").removeClass("overlay_default");
292      $("#lightbox2-overlay").addClass("overlay_macff2");
293      $("#lightbox2-overlay").css({'opacity' : null});
294    }
295    else {
296      $("#lightbox2-overlay").removeClass("overlay_macff2");
297      $("#lightbox2-overlay").addClass("overlay_default");
298      $("#lightbox2-overlay").css({'opacity' : Lightbox.overlayOpacity});
299    }
300    $("#lightbox2-overlay").fadeIn(Lightbox.fadeInSpeed);
301
302
303    Lightbox.isSlideshow = slideshow;
304    Lightbox.isLightframe = lightframe;
305    Lightbox.isVideo = lightvideo;
306    Lightbox.isModal = lightmodal;
307    Lightbox.imageArray = [];
308    Lightbox.imageNum = 0;
309
310    var anchors = $(imageLink.tagName);
311    var anchor = null;
312    var rel_parts = Lightbox.parseRel(imageLink);
313    var rel = rel_parts["rel"];
314    var rel_group = rel_parts["group"];
315    var title = (rel_parts["title"] ? rel_parts["title"] : imageLink.title);
316    var rel_style = null;
317    var i = 0;
318
319    if (rel_parts["flashvars"]) {
320      Lightbox.flvFlashvars = Lightbox.flvFlashvars + '&' + rel_parts["flashvars"];
321    }
322
323    // Set the title for image alternative text.
324    var alt = imageLink.title;
325    if (!alt) {
326      var img = $(imageLink).find("img");
327      if (img && $(img).attr("alt")) {
328        alt = $(img).attr("alt");
329      }
330      else {
331        alt = title;
332      }
333    }
334
335    if ($(imageLink).attr('id') == 'lightboxAutoModal') {
336      rel_style = rel_parts["style"];
337      Lightbox.imageArray.push(['#lightboxAutoModal > *', title, alt, rel_style, 1]);
338    }
339    else {
340      // Handle lightbox images with no grouping.
341      if ((rel == 'lightbox' || rel == 'lightshow') && !rel_group) {
342        Lightbox.imageArray.push([imageLink.href, title, alt]);
343      }
344
345      // Handle other items with no grouping.
346      else if (!rel_group) {
347        rel_style = rel_parts["style"];
348        Lightbox.imageArray.push([imageLink.href, title, alt, rel_style]);
349      }
350
351      // Handle grouped items.
352      else {
353
354        // Loop through anchors and add them to imageArray.
355        for (i = 0; i < anchors.length; i++) {
356          anchor = anchors[i];
357          if (anchor.href && typeof(anchor.href) == "string" && $(anchor).attr('rel')) {
358            var rel_data = Lightbox.parseRel(anchor);
359            var anchor_title = (rel_data["title"] ? rel_data["title"] : anchor.title);
360            img_alt = anchor.title;
361            if (!img_alt) {
362              var anchor_img = $(anchor).find("img");
363              if (anchor_img && $(anchor_img).attr("alt")) {
364                img_alt = $(anchor_img).attr("alt");
365              }
366              else {
367                img_alt = title;
368              }
369            }
370            if (rel_data["rel"] == rel) {
371              if (rel_data["group"] == rel_group) {
372                if (Lightbox.isLightframe || Lightbox.isModal || Lightbox.isVideo) {
373                  rel_style = rel_data["style"];
374                }
375                Lightbox.imageArray.push([anchor.href, anchor_title, img_alt, rel_style]);
376              }
377            }
378          }
379        }
380
381        // Remove duplicates.
382        for (i = 0; i < Lightbox.imageArray.length; i++) {
383          for (j = Lightbox.imageArray.length-1; j > i; j--) {
384            if (Lightbox.imageArray[i][0] == Lightbox.imageArray[j][0]) {
385              Lightbox.imageArray.splice(j,1);
386            }
387          }
388        }
389        while (Lightbox.imageArray[Lightbox.imageNum][0] != imageLink.href) {
390          Lightbox.imageNum++;
391        }
392      }
393    }
394
395    if (Lightbox.isSlideshow && Lightbox.showPlayPause && Lightbox.isPaused) {
396      $('#lightshowPlay').show();
397      $('#lightshowPause').hide();
398    }
399
400    // Calculate top and left offset for the lightbox.
401    var arrayPageScroll = Lightbox.getPageScroll();
402    var lightboxTop = arrayPageScroll[1] + (Lightbox.topPosition == '' ? (arrayPageSize[3] / 10) : Lightbox.topPosition) * 1;
403    var lightboxLeft = arrayPageScroll[0];
404    $('#frameContainer, #modalContainer, #lightboxImage').hide();
405    $('#hoverNav, #prevLink, #nextLink, #frameHoverNav, #framePrevLink, #frameNextLink').hide();
406    $('#imageDataContainer, #numberDisplay, #bottomNavZoom, #bottomNavZoomOut').hide();
407    $('#outerImageContainer').css({'width': '250px', 'height': '250px'});
408    $('#lightbox').css({
409      'zIndex': '10500',
410      'top': lightboxTop + 'px',
411      'left': lightboxLeft + 'px'
412    }).show();
413
414    Lightbox.total = Lightbox.imageArray.length;
415    Lightbox.changeData(Lightbox.imageNum);
416  },
417
418  // changeData()
419  // Hide most elements and preload image in preparation for resizing image
420  // container.
421  changeData: function(imageNum, zoomIn) {
422
423    if (Lightbox.inprogress === false) {
424      if (Lightbox.total > 1 && ((Lightbox.isSlideshow && Lightbox.loopSlides) || (!Lightbox.isSlideshow && Lightbox.loopItems))) {
425        if (imageNum >= Lightbox.total) imageNum = 0;
426        if (imageNum < 0) imageNum = Lightbox.total - 1;
427      }
428
429      if (Lightbox.isSlideshow) {
430        for (var i = 0; i < Lightbox.slideIdCount; i++) {
431          window.clearTimeout(Lightbox.slideIdArray[i]);
432        }
433      }
434      Lightbox.inprogress = true;
435      Lightbox.activeImage = imageNum;
436
437      if (Lightbox.disableResize && !Lightbox.isSlideshow) {
438        zoomIn = true;
439      }
440      Lightbox.isZoomedIn = zoomIn;
441
442
443      // Hide elements during transition.
444      $('#loading').css({'zIndex': '10500'}).show();
445      if (!Lightbox.alternative_layout) {
446        $('#imageContainer').hide();
447      }
448      $('#frameContainer, #modalContainer, #lightboxImage').hide();
449      $('#hoverNav, #prevLink, #nextLink, #frameHoverNav, #framePrevLink, #frameNextLink').hide();
450      $('#imageDataContainer, #numberDisplay, #bottomNavZoom, #bottomNavZoomOut').hide();
451
452      // Preload image content, but not iframe pages.
453      if (!Lightbox.isLightframe && !Lightbox.isVideo && !Lightbox.isModal) {
454        $("#lightbox #imageDataContainer").removeClass('lightbox2-alt-layout-data');
455        imgPreloader = new Image();
456        imgPreloader.onerror = function() { Lightbox.imgNodeLoadingError(this); };
457
458        imgPreloader.onload = function() {
459          var photo = document.getElementById('lightboxImage');
460          photo.src = Lightbox.imageArray[Lightbox.activeImage][0];
461          photo.alt = Lightbox.imageArray[Lightbox.activeImage][2];
462
463          var imageWidth = imgPreloader.width;
464          var imageHeight = imgPreloader.height;
465
466          // Resize code.
467          var arrayPageSize = Lightbox.getPageSize();
468          var targ = { w:arrayPageSize[2] - (Lightbox.borderSize * 2), h:arrayPageSize[3] - (Lightbox.borderSize * 6) - (Lightbox.infoHeight * 4) - (arrayPageSize[3] / 10) };
469          var orig = { w:imgPreloader.width, h:imgPreloader.height };
470
471          // Image is very large, so show a smaller version of the larger image
472          // with zoom button.
473          if (zoomIn !== true) {
474            var ratio = 1.0; // Shrink image with the same aspect.
475            $('#bottomNavZoomOut, #bottomNavZoom').hide();
476            if ((orig.w >= targ.w || orig.h >= targ.h) && orig.h && orig.w) {
477              ratio = ((targ.w / orig.w) < (targ.h / orig.h)) ? targ.w / orig.w : targ.h / orig.h;
478              if (!Lightbox.disableZoom && !Lightbox.isSlideshow) {
479                $('#bottomNavZoom').css({'zIndex': '10500'}).show();
480              }
481            }
482
483            imageWidth  = Math.floor(orig.w * ratio);
484            imageHeight = Math.floor(orig.h * ratio);
485          }
486
487          else {
488            $('#bottomNavZoom').hide();
489            // Only display zoom out button if the image is zoomed in already.
490            if ((orig.w >= targ.w || orig.h >= targ.h) && orig.h && orig.w) {
491              // Only display zoom out button if not a slideshow and if the
492              // buttons aren't disabled.
493              if (!Lightbox.disableResize && Lightbox.isSlideshow === false && !Lightbox.disableZoom) {
494                $('#bottomNavZoomOut').css({'zIndex': '10500'}).show();
495              }
496            }
497          }
498
499          photo.style.width = (imageWidth) + 'px';
500          photo.style.height = (imageHeight) + 'px';
501          Lightbox.resizeContainer(imageWidth, imageHeight);
502
503          // Clear onLoad, IE behaves irratically with animated gifs otherwise.
504          imgPreloader.onload = function() {};
505        };
506
507        imgPreloader.src = Lightbox.imageArray[Lightbox.activeImage][0];
508        imgPreloader.alt = Lightbox.imageArray[Lightbox.activeImage][2];
509      }
510
511      // Set up frame size, etc.
512      else if (Lightbox.isLightframe) {
513        $("#lightbox #imageDataContainer").addClass('lightbox2-alt-layout-data');
514        var src = Lightbox.imageArray[Lightbox.activeImage][0];
515        // DD Override to allow fullscren videos.
516        $('#frameContainer').html('<iframe id="lightboxFrame" style="display: none;" src="' + src +'" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowfullscreen></iframe>');
517
518        // Enable swf support in Gecko browsers.
519        if ($.browser.mozilla && src.indexOf('.swf') != -1) {
520          setTimeout(function () {
521            document.getElementById("lightboxFrame").src = Lightbox.imageArray[Lightbox.activeImage][0];
522          }, 1000);
523        }
524
525        if (!Lightbox.iframe_border) {
526          $('#lightboxFrame').css({'border': 'none'});
527          $('#lightboxFrame').attr('frameborder', '0');
528        }
529        var iframe = document.getElementById('lightboxFrame');
530        var iframeStyles = Lightbox.imageArray[Lightbox.activeImage][3];
531        iframe = Lightbox.setStyles(iframe, iframeStyles);
532        Lightbox.resizeContainer(parseInt(iframe.width, 10), parseInt(iframe.height, 10));
533      }
534      else if (Lightbox.isVideo || Lightbox.isModal) {
535        $("#lightbox #imageDataContainer").addClass('lightbox2-alt-layout-data');
536        var container = document.getElementById('modalContainer');
537        var modalStyles = Lightbox.imageArray[Lightbox.activeImage][3];
538        container = Lightbox.setStyles(container, modalStyles);
539        if (Lightbox.isVideo) {
540          Lightbox.modalHeight =  parseInt(container.height, 10) - 10;
541          Lightbox.modalWidth =  parseInt(container.width, 10) - 10;
542          Lightvideo.startVideo(Lightbox.imageArray[Lightbox.activeImage][0]);
543        }
544        Lightbox.resizeContainer(parseInt(container.width, 10), parseInt(container.height, 10));
545      }
546    }
547  },
548
549  // imgNodeLoadingError()
550  imgNodeLoadingError: function(image) {
551    var s = Drupal.settings.lightbox2;
552    var original_image = Lightbox.imageArray[Lightbox.activeImage][0];
553    if (s.display_image_size !== "") {
554      original_image = original_image.replace(new RegExp("."+s.display_image_size), "");
555    }
556    Lightbox.imageArray[Lightbox.activeImage][0] = original_image;
557    image.onerror = function() { Lightbox.imgLoadingError(image); };
558    image.src = original_image;
559  },
560
561  // imgLoadingError()
562  imgLoadingError: function(image) {
563    var s = Drupal.settings.lightbox2;
564    Lightbox.imageArray[Lightbox.activeImage][0] = s.default_image;
565    image.src = s.default_image;
566  },
567
568  // resizeContainer()
569  resizeContainer: function(imgWidth, imgHeight) {
570
571    imgWidth = (imgWidth < Lightbox.minWidth ? Lightbox.minWidth : imgWidth);
572
573    this.widthCurrent = $('#outerImageContainer').width();
574    this.heightCurrent = $('#outerImageContainer').height();
575
576    var widthNew = (imgWidth  + (Lightbox.borderSize * 2));
577    var heightNew = (imgHeight  + (Lightbox.borderSize * 2));
578
579    // Scalars based on change from old to new.
580    this.xScale = ( widthNew / this.widthCurrent) * 100;
581    this.yScale = ( heightNew / this.heightCurrent) * 100;
582
583    // Calculate size difference between new and old image, and resize if
584    // necessary.
585    wDiff = this.widthCurrent - widthNew;
586    hDiff = this.heightCurrent - heightNew;
587
588    $('#modalContainer').css({'width': imgWidth, 'height': imgHeight});
589    // Detect animation sequence.
590    if (Lightbox.resizeSequence) {
591      var animate1 = {width: widthNew};
592      var animate2 = {height: heightNew};
593      if (Lightbox.resizeSequence == 2) {
594        animate1 = {height: heightNew};
595        animate2 = {width: widthNew};
596      }
597      $('#outerImageContainer').animate(animate1, Lightbox.resizeSpeed).animate(animate2, Lightbox.resizeSpeed, 'linear', function() { Lightbox.showData(); });
598    }
599    // Simultaneous.
600    else {
601      $('#outerImageContainer').animate({'width': widthNew, 'height': heightNew}, Lightbox.resizeSpeed, 'linear', function() { Lightbox.showData(); });
602    }
603
604    // If new and old image are same size and no scaling transition is necessary
605    // do a quick pause to prevent image flicker.
606    if ((hDiff === 0) && (wDiff === 0)) {
607      if ($.browser.msie) {
608        Lightbox.pause(250);
609      }
610      else {
611        Lightbox.pause(100);
612      }
613    }
614
615    var s = Drupal.settings.lightbox2;
616    if (!s.use_alt_layout) {
617      $('#prevLink, #nextLink').css({'height': imgHeight + 'px'});
618    }
619    $('#imageDataContainer').css({'width': widthNew + 'px'});
620  },
621
622  // showData()
623  // Display image and begin preloading neighbors.
624  showData: function() {
625    $('#loading').hide();
626
627    if (Lightbox.isLightframe || Lightbox.isVideo || Lightbox.isModal) {
628      Lightbox.updateDetails();
629      if (Lightbox.isLightframe) {
630        $('#frameContainer').show();
631        if ($.browser.safari || Lightbox.fadeInSpeed === 0) {
632          $('#lightboxFrame').css({'zIndex': '10500'}).show();
633        }
634        else {
635          $('#lightboxFrame').css({'zIndex': '10500'}).fadeIn(Lightbox.fadeInSpeed);
636        }
637      }
638      else {
639        if (Lightbox.isVideo) {
640          $("#modalContainer").html(Lightbox.modalHTML).click(function(){return false;}).css('zIndex', '10500').show();
641        }
642        else {
643          var src = unescape(Lightbox.imageArray[Lightbox.activeImage][0]);
644          if (Lightbox.imageArray[Lightbox.activeImage][4]) {
645            $(src).appendTo("#modalContainer");
646            $('#modalContainer').css({'zIndex': '10500'}).show();
647          }
648          else {
649            // Use a callback to show the new image, otherwise you get flicker.
650            $("#modalContainer").hide().load(src, function () {$('#modalContainer').css({'zIndex': '10500'}).show();});
651          }
652          $('#modalContainer').unbind('click');
653        }
654        // This might be needed in the Lightframe section above.
655        //$('#modalContainer').css({'zIndex': '10500'}).show();
656      }
657    }
658
659    // Handle display of image content.
660    else {
661      $('#imageContainer').show();
662      if ($.browser.safari || Lightbox.fadeInSpeed === 0) {
663        $('#lightboxImage').css({'zIndex': '10500'}).show();
664      }
665      else {
666        $('#lightboxImage').css({'zIndex': '10500'}).fadeIn(Lightbox.fadeInSpeed);
667      }
668      Lightbox.updateDetails();
669      this.preloadNeighborImages();
670    }
671    Lightbox.inprogress = false;
672
673    // Slideshow specific stuff.
674    if (Lightbox.isSlideshow) {
675      if (!Lightbox.loopSlides && Lightbox.activeImage == (Lightbox.total - 1)) {
676        if (Lightbox.autoExit) {
677          Lightbox.slideIdArray[Lightbox.slideIdCount++] = setTimeout(function () {Lightbox.end('slideshow');}, Lightbox.slideInterval);
678        }
679      }
680      else {
681        if (!Lightbox.isPaused && Lightbox.total > 1) {
682          Lightbox.slideIdArray[Lightbox.slideIdCount++] = setTimeout(function () {Lightbox.changeData(Lightbox.activeImage + 1);}, Lightbox.slideInterval);
683        }
684      }
685      if (Lightbox.showPlayPause && Lightbox.total > 1 && !Lightbox.isPaused) {
686        $('#lightshowPause').show();
687        $('#lightshowPlay').hide();
688      }
689      else if (Lightbox.showPlayPause && Lightbox.total > 1) {
690        $('#lightshowPause').hide();
691        $('#lightshowPlay').show();
692      }
693    }
694
695    // Adjust the page overlay size.
696    var arrayPageSize = Lightbox.getPageSize();
697    var arrayPageScroll = Lightbox.getPageScroll();
698    var pageHeight = arrayPageSize[1];
699    if (Lightbox.isZoomedIn && arrayPageSize[1] > arrayPageSize[3]) {
700      var lightboxTop = (Lightbox.topPosition == '' ? (arrayPageSize[3] / 10) : Lightbox.topPosition) * 1;
701      pageHeight = pageHeight + arrayPageScroll[1] + lightboxTop;
702    }
703    $('#lightbox2-overlay').css({'height': pageHeight + 'px', 'width': arrayPageSize[0] + 'px'});
704
705    // Gecko browsers (e.g. Firefox, SeaMonkey, etc) don't handle pdfs as
706    // expected.
707    if ($.browser.mozilla) {
708      if (Lightbox.imageArray[Lightbox.activeImage][0].indexOf(".pdf") != -1) {
709        setTimeout(function () {
710          document.getElementById("lightboxFrame").src = Lightbox.imageArray[Lightbox.activeImage][0];
711        }, 1000);
712      }
713    }
714  },
715
716  // updateDetails()
717  // Display caption, image number, and bottom nav.
718  updateDetails: function() {
719
720    $("#imageDataContainer").hide();
721
722    var s = Drupal.settings.lightbox2;
723
724    if (s.show_caption) {
725      var caption = Lightbox.filterXSS(Lightbox.imageArray[Lightbox.activeImage][1]);
726      if (!caption) caption = '';
727      $('#caption').html(caption).css({'zIndex': '10500'}).show();
728    }
729
730    // If image is part of set display 'Image x of x'.
731    var numberDisplay = null;
732    if (s.image_count && Lightbox.total > 1) {
733      var currentImage = Lightbox.activeImage + 1;
734      if (!Lightbox.isLightframe && !Lightbox.isModal && !Lightbox.isVideo) {
735        numberDisplay = s.image_count.replace(/\!current/, currentImage).replace(/\!total/, Lightbox.total);
736      }
737      else if (Lightbox.isVideo) {
738        numberDisplay = s.video_count.replace(/\!current/, currentImage).replace(/\!total/, Lightbox.total);
739      }
740      else {
741        numberDisplay = s.page_count.replace(/\!current/, currentImage).replace(/\!total/, Lightbox.total);
742      }
743      $('#numberDisplay').html(numberDisplay).css({'zIndex': '10500'}).show();
744    }
745    else {
746      $('#numberDisplay').hide();
747    }
748
749    $("#imageDataContainer").hide().slideDown(Lightbox.slideDownSpeed, function() {
750      $("#bottomNav").show();
751    });
752    if (Lightbox.rtl == 1) {
753      $("#bottomNav").css({'float': 'left'});
754    }
755    Lightbox.updateNav();
756  },
757
758  // updateNav()
759  // Display appropriate previous and next hover navigation.
760  updateNav: function() {
761
762    $('#hoverNav').css({'zIndex': '10500'}).show();
763    var prevLink = '#prevLink';
764    var nextLink = '#nextLink';
765
766    // Slideshow is separated as we need to show play / pause button.
767    if (Lightbox.isSlideshow) {
768      if ((Lightbox.total > 1 && Lightbox.loopSlides) || Lightbox.activeImage !== 0) {
769        $(prevLink).css({'zIndex': '10500'}).show().click(function() {
770          if (Lightbox.pauseOnPrevClick) {
771            Lightbox.togglePlayPause("lightshowPause", "lightshowPlay");
772          }
773          Lightbox.changeData(Lightbox.activeImage - 1); return false;
774        });
775      }
776      else {
777        $(prevLink).hide();
778      }
779
780      // If not last image in set, display next image button.
781      if ((Lightbox.total > 1 && Lightbox.loopSlides) || Lightbox.activeImage != (Lightbox.total - 1)) {
782        $(nextLink).css({'zIndex': '10500'}).show().click(function() {
783          if (Lightbox.pauseOnNextClick) {
784            Lightbox.togglePlayPause("lightshowPause", "lightshowPlay");
785          }
786          Lightbox.changeData(Lightbox.activeImage + 1); return false;
787        });
788      }
789      // Safari browsers need to have hide() called again.
790      else {
791        $(nextLink).hide();
792      }
793    }
794
795    // All other types of content.
796    else {
797
798      if ((Lightbox.isLightframe || Lightbox.isModal || Lightbox.isVideo) && !Lightbox.alternative_layout) {
799        $('#frameHoverNav').css({'zIndex': '10500'}).show();
800        $('#hoverNav').css({'zIndex': '10500'}).hide();
801        prevLink = '#framePrevLink';
802        nextLink = '#frameNextLink';
803      }
804
805      // If not first image in set, display prev image button.
806      if ((Lightbox.total > 1 && Lightbox.loopItems) || Lightbox.activeImage !== 0) {
807        // Unbind any other click handlers, otherwise this adds a new click handler
808        // each time the arrow is clicked.
809        $(prevLink).css({'zIndex': '10500'}).show().unbind().click(function() {
810          Lightbox.changeData(Lightbox.activeImage - 1); return false;
811        });
812      }
813      // Safari browsers need to have hide() called again.
814      else {
815        $(prevLink).hide();
816      }
817
818      // If not last image in set, display next image button.
819      if ((Lightbox.total > 1 && Lightbox.loopItems) || Lightbox.activeImage != (Lightbox.total - 1)) {
820        // Unbind any other click handlers, otherwise this adds a new click handler
821        // each time the arrow is clicked.
822        $(nextLink).css({'zIndex': '10500'}).show().unbind().click(function() {
823          Lightbox.changeData(Lightbox.activeImage + 1); return false;
824        });
825      }
826      // Safari browsers need to have hide() called again.
827      else {
828        $(nextLink).hide();
829      }
830    }
831
832    // Don't enable keyboard shortcuts so forms will work.
833    if (!Lightbox.isModal) {
834      this.enableKeyboardNav();
835    }
836  },
837
838
839  // enableKeyboardNav()
840  enableKeyboardNav: function() {
841    $(document).bind("keydown", this.keyboardAction);
842  },
843
844  // disableKeyboardNav()
845  disableKeyboardNav: function() {
846    $(document).unbind("keydown", this.keyboardAction);
847  },
848
849  // keyboardAction()
850  keyboardAction: function(e) {
851    if (e === null) { // IE.
852      keycode = event.keyCode;
853      escapeKey = 27;
854    }
855    else { // Mozilla.
856      keycode = e.keyCode;
857      escapeKey = e.DOM_VK_ESCAPE;
858    }
859
860    key = String.fromCharCode(keycode).toLowerCase();
861
862    // Close lightbox.
863    if (Lightbox.checkKey(Lightbox.keysClose, key, keycode)) {
864      Lightbox.end('forceClose');
865    }
866    // Display previous image (p, <-).
867    else if (Lightbox.checkKey(Lightbox.keysPrevious, key, keycode)) {
868      if ((Lightbox.total > 1 && ((Lightbox.isSlideshow && Lightbox.loopSlides) || (!Lightbox.isSlideshow && Lightbox.loopItems))) || Lightbox.activeImage !== 0) {
869        Lightbox.changeData(Lightbox.activeImage - 1);
870      }
871
872    }
873    // Display next image (n, ->).
874    else if (Lightbox.checkKey(Lightbox.keysNext, key, keycode)) {
875      if ((Lightbox.total > 1 && ((Lightbox.isSlideshow && Lightbox.loopSlides) || (!Lightbox.isSlideshow && Lightbox.loopItems))) || Lightbox.activeImage != (Lightbox.total - 1)) {
876        Lightbox.changeData(Lightbox.activeImage + 1);
877      }
878    }
879    // Zoom in.
880    else if (Lightbox.checkKey(Lightbox.keysZoom, key, keycode) && !Lightbox.disableResize && !Lightbox.disableZoom && !Lightbox.isSlideshow && !Lightbox.isLightframe) {
881      if (Lightbox.isZoomedIn) {
882        Lightbox.changeData(Lightbox.activeImage, false);
883      }
884      else if (!Lightbox.isZoomedIn) {
885        Lightbox.changeData(Lightbox.activeImage, true);
886      }
887      return false;
888    }
889    // Toggle play / pause (space).
890    else if (Lightbox.checkKey(Lightbox.keysPlayPause, key, keycode) && Lightbox.isSlideshow) {
891
892      if (Lightbox.isPaused) {
893        Lightbox.togglePlayPause("lightshowPlay", "lightshowPause");
894      }
895      else {
896        Lightbox.togglePlayPause("lightshowPause", "lightshowPlay");
897      }
898      return false;
899    }
900  },
901
902  preloadNeighborImages: function() {
903
904    if ((Lightbox.total - 1) > Lightbox.activeImage) {
905      preloadNextImage = new Image();
906      preloadNextImage.src = Lightbox.imageArray[Lightbox.activeImage + 1][0];
907    }
908    if (Lightbox.activeImage > 0) {
909      preloadPrevImage = new Image();
910      preloadPrevImage.src = Lightbox.imageArray[Lightbox.activeImage - 1][0];
911    }
912
913  },
914
915  end: function(caller) {
916    var closeClick = (caller == 'slideshow' ? false : true);
917    if (Lightbox.isSlideshow && Lightbox.isPaused && !closeClick) {
918      return;
919    }
920    // To prevent double clicks on navigation links.
921    if (Lightbox.inprogress === true && caller != 'forceClose') {
922      return;
923    }
924    Lightbox.disableKeyboardNav();
925    $('#lightbox').hide();
926    $("#lightbox2-overlay").fadeOut();
927    Lightbox.isPaused = true;
928    Lightbox.inprogress = false;
929    // Replaces calls to showSelectBoxes() and showFlash() in original
930    // lightbox2.
931    Lightbox.toggleSelectsFlash('visible');
932    if (Lightbox.isSlideshow) {
933      for (var i = 0; i < Lightbox.slideIdCount; i++) {
934        window.clearTimeout(Lightbox.slideIdArray[i]);
935      }
936      $('#lightshowPause, #lightshowPlay').hide();
937    }
938    else if (Lightbox.isLightframe) {
939      $('#frameContainer').empty().hide();
940    }
941    else if (Lightbox.isVideo || Lightbox.isModal) {
942      if (!Lightbox.auto_modal) {
943        $('#modalContainer').hide().html("");
944      }
945      Lightbox.auto_modal = false;
946    }
947  },
948
949
950  // getPageScroll()
951  // Returns array with x,y page scroll values.
952  // Core code from - quirksmode.com.
953  getPageScroll : function() {
954
955    var xScroll, yScroll;
956
957    if (self.pageYOffset || self.pageXOffset) {
958      yScroll = self.pageYOffset;
959      xScroll = self.pageXOffset;
960    }
961    else if (document.documentElement && (document.documentElement.scrollTop || document.documentElement.scrollLeft)) {  // Explorer 6 Strict.
962      yScroll = document.documentElement.scrollTop;
963      xScroll = document.documentElement.scrollLeft;
964    }
965    else if (document.body) {// All other Explorers.
966      yScroll = document.body.scrollTop;
967      xScroll = document.body.scrollLeft;
968    }
969
970    arrayPageScroll = [xScroll,yScroll];
971    return arrayPageScroll;
972  },
973
974  // getPageSize()
975  // Returns array with page width, height and window width, height.
976  // Core code from - quirksmode.com.
977  // Edit for Firefox by pHaez.
978
979  getPageSize : function() {
980
981    var xScroll, yScroll;
982
983    if (window.innerHeight && window.scrollMaxY) {
984      xScroll = window.innerWidth + window.scrollMaxX;
985      yScroll = window.innerHeight + window.scrollMaxY;
986    }
987    else if (document.body.scrollHeight > document.body.offsetHeight) { // All but Explorer Mac.
988      xScroll = document.body.scrollWidth;
989      yScroll = document.body.scrollHeight;
990    }
991    else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari.
992      xScroll = document.body.offsetWidth;
993      yScroll = document.body.offsetHeight;
994    }
995
996    var windowWidth, windowHeight;
997
998    if (self.innerHeight) { // All except Explorer.
999      if (document.documentElement.clientWidth) {
1000        windowWidth = document.documentElement.clientWidth;
1001      }
1002      else {
1003        windowWidth = self.innerWidth;
1004      }
1005      windowHeight = self.innerHeight;
1006    }
1007    else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode.
1008      windowWidth = document.documentElement.clientWidth;
1009      windowHeight = document.documentElement.clientHeight;
1010    }
1011    else if (document.body) { // Other Explorers.
1012      windowWidth = document.body.clientWidth;
1013      windowHeight = document.body.clientHeight;
1014    }
1015    // For small pages with total height less than height of the viewport.
1016    if (yScroll < windowHeight) {
1017      pageHeight = windowHeight;
1018    }
1019    else {
1020      pageHeight = yScroll;
1021    }
1022    // For small pages with total width less than width of the viewport.
1023    if (xScroll < windowWidth) {
1024      pageWidth = xScroll;
1025    }
1026    else {
1027      pageWidth = windowWidth;
1028    }
1029    arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight);
1030    return arrayPageSize;
1031  },
1032
1033
1034  // pause(numberMillis)
1035  pause : function(ms) {
1036    var date = new Date();
1037    var curDate = null;
1038    do { curDate = new Date(); }
1039    while (curDate - date < ms);
1040  },
1041
1042
1043  // toggleSelectsFlash()
1044  // Hide / unhide select lists and flash objects as they appear above the
1045  // lightbox in some browsers.
1046  toggleSelectsFlash: function (state) {
1047    if (state == 'visible') {
1048      $("select.lightbox_hidden, embed.lightbox_hidden, object.lightbox_hidden").show();
1049    }
1050    else if (state == 'hide') {
1051      $("select:visible, embed:visible, object:visible").not('#lightboxAutoModal select, #lightboxAutoModal embed, #lightboxAutoModal object').addClass("lightbox_hidden");
1052      $("select.lightbox_hidden, embed.lightbox_hidden, object.lightbox_hidden").hide();
1053    }
1054  },
1055
1056
1057  // parseRel()
1058  parseRel: function (link) {
1059    var parts = [];
1060    parts["rel"] = parts["title"] = parts["group"] = parts["style"] = parts["flashvars"] = null;
1061    if (!$(link).attr('rel')) return parts;
1062    parts["rel"] = $(link).attr('rel').match(/\w+/)[0];
1063
1064    if ($(link).attr('rel').match(/\[(.*)\]/)) {
1065      var info = $(link).attr('rel').match(/\[(.*?)\]/)[1].split('|');
1066      parts["group"] = info[0];
1067      parts["style"] = info[1];
1068      if (parts["style"] != undefined && parts["style"].match(/flashvars:\s?(.*?);/)) {
1069        parts["flashvars"] = parts["style"].match(/flashvars:\s?(.*?);/)[1];
1070      }
1071    }
1072    if ($(link).attr('rel').match(/\[.*\]\[(.*)\]/)) {
1073      parts["title"] = $(link).attr('rel').match(/\[.*\]\[(.*)\]/)[1];
1074    }
1075    return parts;
1076  },
1077
1078  // setStyles()
1079  setStyles: function(item, styles) {
1080    item.width = Lightbox.iframe_width;
1081    item.height = Lightbox.iframe_height;
1082    item.scrolling = "auto";
1083
1084    if (!styles) return item;
1085    var stylesArray = styles.split(';');
1086    for (var i = 0; i< stylesArray.length; i++) {
1087      if (stylesArray[i].indexOf('width:') >= 0) {
1088        var w = stylesArray[i].replace('width:', '');
1089        item.width = jQuery.trim(w);
1090      }
1091      else if (stylesArray[i].indexOf('height:') >= 0) {
1092        var h = stylesArray[i].replace('height:', '');
1093        item.height = jQuery.trim(h);
1094      }
1095      else if (stylesArray[i].indexOf('scrolling:') >= 0) {
1096        var scrolling = stylesArray[i].replace('scrolling:', '');
1097        item.scrolling = jQuery.trim(scrolling);
1098      }
1099      else if (stylesArray[i].indexOf('overflow:') >= 0) {
1100        var overflow = stylesArray[i].replace('overflow:', '');
1101        item.overflow = jQuery.trim(overflow);
1102      }
1103    }
1104    return item;
1105  },
1106
1107
1108  // togglePlayPause()
1109  // Hide the pause / play button as appropriate.  If pausing the slideshow also
1110  // clear the timers, otherwise move onto the next image.
1111  togglePlayPause: function(hideId, showId) {
1112    if (Lightbox.isSlideshow && hideId == "lightshowPause") {
1113      for (var i = 0; i < Lightbox.slideIdCount; i++) {
1114        window.clearTimeout(Lightbox.slideIdArray[i]);
1115      }
1116    }
1117    $('#' + hideId).hide();
1118    $('#' + showId).show();
1119
1120    if (hideId == "lightshowPlay") {
1121      Lightbox.isPaused = false;
1122      if (!Lightbox.loopSlides && Lightbox.activeImage == (Lightbox.total - 1)) {
1123        Lightbox.end();
1124      }
1125      else if (Lightbox.total > 1) {
1126        Lightbox.changeData(Lightbox.activeImage + 1);
1127      }
1128    }
1129    else {
1130      Lightbox.isPaused = true;
1131    }
1132  },
1133
1134  triggerLightbox: function (rel_type, rel_group) {
1135    if (rel_type.length) {
1136      if (rel_group && rel_group.length) {
1137        $("a[rel^='" + rel_type +"\[" + rel_group + "\]'], area[rel^='" + rel_type +"\[" + rel_group + "\]']").eq(0).trigger("click");
1138      }
1139      else {
1140        $("a[rel^='" + rel_type +"'], area[rel^='" + rel_type +"']").eq(0).trigger("click");
1141      }
1142    }
1143  },
1144
1145  detectMacFF2: function() {
1146    var ua = navigator.userAgent.toLowerCase();
1147    if (/firefox[\/\s](\d+\.\d+)/.test(ua)) {
1148      var ffversion = new Number(RegExp.$1);
1149      if (ffversion < 3 && ua.indexOf('mac') != -1) {
1150        return true;
1151      }
1152    }
1153    return false;
1154  },
1155
1156  checkKey: function(keys, key, code) {
1157    return (jQuery.inArray(key, keys) != -1 || jQuery.inArray(String(code), keys) != -1);
1158  },
1159
1160  filterXSS: function(str, allowed_tags) {
1161    var output = "";
1162    $.ajax({
1163      url: Drupal.settings.basePath + 'system/lightbox2/filter-xss',
1164      data: {
1165        'string' : str,
1166        'allowed_tags' : allowed_tags
1167      },
1168      type: "POST",
1169      async: false,
1170      dataType:  "json",
1171      success: function(data) {
1172        output = data;
1173      }
1174    });
1175    return output;
1176  }
1177
1178};
1179
1180// Initialize the lightbox.
1181Drupal.behaviors.initLightbox = function (context) {
1182  $('body:not(.lightbox-processed)', context).addClass('lightbox-processed').each(function() {
1183    Lightbox.initialize();
1184    return false; // Break the each loop.
1185  });
1186
1187  // Attach lightbox to any links with lightbox rels.
1188  Lightbox.initList(context);
1189  $('#lightboxAutoModal', context).triggerHandler('click');
1190};
1191
1192;
1193Drupal.behaviors.ddContactLoadReferrers = function(context) {
1194  var teasers = $('#node-sidebar .field-email a, #block-digitaldcore-node_contacts .node-teaser .field-email a', context);
1195
1196  teasers.each(function() {
1197    var teaser = $(this);
1198
1199    if (teaser.attr('href').indexOf('?profile=') > 0) {
1200      // Add destination parameter to the end to be able to track where the link came from
1201      var destination = window.location.pathname.substring(Drupal.settings.basePath.length);
1202      teaser.attr('href', teaser.attr('href') + '&destination=' + encodeURIComponent(destination));
1203    }
1204  });
1205};
1206;
1207/* eslint-disable */
1208(function(cc) {
1209  // stop from running again, if accidently included more than once.
1210  if (cc.hasInitialised) {
1211    return;
1212  }
1213
1214  var util = {
1215    // http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
1216    escapeRegExp: function(str) {
1217      return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
1218    },
1219
1220    hasClass: function(element, selector) {
1221      var s = ' ';
1222      return element.nodeType === 1 &&
1223        (s + element.className + s).replace(/[\n\t]/g, s).indexOf(s + selector + s) >= 0;
1224    },
1225
1226    addClass: function(element, className) {
1227      element.className += ' ' + className;
1228    },
1229
1230    removeClass: function(element, className) {
1231      var regex = new RegExp('\\b' + this.escapeRegExp(className) + '\\b');
1232      element.className = element.className.replace(regex, '');
1233    },
1234
1235    interpolateString: function(str, callback) {
1236      var marker = /{{([a-z][a-z0-9\-_]*)}}/ig;
1237      return str.replace(marker, function(matches) {
1238        return callback(arguments[1]) || '';
1239      })
1240    },
1241
1242    getCookie: function(name) {
1243      // Remove timestamp from cookie value.
1244      var value = '; ' + document.cookie;
1245      var parts = value.split('; ' + name + '=');
1246      var status = parts.pop().split(',').shift();
1247      if (status in cc.status) {
1248        return status;
1249      } else {
1250        value = '; ' + document.cookie;
1251        parts = value.split('; ' + name + '=');
1252        return parts.length != 2 ?
1253          undefined : parts.pop().split(';').shift();
1254      }
1255    },
1256
1257    setCookie: function(name, value, expiryDays, domain, path) {
1258      var exdate = new Date();
1259      exdate.setDate(exdate.getDate() + (expiryDays || 365));
1260
1261      var cookie = [
1262        name + '=' + value,
1263        'expires=' + exdate.toUTCString(),
1264        'path=' + (path || '/')
1265      ];
1266
1267      if (domain) {
1268        cookie.push('domain=' + domain);
1269      }
1270      document.cookie = cookie.join(';');
1271    },
1272
1273    // only used for extending the initial options
1274    deepExtend: function(target, source) {
1275      for (var prop in source) {
1276        if (source.hasOwnProperty(prop)) {
1277          if (prop in target && this.isPlainObject(target[prop]) && this.isPlainObject(source[prop])) {
1278            this.deepExtend(target[prop], source[prop]);
1279          } else {
1280            target[prop] = source[prop];
1281          }
1282        }
1283      }
1284      return target;
1285    },
1286
1287    // only used for throttling the 'mousemove' event (used for animating the revoke button when `animateRevokable` is true)
1288    throttle: function(callback, limit) {
1289      var wait = false;
1290      return function() {
1291        if (!wait) {
1292          callback.apply(this, arguments);
1293          wait = true;
1294          setTimeout(function() {
1295            wait = false;
1296          }, limit);
1297        }
1298      }
1299    },
1300
1301    // only used for hashing json objects (used for hash mapping palette objects, used when custom colours are passed through JavaScript)
1302    hash: function(str) {
1303      var hash = 0,
1304        i, chr, len;
1305      if (str.length === 0) return hash;
1306      for (i = 0, len = str.length; i < len; ++i) {
1307        chr = str.charCodeAt(i);
1308        hash = ((hash << 5) - hash) + chr;
1309        hash |= 0;
1310      }
1311      return hash;
1312    },
1313
1314    normaliseHex: function(hex) {
1315      if (hex[0] == '#') {
1316        hex = hex.substr(1);
1317      }
1318      if (hex.length == 3) {
1319        hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
1320      }
1321      return hex;
1322    },
1323
1324    // used to get text colors if not set
1325    getContrast: function(hex) {
1326      hex = this.normaliseHex(hex);
1327      var r = parseInt(hex.substr(0, 2), 16);
1328      var g = parseInt(hex.substr(2, 2), 16);
1329      var b = parseInt(hex.substr(4, 2), 16);
1330      var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
1331      return (yiq >= 128) ? '#000' : '#fff';
1332    },
1333
1334    // used to change color on highlight
1335    getLuminance: function(hex) {
1336      var num = parseInt(this.normaliseHex(hex), 16), 
1337          amt = 38,
1338          R = (num >> 16) + amt,
1339          B = (num >> 8 & 0x00FF) + amt,
1340          G = (num & 0x0000FF) + amt;
1341      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);
1342      return '#'+newColour;
1343    },
1344
1345    isMobile: function() {
1346      return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
1347    },
1348
1349    isPlainObject: function(obj) {
1350      // The code "typeof obj === 'object' && obj !== null" allows Array objects
1351      return typeof obj === 'object' && obj !== null && obj.constructor == Object;
1352    },
1353  };
1354
1355  // valid cookie values
1356  cc.status = {
1357    deny: 'deny',
1358    allow: 'allow',
1359    dismiss: 'dismiss'
1360  };
1361
1362  // detects the `transitionend` event name
1363  cc.transitionEnd = (function() {
1364    var el = document.createElement('div');
1365    var trans = {
1366      t: "transitionend",
1367      OT: "oTransitionEnd",
1368      msT: "MSTransitionEnd",
1369      MozT: "transitionend",
1370      WebkitT: "webkitTransitionEnd",
1371    };
1372
1373    for (var prefix in trans) {
1374      if (trans.hasOwnProperty(prefix) && typeof el.style[prefix + 'ransition'] != 'undefined') {
1375        return trans[prefix];
1376      }
1377    }
1378    return '';
1379  }());
1380
1381  cc.hasTransition = !!cc.transitionEnd;
1382
1383  // array of valid regexp escaped statuses
1384  var __allowedStatuses = Object.keys(cc.status).map(util.escapeRegExp);
1385
1386  // contains references to the custom <style> tags
1387  cc.customStyles = {};
1388
1389  cc.Popup = (function() {
1390
1391    var defaultOptions = {
1392
1393      // if false, this prevents the popup from showing (useful for giving to control to another piece of code)
1394      enabled: true,
1395
1396      // optional (expecting a HTML element) if passed, the popup is appended to this element. default is `document.body`
1397      container: null,
1398
1399      // defaults cookie options - it is RECOMMENDED to set these values to correspond with your server
1400      cookie: {
1401        // This is the name of this cookie - you can ignore this
1402        name: 'cookieconsent_status',
1403
1404        // This is the url path that the cookie 'name' belongs to. The cookie can only be read at this location
1405        path: '/',
1406
1407        // This is the domain that the cookie 'name' belongs to. The cookie can only be read on this domain.
1408        //  - Guide to cookie domains - http://erik.io/blog/2014/03/04/definitive-guide-to-cookie-domains/
1409        domain: '',
1410
1411        // The cookies expire date, specified in days (specify -1 for no expiry)
1412        expiryDays: 365,
1413        cookieVersion: 0,
1414      },
1415
1416      // these callback hooks are called at certain points in the program execution
1417      onPopupOpen: function() {},
1418      onPopupClose: function() {},
1419      onInitialise: function(status) {},
1420      onStatusChange: function(status, chosenBefore) {},
1421      onRevokeChoice: function() {},
1422
1423      // each item defines the inner text for the element that it references
1424      content: {
1425        header: 'Cookies used on the website!',
1426        message: 'This website uses cookies to ensure you get the best experience on our website.',
1427        dismiss: 'Got it!',
1428        allow: 'Allow cookies',
1429        deny: 'Decline',
1430        link: 'Learn more',
1431        href: 'http://cookiesandyou.com',
1432        close: '&#x274c;',
1433        target: '_self', // 180713
1434        version: 1,
1435      },
1436
1437      // This is the HTML for the elements above. The string {{header}} will be replaced with the equivalent text below.
1438      // You can remove "{{header}}" and write the content directly inside the HTML if you want.
1439      //
1440      //  - ARIA rules suggest to ensure controls are tabbable (so the browser can find the first control),
1441      //    and to set the focus to the first interactive control (http://w3c.github.io/aria-in-html/)
1442      elements: {
1443        header: '<span class="cc-header">{{header}}</span>&nbsp;',
1444        message: '<span id="cookieconsent:{{version}}:desc" class="cc-message">{{message}}</span>',
1445        messagelink: '<span class="cc-message cc-message-link" id="cookieconsent:{{version}}:desc">{{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>',
1446        dismiss: '<a aria-label="dismiss cookie message" role=button tabindex="0" class="cc-btn cc-dismiss">{{dismiss}}</a>',
1447        allow: '<a aria-label="allow cookies" role=button tabindex="0"  class="cc-btn cc-allow">{{allow}}</a>',
1448        deny: '<a aria-label="deny cookies" role=button tabindex="0" class="cc-btn cc-deny">{{deny}}</a>',
1449        // link: '<a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" target="_blank">{{link}}</a>',
1450        link: '<a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" target="{{target}}">{{link}}</a>',
1451        close: '<span aria-label="dismiss cookie message" role=button tabindex="0" class="cc-close">{{close}}</span>',
1452
1453        //compliance: compliance is also an element, but it is generated by the application, depending on `type` below
1454      },
1455
1456      // The placeholders {{classes}} and {{children}} both get replaced during initialisation:
1457      //  - {{classes}} is where additional classes get added
1458      //  - {{children}} is where the HTML children are placed
1459      window: '<div role="dialog" aria-live="polite" aria-label="cookieconsent" aria-describedby="cookieconsent:{{version}}:desc" class="cc-window {{classes}}"><!--googleoff: all-->{{children}}<!--googleon: all--></div>',
1460
1461      // This is the html for the revoke button. This only shows up after the user has selected their level of consent
1462      // It can be enabled of disabled using the `revokable` option
1463      revokeBtn: '<div class="cc-revoke {{classes}}">Cookie Policy</div>',
1464
1465      // define types of 'compliance' here. '{{value}}' strings in here are linked to `elements`
1466      compliance: {
1467        'info': '<div class="cc-compliance">{{dismiss}}</div>',
1468        'opt-in': '<div class="cc-compliance cc-highlight">{{dismiss}}{{allow}}</div>',
1469        'opt-out': '<div class="cc-compliance cc-highlight">{{deny}}{{dismiss}}</div>',
1470      },
1471
1472      // select your type of popup here
1473      type: 'info', // refers to `compliance` (in other words, the buttons that are displayed)
1474
1475      // define layout layouts here
1476      layouts: {
1477        // the 'block' layout tend to be for square floating popups
1478        'basic': '{{messagelink}}{{compliance}}',
1479        'basic-close': '{{messagelink}}{{compliance}}{{close}}',
1480        'basic-header': '{{header}}{{message}}{{link}}{{compliance}}',
1481
1482        // add a custom layout here, then add some new css with the class '.cc-layout-my-cool-layout'
1483        //'my-cool-layout': '<div class="my-special-layout">{{message}}{{compliance}}</div>{{close}}',
1484      },
1485
1486      // default layout (see above)
1487      layout: 'basic',
1488
1489      // this refers to the popup windows position. we currently support:
1490      //  - banner positions: top, bottom
1491      //  - floating positions: top-left, top-right, bottom-left, bottom-right
1492      //
1493      // adds a class `cc-floating` or `cc-banner` which helps when styling
1494      position: 'bottom', // default position is 'bottom'
1495
1496      // Available styles
1497      //    -block (default, no extra classes)
1498      //    -edgeless
1499      //    -classic
1500      // use your own style name and use `.cc-theme-STYLENAME` class in CSS to edit.
1501      // Note: style "wire" is used for the configurator, but has no CSS styles of its own, only palette is used.
1502      theme: 'block',
1503
1504      // The popup is `fixed` by default, but if you want it to be static (inline with the page content), set this to fal
1504se
1505      // Note: by default, we animate the height of the popup from 0 to full size
1506      static: false,
1507
1508      // if you want custom colours, pass them in here. this object should look like this.
1509      // ideally, any custom colours/themes should be created in a separate style sheet, as this is more efficient.
1510      //   {
1511      //     popup: {background: '#000000', text: '#fff', link: '#fff'},
1512      //     button: {background: 'transparent', border: '#f8e71c', text: '#f8e71c'},
1513      //     highlight: {background: '#f8e71c', border: '#f8e71c', text: '#000000'},
1514      //   }
1515      // `highlight` is optional and extends `button`. if it exists, it will apply to the first button
1516      // only background needs to be defined for every element. if not set, other colors can be calculated from it
1517      palette: null,
1518
1519      // Some countries REQUIRE that a user can change their mind. You can configure this yourself.
1520      // Most of the time this should be false, but the `cookieconsent.law` can change this to `true` if it detects that it should
1521      revokable: false,
1522
1523      // if true, the revokable button will tranlate in and out
1524      animateRevokable: true,
1525
1526      // used to disable link on existing layouts
1527      // replaces element messagelink with message and removes content of link
1528      showLink: true,
1529
1530      // set value as scroll range to enable
1531      dismissOnScroll: false,
1532
1533      // set value as time in milliseconds to autodismiss after set time
1534      dismissOnTimeout: false,
1535
1536      // The application automatically decide whether the popup should open.
1537      // Set this to false to prevent this from happening and to allow you to control the behaviour yourself
1538      autoOpen: true,
1539
1540      // By default the created HTML is automatically appended to the container (which defaults to <body>). You can prevent this behaviour
1541      // by setting this to false, but if you do, you must attach the `element` yourself, which is a public property of the popup instance:
1542      // 
1543      //     var instance = cookieconsent.factory(options);
1544      //     document.body.appendChild(instance.element);
1545      //
1546      autoAttach: true,
1547
1548      // simple whitelist/blacklist for pages. specify page by:
1549      //   - using a string : '/index.html'           (matches '/index.html' exactly) OR
1550      //   - using RegExp   : /\/page_[\d]+\.html/    (matched '/page_1.html' and '/page_2.html' etc)
1551      whitelistPage: [],
1552      blacklistPage: [],
1553
1554      // If this is defined, then it is used as the inner html instead of layout. This allows for ultimate customisation.
1555      // Be sure to use the classes `cc-btn` and `cc-allow`, `cc-deny` or `cc-dismiss`. They enable the app to register click
1556      // handlers. You can use other pre-existing classes too. See `src/styles` folder.
1557      overrideHTML: null,
1558      // Specifies index of message displayed.
1559      index: 0,
1560    };
1561
1562    function CookiePopup() {
1563      this.initialise.apply(this, arguments);
1564    }
1565
1566    CookiePopup.prototype.initialise = function(options) {
1567      if (this.options) {
1568        this.destroy(); // already rendered
1569      }
1570
1571      // set options back to default options
1572      util.deepExtend(this.options = {}, defaultOptions);
1573
1574      // merge in user options
1575      if (util.isPlainObject(options)) {
1576        util.deepExtend(this.options, options);
1577      }
1578
1579      // returns true if `onComplete` was called
1580      if (checkCallbackHooks.call(this)) {
1581        // user has already answered
1582        this.options.enabled = false;
1583      }
1584
1585      // apply blacklist / whitelist
1586      if (arrayContainsMatches(this.options.blacklistPage, location.pathname)) {
1587        this.options.enabled = false;
1588      }
1589      if (arrayContainsMatches(this.options.whitelistPage, location.pathname)) {
1590        this.options.enabled = true;
1591      }
1592
1593      // the full markup either contains the wrapper or it does not (for multiple instances)
1594      var cookiePopup = this.options.window
1595        .replace('{{classes}}', getPopupClasses.call(this).join(' '))
1596        .replace('{{children}}', getPopupInnerMarkup.call(this));
1597
1598      // if user passes html, use it instead
1599      var customHTML = this.options.overrideHTML;
1600      if (typeof customHTML == 'string' && customHTML.length) {
1601        cookiePopup = customHTML;
1602      }
1603
1604      // if static, we need to grow the element from 0 height so it doesn't jump the page
1605      // content. we wrap an element around it which will mask the hidden content
1606      if (this.options.static) {
1607        // `grower` is a wrapper div with a hidden overflow whose height is animated
1608        var wrapper = appendMarkup.call(this, '<div class="cc-grower">' + cookiePopup + '</div>');
1609
1610        wrapper.style.display = ''; // set it to visible (because appendMarkup hides it)
1611        this.element = wrapper.firstChild; // get the `element` reference from the wrapper
1612        this.element.style.display = 'none';
1613        util.addClass(this.element, 'cc-invisible');
1614      } else {
1615        this.element = appendMarkup.call(this, cookiePopup);
1616      }
1617
1618      applyAutoDismiss.call(this);
1619
1620      applyRevokeButton.call(this);
1621
1622      if (this.options.autoOpen) {
1623        this.autoOpen();
1624      }
1625    };
1626
1627    CookiePopup.prototype.destroy = function() {
1628      if (this.onButtonClick && this.element) {
1629        this.element.removeEventListener('click', this.onButtonClick);
1630        this.onButtonClick = null;
1631      }
1632
1633      if (this.dismissTimeout) {
1634        clearTimeout(this.dismissTimeout);
1635        this.dismissTimeout = null;
1636      }
1637
1638      if (this.onWindowScroll) {
1639        window.removeEventListener('scroll', this.onWindowScroll);
1640        this.onWindowScroll = null;
1641      }
1642
1643      if (this.onMouseMove) {
1644        window.removeEventListener('mousemove', this.onMouseMove);
1645        this.onMouseMove = null;
1646      }
1647
1648      if (this.element && this.element.parentNode) {
1649        this.element.parentNode.removeChild(this.element);
1650      }
1651      this.element = null;
1652
1653      if (this.revokeBtn && this.revokeBtn.parentNode) {
1654        this.revokeBtn.parentNode.removeChild(this.revokeBtn);
1655      }
1656      this.revokeBtn = null;
1657
1658      removeCustomStyle(this.options.palette);
1659      this.options = null;
1660    };
1661
1662    CookiePopup.prototype.open = function(callback) {
1663      if (!this.element) return;
1664
1665      if (!this.isOpen()) {
1666        if (cc.hasTransition) {
1667          this.fadeIn();
1668        } else {
1669          this.element.style.display = '';
1670        }
1671
1672        if (this.options.revokable) {
1673          this.toggleRevokeButton();
1674        }
1675        this.options.onPopupOpen.call(this);
1676      }
1677
1678      return this;
1679    };
1680
1681    CookiePopup.prototype.close = function(showRevoke) {
1682      if (!this.element) return;
1683
1684      if (this.isOpen()) {
1685        if (cc.hasTransition) {
1686          this.fadeOut();
1687        } else {
1688          this.element.style.display = 'none';
1689        }
1690
1691        if (showRevoke && this.options.revokable) {
1692          this.toggleRevokeButton(true);
1693        }
1694        this.options.onPopupClose.call(this);
1695      }
1696
1697      return this;
1698    };
1699
1700    CookiePopup.prototype.fadeIn = function() {
1701      var el = this.element;
1702
1703      if (!cc.hasTransition || !el)
1704        return;
1705
1706      // This should always be called AFTER fadeOut (which is governed by the 'transitionend' event).
1707      // 'transitionend' isn't all that reliable, so, if we try and fadeIn before 'transitionend' has
1708      // has a chance to run, then we run it ourselves
1709      if (this.afterTransition) {
1710        afterFadeOut.call(this, el)
1711      }
1712
1713      if (util.hasClass(el, 'cc-invisible')) {
1714        el.style.display = '';
1715
1716        if (this.options.static) {
1717          var height = this.element.clientHeight;
1718          this.element.parentNode.style.maxHeight = height + 'px';
1719        }
1720
1721        var fadeInTimeout = 20; // (ms) DO NOT MAKE THIS VALUE SMALLER. See below
1722
1723        // Although most browsers can handle values less than 20ms, it should remain above this value.
1724        // This is because we are waiting for a "browser redraw" before we remove the 'cc-invisible' class.
1725        // If the class is removed before a redraw could happen, then the fadeIn effect WILL NOT work, and
1726        // the popup will appear from nothing. Therefore we MUST allow enough time for the browser to do
1727        // its thing. The actually difference between using 0 and 20 in a set timeout is neglegible anyway
1728        this.openingTimeout = setTimeout(afterFadeIn.bind(this, el), fadeInTimeout);
1729      }
1730    };
1731
1732    CookiePopup.prototype.fadeOut = function() {
1733      var el = this.element;
1734
1735      if (!cc.hasTransition || !el)
1736        return;
1737
1738      if (this.openingTimeout) {
1739        clearTimeout(this.openingTimeout);
1740        afterFadeIn.bind(this, el);
1741      }
1742
1743      if (!util.hasClass(el, 'cc-invisible')) {
1744        if (this.options.static) {
1745          this.element.parentNode.style.maxHeight = '';
1746        }
1747
1748        this.afterTransition = afterFadeOut.bind(this, el);
1749        el.addEventListener(cc.transitionEnd, this.afterTransition);
1750
1751        util.addClass(el, 'cc-invisible');
1752      }
1753    };
1754
1755    CookiePopup.prototype.isOpen = function() {
1756      return this.element && this.element.style.display == '' && (cc.hasTransition ? !util.hasClass(this.element, 'cc-invisible') : true);
1757    };
1758
1759    CookiePopup.prototype.toggleRevokeButton = function(show) {
1760      if (this.revokeBtn) this.revokeBtn.style.display = show ? '' : 'none';
1761    };
1762
1763    CookiePopup.prototype.revokeChoice = function(preventOpen) {
1764      this.options.enabled = true;
1765      this.clearStatus();
1766
1767      this.options.onRevokeChoice.call(this);
1768
1769      if (!preventOpen) {
1770        this.autoOpen();
1771      }
1772    };
1773
1774    // returns true if the cookie has a valid value
1775    CookiePopup.prototype.hasAnswered = function(options) {
1776      return Object.keys(cc.status).indexOf(this.getStatus()) >= 0;
1777    };
1778
1779    // returns true if the cookie indicates that consent has been given
1780    CookiePopup.prototype.hasConsented = function(options) {
1781      var val = this.getStatus();
1782      return val == cc.status.allow || val == cc.status.dismiss;
1783    };
1784
1785    // opens the popup if no answer has been given
1786    CookiePopup.prototype.autoOpen = function(options) {
1787      !this.hasAnswered() && this.options.enabled && this.open();
1788    };
1789
1790    CookiePopup.prototype.setStatus = function(status, cookie_data) {
1791      var cookie_data = cookie_data || false;
1792      // How to get button from here?
1793      // var c = this.options.cookie;
1794      var c = cookie_data ? cookie_data.cookie : this.options.cookie;
1795      var value = util.getCookie(c.name);
1796      // @todo - status = 'status,timestamp'
1797      var chosenBefore = Object.keys(cc.status).indexOf(value) >= 0;
1798      // if `status` is valid
1799      if (Object.keys(cc.status).indexOf(status) >= 0) {
1800        // util.setCookie(c.name, status + ',' + (Date.now() / 1000 | 0), c.expiryDays, c.domain, c.path);
1801        util.setCookie(c.name, status + ',' + c.cookieVersion, c.expiryDays, c.domain, c.path);
1802
1803        this.options.onStatusChange.call(this, status, chosenBefore);
1804      } else {
1805        this.clearStatus();
1806      }
1807    };
1808
1809    CookiePopup.prototype.getStatus = function() {
1810      // @todo - make sure it doesn't get the wrong cookie name
1811      return util.getCookie(this.options.cookie.name);
1812    };
1813
1814    CookiePopup.prototype.clearStatus = function() {
1815      // @todo - make sure it doesn't get the wrong cookie name for dd_sitewide_notifcation
1816      var c = this.options.cookie;
1817      util.setCookie(c.name, '', -1, c.domain, c.path);
1818    };
1819
1820    // This needs to be called after 'fadeIn'. This is the code that actually causes the fadeIn to work
1821    // There is a good reason why it's called in a timeout. Read 'fadeIn';
1822    function afterFadeIn(el) {
1823      this.openingTimeout = null;
1824      util.removeClass(el, 'cc-invisible');
1825    }
1826
1827    // This is called on 'transitionend' (only on the transition of the fadeOut). That's because after we've faded out, we need to
1828    // set the display to 'none' (so there aren't annoying invisible popups all over the page). If for whenever reason this function
1829    // is not called (lack of support), the open/close mechanism will still work.
1830    function afterFadeOut(el) {
1831      el.style.display = 'none'; // after close and before open, the display should be none
1832      el.removeEventListener(cc.transitionEnd, this.afterTransition);
1833      this.afterTransition = null;
1834    }
1835
1836    // this function calls the `onComplete` hook and returns true (if needed) and returns false otherwise
1837    function checkCallbackHooks() {
1838      var complete = this.options.onInitialise.bind(this);
1839
1840      if (!window.navigator.cookieEnabled) {
1841        complete(cc.status.deny);
1842        return true;
1843      }
1844
1845      if (window.CookiesOK || window.navigator.CookiesOK) {
1846        complete(cc.status.allow);
1847        return true;
1848      }
1849
1850      var allowed = Object.keys(cc.status);
1851      var answer = this.getStatus();
1852      var match = allowed.indexOf(answer) >= 0;
1853
1854      if (match) {
1855        complete(answer);
1856      }
1857      return match;
1858    }
1859
1860    function getPositionClasses() {
1861      var positions = this.options.position.split('-'); // top, bottom, left, right
1862      var classes = [];
1863
1864      // top, left, right, bottom
1865      positions.forEach(function(cur) {
1866        classes.push('cc-' + cur);
1867      });
1868
1869      return classes;
1870    }
1871
1872    function getPopupClasses() {
1873      var opts = this.options;
1874      var positionStyle = (opts.position == 'top' || opts.position == 'bottom') ? 'banner' : 'floating';
1875
1876      if (util.isMobile()) {
1877        positionStyle = 'floating';
1878      }
1879
1880      var classes = [
1881        'cc-' + positionStyle, // floating or banner
1882        'cc-type-' + opts.type, // add the compliance type
1883        'cc-theme-' + opts.theme, // add the theme
1884      ];
1885
1886      if (opts.static) {
1887        classes.push('cc-static');
1888      }
1889
1890      classes.push.apply(classes, getPositionClasses.call(this));
1891
1892      // we only add extra styles if `palette` has been set to a valid value
1893      var didAttach = attachCustomPalette.call(this, this.options.palette);
1894
1895      // if we override the palette, add the class that enables this
1896      if (this.customStyleSelector) {
1897        classes.push(this.customStyleSelector);
1898      }
1899
1900      return classes;
1901    }
1902
1903    function getPopupInnerMarkup() {
1904      var interpolated = {};
1905      var opts = this.options;
1906
1907      // removes link if showLink is false
1908      if (!opts.showLink) {
1909        opts.elements.link = '';
1910        opts.elements.messagelink = opts.elements.message;
1911      }
1912
1913      Object.keys(opts.elements).forEach(function(prop) {
1914        interpolated[prop] = util.interpolateString(opts.elements[prop], function(name) {
1915          var str = opts.content[name];
1916          return (name && typeof str == 'string' && str.length) ? str : '';
1917        })
1918      });
1919
1920      // checks if the type is valid and defaults to info if it's not
1921      var complianceType = opts.compliance[opts.type];
1922      if (!complianceType) {
1923        complianceType = opts.compliance.info;
1924      }
1925
1926      // build the compliance types from the already interpolated `elements`
1927      interpolated.compliance = util.interpolateString(complianceType, function(name) {
1928        return interpolated[name];
1929      });
1930
1931      // checks if the layout is valid and defaults to basic if it's not
1932      var layout = opts.layouts[opts.layout];
1933      if (!layout) {
1934        layout = opts.layouts.basic;
1935      }
1936
1937      return util.interpolateString(layout, function(match) {
1938        return interpolated[match];
1939      });
1940    }
1941
1942    function appendMarkup(markup) {
1943      var opts = this.options;
1944      var div = document.createElement('div');
1945      var cont = (opts.container && opts.container.nodeType === 1) ? opts.container : document.body;
1946
1947      div.innerHTML = markup;
1948
1949      var el = div.children[0];
1950
1951      el.style.display = 'none';
1952
1953      if (util.hasClass(el, 'cc-window') && cc.hasTransition) {
1954        util.addClass(el, 'cc-invisible');
1955      }
1956
1957      // save ref to the function handle so we can unbind it later
1958      this.onButtonClick = handleButtonClick.bind(this);
1959
1960      el.addEventListener('click', this.onButtonClick);
1961
1962      if (opts.autoAttach) {
1963        if (!cont.firstChild) {
1964          cont.appendChild(el);
1965        } else {
1966          cont.insertBefore(el, cont.firstChild)
1967        }
1968      }
1969
1970      return el;
1971    }
1972
1973    function handleButtonClick(event) {
1974      var targ = event.target;
1975      if (util.hasClass(targ, 'cc-btn')) {
1976
1977        var matches = targ.className.match(new RegExp("\\bcc-(" + __allowedStatuses.join('|') + ")\\b"));
1978        var match = (matches && matches[1]) || false;
1979
1980        if (match) {
1981          // new cookieconsent.Popup(cookieconsent.all_options[event.currentTarget.attributes['data-index']]);
1982          var target = event.target.attributes['data-index'];
1983          if (target) {
1984            var cookie_index = parseInt(target.value);
1985            var cookie_data = cookieconsent.all_options[cookie_index];
1986          }
1987          // var cookie_index = parseInt(event.target.attributes['data-index'].value);
1988          // var cookie_data = cookieconsent.all_options[cookie_index];
1989          this.setStatus(match, cookie_data ? cookie_data : null);
1990          this.close(true);
1991        }
1992      }
1993      if (util.hasClass(targ, 'cc-close')) {
1994        var cookie_index = parseInt(event.target.attributes['data-index'].value);
1995        var cookie_data = cookieconsent.all_options[cookie_index];
1996        // this.setStatus(cc.status.dismiss + ',' + Date.now() / 1000 | 0, cookie_data ? cookie_data : null);
1997        // this.setStatus(cc.status.dismiss + ',' + cookie_data.cookieVersion, cookie_data ? cookie_data : null);
1998        this.setStatus(cc.status.dismiss, cookie_data ? cookie_data : null);
1999        this.close(true);
2000      }
2001      if (util.hasClass(targ, 'cc-revoke')) {
2002        this.revokeChoice();
2003      }
2004    }
2005
2006    // I might change this function to use inline styles. I originally chose a stylesheet because I could select many elements with a
2007    // single rule (something that happened a lot), the apps has changed slightly now though, so inline styles might be more applicable.
2008    function attachCustomPalette(palette) {
2009      var hash = util.hash(JSON.stringify(palette));
2010      var selector = 'cc-color-override-' + hash;
2011      var isValid = util.isPlainObject(palette);
2012
2013      this.customStyleSelector = isValid ? selector : null;
2014
2015      if (isValid) {
2016        addCustomStyle(hash, palette, '.' + selector);
2017      }
2018      return isValid;
2019    }
2020
2021    function addCustomStyle(hash, palette, prefix) {
2022
2023      // only add this if a style like it doesn't exist
2024      if (cc.customStyles[hash]) {
2025        // custom style already exists, so increment the reference count
2026        ++cc.customStyles[hash].references;
2027        return;
2028      }
2029
2030      var colorStyles = {};
2031      var popup = palette.popup;
2032      var button = palette.button;
2033      var highlight = palette.highlight;
2034      // needs background colour, text and link will be set to black/white if not specified
2035      if (popup) {
2036        // assumes popup.background is set
2037        popup.text = popup.text ? popup.text : util.getContrast(popup.background);
2038        popup.link = popup.link ? popup.link : popup.text;
2039
2040        colorStyles[prefix + '.cc-window'] = [
2041          'color: ' + popup.text,
2042          'background-color: ' + popup.background
2043        ];
2044        colorStyles[prefix + '.cc-revoke'] = [
2045          'color: ' + popup.text,
2046          'background-color: ' + popup.background
2047        ];
2048        colorStyles[prefix + ' .cc-link,' + prefix + ' .cc-link:active,' + prefix + ' .cc-link:visited'] = [
2049          'color: ' + popup.link
2050        ];
2051
2052        if (button) {
2053          // assumes button.background is set
2054          button.text = button.text ? button.text : util.getContrast(button.background);
2055          button.border = button.border ? button.border : 'transparent';
2056          colorStyles[prefix + ' .cc-btn'] = [
2057            'color: ' + button.text,
2058            'border-color: ' + button.border,
2059            'background-color: ' + button.background
2060          ];
2061          
2062          if(button.background != 'transparent') 
2063            colorStyles[prefix + ' .cc-btn:hover, ' + prefix + ' .cc-btn:focus'] = [
2064              'background-color: ' + getHoverColour(button.background)
2065            ];
2066
2067          if (highlight) {
2068            //assumes highlight.background is set
2069            highlight.text = highlight.text ? highlight.text : util.getContrast(highlight.background);
2070            highlight.border = highlight.border ? highlight.border : 'transparent';
2071            colorStyles[prefix + ' .cc-highlight .cc-btn:first-child'] = [
2072              'color: ' + highlight.text,
2073              'border-color: ' + highlight.border,
2074              'background-color: ' + highlight.background
2075            ];
2076          } else {
2077            // sets highlight text color to popup text. background and border are transparent by default.
2078            colorStyles[prefix + ' .cc-highlight .cc-btn:first-child'] = [
2079              'color: ' + popup.text
2080            ];
2081          }
2082        }
2083
2084      }
2085
2086      // this will be interpretted as CSS. the key is the selector, and each array element is a rule
2087      var style = document.createElement('style');
2088      document.head.appendChild(style);
2089
2090      // custom style doesn't exist, so we create it
2091      cc.customStyles[hash] = {
2092        references: 1,
2093        element: style.sheet
2094      };
2095
2096      var ruleIndex = -1;
2097      for (var prop in colorStyles) {
2098        if (colorStyles.hasOwnProperty(prop)) {
2099          style.sheet.insertRule(prop + '{' + colorStyles[prop].join(';') + '}', ++ruleIndex);
2100        }
2101      }
2102    }
2103
2104    function getHoverColour(hex) {
2105      hex = util.normaliseHex(hex);
2106      // for black buttons
2107      if (hex == '000000') {
2108        return '#222';
2109      }
2110      return util.getLuminance(hex);
2111    }
2112
2113    function removeCustomStyle(palette) {
2114      if (util.isPlainObject(palette)) {
2115        var hash = util.hash(JSON.stringify(palette));
2116        var customStyle = cc.customStyles[hash];
2117        if (customStyle && !--customStyle.references) {
2118          var styleNode = customStyle.element.ownerNode;
2119          if (styleNode && styleNode.parentNode) {
2120            styleNode.parentNode.removeChild(styleNode);
2121          }
2122          cc.customStyles[hash] = null;
2123        }
2124      }
2125    }
2126
2127    function arrayContainsMatches(array, search) {
2128      for (var i = 0, l = array.length; i < l; ++i) {
2129        var str = array[i];
2130        // if regex matches or string is equal, return true
2131        if ((str instanceof RegExp && str.test(search)) ||
2132          (typeof str == 'string' && str.length && str === search)) {
2133          return true;
2134        }
2135      }
2136      return false;
2137    }
2138
2139    function applyAutoDismiss() {
2140      // This function is not ready for dd_sitewide_notifications.
2141      var setStatus = this.setStatus.bind(this);
2142
2143      var delay = this.options.dismissOnTimeout;
2144      if (typeof delay == 'number' && delay >= 0) {
2145        this.dismissTimeout = window.setTimeout(function() {
2146          setStatus(cc.status.dismiss);
2147        }, Math.floor(delay));
2148      }
2149
2150      var scrollRange = this.options.dismissOnScroll;
2151      if (typeof scrollRange == 'number' && scrollRange >= 0) {
2152        var onWindowScroll = function(evt) {
2153          if (window.pageYOffset > Math.floor(scrollRange)) {
2154            setStatus(cc.status.dismiss);
2155
2156            window.removeEventListener('scroll', onWindowScroll);
2157            this.onWindowScroll = null;
2158          }
2159        };
2160
2161        this.onWindowScroll = onWindowScroll;
2162        window.addEventListener('scroll', onWindowScroll);
2163      }
2164    }
2165
2166    function applyRevokeButton() {
2167      // revokable is true if advanced compliance is selected
2168      if (this.options.type != 'info') this.options.revokable = true;
2169      // animateRevokable false for mobile devices
2170      if (util.isMobile()) this.options.animateRevokable = false;
2171
2172      if (this.options.revokable) {
2173        var classes = getPositionClasses.call(this);
2174        if (this.options.animateRevokable) {
2175          classes.push('cc-animate');
2176        }
2177        if (this.customStyleSelector) {
2178          classes.push(this.customStyleSelector)
2179        }
2180        var revokeBtn = this.options.revokeBtn.replace('{{classes}}', classes.join(' '));
2181        this.revokeBtn = appendMarkup.call(this, revokeBtn);
2182
2183        var btn = this.revokeBtn;
2184        if (this.options.animateRevokable) {
2185          var wait = false;
2186          var onMouseMove = util.throttle(function(evt) {
2187            var active = false;
2188            var minY = 20;
2189            var maxY = (window.innerHeight - 20);
2190
2191            if (util.hasClass(btn, 'cc-top') && evt.clientY < minY) active = true;
2192            if (util.hasClass(btn, 'cc-bottom') && evt.clientY > maxY) active = true;
2193
2194            if (active) {
2195              if (!util.hasClass(btn, 'cc-active')) {
2196                util.addClass(btn, 'cc-active');
2197              }
2198            } else {
2199              if (util.hasClass(btn, 'cc-active')) {
2200                util.removeClass(btn, 'cc-active');
2201              }
2202            }
2203          }, 200);
2204
2205          this.onMouseMove = onMouseMove;
2206          window.addEventListener('mousemove', onMouseMove);
2207        }
2208      }
2209    }
2210
2211    return CookiePopup
2212  }());
2213
2214  cc.Location = (function() {
2215
2216    // An object containing all the location services we have already set up.
2217    // When using a service, it could either return a data structure in plain text (like a JSON object) or an executable script
2218    // When the response needs to be executed by the browser, then `isScript` must be set to true, otherwise it won't work.
2219
2220    // When the service uses a script, the chances are that you'll have to use the script to make additional requests. In these
2221    // cases, the services `callback` property is called with a `done` function. When performing async operations, this must be called
2222    // with the data (or Error), and `cookieconsent.locate` will take care of the rest
2223    var defaultOptions = {
2224
2225      // The default timeout is 5 seconds. This is mainly needed to catch JSONP requests that error.
2226      // Otherwise there is no easy way to catch JSONP errors. That means that if a JSONP fails, the
2227      // app will take `timeout` milliseconds to react to a JSONP network error.
2228      timeout: 5000,
2229
2230      // the order that services will be attempted in
2231      services: [
2232        'freegeoip',
2233        'ipinfo',
2234        'maxmind'
2235
2236        /*
2237
2238        // 'ipinfodb' requires some options, so we define it using an object
2239        // this object will be passed to the function that defines the service
2240
2241        {
2242          name: 'ipinfodb',
2243          interpolateUrl: {
2244            // obviously, this is a fake key
2245            api_key: 'vOgI3748dnIytIrsJcxS7qsDf6kbJkE9lN4yEDrXAqXcKUNvjjZPox3ekXqmMMld'
2246          },
2247        },
2248
2249        // as well as defining an object, you can define a function that returns an object
2250
2251        function () {
2252          return {name: 'ipinfodb'};
2253        },
2254
2255        */
2256      ],
2257
2258      serviceDefinitions: {
2259
2260        freegeoip: function() {
2261          return {
2262            // This service responds with JSON, but they do not have 
2262CORS set, so we must use JSONP and provide a callback
2263            // The `{callback}` is automatically rewritten by the tool
2264            url: '//freegeoip.net/json/?callback={callback}',
2265            isScript: true, // this is JSONP, therefore we must set it to run as a script
2266            callback: function(done, response) {
2267              try{
2268                var json = JSON.parse(response);
2269                return json.error ? toError(json) : {
2270                  code: json.country_code
2271                };
2272              } catch (err) {
2273                return toError({error: 'Invalid response ('+err+')'});
2274              }
2275            }
2276          }
2277        },
2278
2279        ipinfo: function() {
2280          return {
2281            // This service responds with JSON, so we simply need to parse it and return the country code
2282            url: '//ipinfo.io',
2283            headers: ['Accept: application/json'],
2284            callback: function(done, response) {
2285              try{
2286                var json = JSON.parse(response);
2287                return json.error ? toError(json) : {
2288                  code: json.country
2289                };
2290              } catch (err) {
2291                return toError({error: 'Invalid response ('+err+')'});
2292              }
2293            }
2294          }
2295        },
2296
2297        // This service requires an option to define `key`. Options are proived using objects or functions
2298        ipinfodb: function(options) {
2299          return {
2300            // This service responds with JSON, so we simply need to parse it and return the country code
2301            url: '//api.ipinfodb.com/v3/ip-country/?key={api_key}&format=json&callback={callback}',
2302            isScript: true, // this is JSONP, therefore we must set it to run as a script
2303            callback: function(done, response) {
2304              try{
2305                var json = JSON.parse(response);
2306                return json.statusCode == 'ERROR' ? toError({error: json.statusMessage}) : {
2307                  code: json.countryCode
2308                };
2309              } catch (err) {
2310                return toError({error: 'Invalid response ('+err+')'});
2311              }
2312            }
2313          }
2314        },
2315
2316        maxmind: function() {
2317          return {
2318            // This service responds with a JavaScript file which defines additional functionality. Once loaded, we must
2319            // make an additional AJAX call. Therefore we provide a `done` callback that can be called asynchronously
2320            url: '//js.maxmind.com/js/apis/geoip2/v2.1/geoip2.js',
2321            isScript: true, // this service responds with a JavaScript file, so it must be run as a script
2322            callback: function(done) {
2323              // if everything went okay then `geoip2` WILL be defined
2324              if (!window.geoip2) {
2325                done(new Error('Unexpected response format. The downloaded script should have exported `geoip2` to the global scope'));
2326                return;
2327              }
2328
2329              geoip2.country(function(location) {
2330                try {
2331                  done({
2332                    code: location.country.iso_code
2333                  });
2334                } catch (err) {
2335                  done(toError(err));
2336                }
2337              }, function(err) {
2338                done(toError(err));
2339              });
2340
2341              // We can't return anything, because we need to wait for the second AJAX call to return.
2342              // Then we can 'complete' the service by passing data or an error to the `done` callback.
2343            }
2344          }
2345        },
2346      },
2347    };
2348
2349    function Location(options) {
2350      // Set up options
2351      util.deepExtend(this.options = {}, defaultOptions);
2352
2353      if (util.isPlainObject(options)) {
2354        util.deepExtend(this.options, options);
2355      }
2356
2357      this.currentServiceIndex = -1; // the index (in options) of the service we're currently using
2358    }
2359
2360    Location.prototype.getNextService = function() {
2361      var service;
2362
2363      do {
2364        service = this.getServiceByIdx(++this.currentServiceIndex);
2365      } while (this.currentServiceIndex < this.options.services.length && !service);
2366
2367      return service;
2368    };
2369
2370    Location.prototype.getServiceByIdx = function(idx) {
2371      // This can either be the name of a default locationService, or a function.
2372      var serviceOption = this.options.services[idx];
2373
2374      // If it's a string, use one of the location services.
2375      if (typeof serviceOption === 'function') {
2376        var dynamicOpts = serviceOption();
2377        if (dynamicOpts.name) {
2378          util.deepExtend(dynamicOpts, this.options.serviceDefinitions[dynamicOpts.name](dynamicOpts));
2379        }
2380        return dynamicOpts;
2381      }
2382
2383      // If it's a string, use one of the location services.
2384      if (typeof serviceOption === 'string') {
2385        return this.options.serviceDefinitions[serviceOption]();
2386      }
2387
2388      // If it's an object, assume {name: 'ipinfo', ...otherOptions}
2389      // Allows user to pass in API keys etc.
2390      if (util.isPlainObject(serviceOption)) {
2391        return this.options.serviceDefinitions[serviceOption.name](serviceOption);
2392      }
2393
2394      return null;
2395    };
2396
2397    // This runs the service located at index `currentServiceIndex`.
2398    // If the service fails, `runNextServiceOnError` will continue trying each service until all fail, or one completes successfully
2399    Location.prototype.locate = function(complete, error) {
2400      var service = this.getNextService();
2401
2402      if (!service) {
2403        error(new Error('No services to run'));
2404        return;
2405      }
2406
2407      this.callbackComplete = complete;
2408      this.callbackError = error;
2409
2410      this.runService(service, this.runNextServiceOnError.bind(this));
2411    };
2412
2413    // Potentially adds a callback to a url for jsonp.
2414    Location.prototype.setupUrl = function(service) {
2415      var serviceOpts = this.getCurrentServiceOpts();
2416      return service.url.replace(/\{(.*?)\}/g, function(_, param) {
2417        if (param === 'callback') {
2418          var tempName = 'callback' + Date.now();
2419          window[tempName] = function(res) {
2420            service.__JSONP_DATA = JSON.stringify(res);
2421          }
2422          return tempName;
2423        }
2424        if (param in serviceOpts.interpolateUrl) {
2425          return serviceOpts.interpolateUrl[param];
2426        }
2427      });
2428    };
2429
2430    // requires a `service` object that defines at least a `url` and `callback`
2431    Location.prototype.runService = function(service, complete) {
2432      var self = this;
2433
2434      // basic check to ensure it resembles a `service`
2435      if (!service || !service.url || !service.callback) {
2436        return;
2437      }
2438
2439      // we call either `getScript` or `makeAsyncRequest` depending on the type of resource
2440      var requestFunction = service.isScript ? getScript : makeAsyncRequest;
2441
2442      var url = this.setupUrl(service);
2443
2444      // both functions have similar signatures so we can pass the same arguments to both
2445      requestFunction(url, function(xhr) {
2446        // if `!xhr`, then `getScript` function was used, so there is no response text
2447        var responseText = xhr ? xhr.responseText : '';
2448
2449        // if the resource is a script, then this function is called after the script has been run.
2450        // if the script is JSONP, then a time defined function `callback_{Date.now}` has already
2451        // been called (as the JSONP callback). This callback sets the __JSONP_DATA property
2452        if (service.__JSONP_DATA) {
2453          responseText = service.__JSONP_DATA;
2454          delete service.__JSONP_DATA;
2455        }
2456
2457        // call the service callback with the response text (so it can parse the response)
2458        self.runServiceCallback.call(self, complete, service, responseText);
2459
2460      }, this.options.timeout, service.data, service.headers);
2461
2462      // `service.data` and `service.headers` are optional (they only count if `!service.isScript` anyway)
2463    };
2464
2465    // The service request has run (and possibly has a `responseText`) [no `responseText` if `isScript`]
2466    // We need to run its callback which determines if its successful or not
2467    // `complete` is called on success or failure
2468    Location.prototype.runServiceCallback = function(complete, service, responseText) {
2469      var self = this;
2470      // this is the function that is called if the service uses the async callback in its handler method
2471      var serviceResultHandler = function (asyncResult) {
2472        // if `result` is a valid value, then this function shouldn't really run
2473        // even if it is called by `service.callback`
2474        if (!result) {
2475          self.onServiceResult.call(self, complete, asyncResult)
2476        }
2477      };
2478
2479      // the function `service.callback` will either extract a country code from `responseText` and return it (in `result`)
2480      // or (if it has to make additional requests) it will call a `done` callback with the country code when it is ready
2481      var result = service.callback(serviceResultHandler, responseText);
2482
2483      if (result) {
2484        this.onServiceResult.call(this, complete, result);
2485      }
2486    };
2487
2488    // This is called with the `result` from `service.callback` regardless of how it provided that result (sync or async).
2489    // `result` will be whatever is returned from `service.callback`. A service callback should provide an object with data
2490    Location.prototype.onServiceResult = function(complete, result) {
2491      // convert result to nodejs style async callback
2492      if (result instanceof Error || (result && result.error)) {
2493        complete.call(this, result, null);
2494      } else {
2495        complete.call(this, null, result);
2496      }
2497    };
2498
2499    // if `err` is set, the next service handler is called
2500    // if `err` is null, the `onComplete` handler is called with `data`
2501    Location.prototype.runNextServiceOnError = function(err, data) {
2502      if (err) {
2503        this.logError(err);
2504
2505        var nextService = this.getNextService();
2506
2507        if (nextService) {
2508          this.runService(nextService, this.runNextServiceOnError.bind(this));
2509        } else {
2510          this.completeService.call(this, this.callbackError, new Error('All services failed'));
2511        }
2512      } else {
2513        this.completeService.call(this, this.callbackComplete, data);
2514      }
2515    };
2516
2517    Location.prototype.getCurrentServiceOpts = function() {
2518      var val = this.options.services[this.currentServiceIndex];
2519
2520      if (typeof val == 'string') {
2521        return {name: val};
2522      }
2523
2524      if (typeof val == 'function') {
2525        return val();
2526      }
2527
2528      if (util.isPlainObject(val)) {
2529        return val;
2530      }
2531
2532      return {};
2533    };
2534
2535    // calls the `onComplete` callback after resetting the `currentServiceIndex`
2536    Location.prototype.completeService = function(fn, data) {
2537      this.currentServiceIndex = -1;
2538
2539      fn && fn(data);
2540    };
2541
2542    Location.prototype.logError = function (err) {
2543      var idx = this.currentServiceIndex;
2544      var service = this.getServiceByIdx(idx);
2545
2546      console.error('The service[' + idx + '] (' + service.url + ') responded with the following error', err);
2547    };
2548
2549    function getScript(url, callback, timeout) {
2550      var timeoutIdx, s = document.createElement('script');
2551
2552      s.type = 'text/' + (url.type || 'javascript');
2553      s.src = url.src || url;
2554      s.async = false;
2555
2556      s.onreadystatechange = s.onload = function() {
2557        // this code handles two scenarios, whether called by onload or onreadystatechange
2558        var state = s.readyState;
2559
2560        clearTimeout(timeoutIdx);
2561
2562        if (!callback.done && (!state || /loaded|complete/.test(state))) {
2563          callback.done = true;
2564          callback();
2565          s.onreadystatechange = s.onload = null;
2566        }
2567      };
2568
2569      document.body.appendChild(s);
2570
2571      // You can't catch JSONP Errors, because it's handled by the script tag
2572      // one way is to use a timeout
2573      timeoutIdx = setTimeout(function () {
2574        callback.done = true;
2575        callback();
2576        s.onreadystatechange = s.onload = null;
2577      }, timeout);
2578    }
2579
2580    function makeAsyncRequest(url, onComplete, timeout, postData, requestHeaders) {
2581      var xhr = new(window.XMLHttpRequest || window.ActiveXObject)('MSXML2.XMLHTTP.3.0');
2582
2583      xhr.open(postData ? 'POST' : 'GET', url, 1);
2584
2585      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
2586      xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
2587
2588      if (Array.isArray(requestHeaders)) {
2589        for (var i = 0, l = requestHeaders.length; i < l; ++i) {
2590          var split = requestHeaders[i].split(':', 2)
2591          xhr.setRequestHeader(split[0].replace(/^\s+|\s+$/g, ''), split[1].replace(/^\s+|\s+$/g, ''));
2592        }
2593      }
2594
2595      if (typeof onComplete == 'function') {
2596        xhr.onreadystatechange = function() {
2597          if (xhr.readyState > 3) {
2598            onComplete(xhr);
2599          }
2600        };
2601      }
2602
2603      xhr.send(postData);
2604    }
2605
2606    function toError(obj) {
2607      return new Error('Error [' + (obj.code || 'UNKNOWN') + ']: ' + obj.error);
2608    }
2609
2610    return Location;
2611  }());
2612
2613  cc.Law = (function() {
2614
2615    var defaultOptions = {
2616      // Make this false if you want to disable all regional overrides for settings.
2617      // If true, options can differ by country, depending on their cookie law.
2618      // It does not affect hiding the popup for countries that do not have cookie law.
2619      regionalLaw: true,
2620
2621      // countries that enforce some version of a cookie law
2622      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', '
2622SE', 'GB', 'UK'],
2623
2624      // countries that say that all cookie consent choices must be revokable (a user must be able too change their mind)
2625      revokable: ['HR', 'CY', 'DK', 'EE', 'FR', 'DE', 'LV', 'LT', 'NL', 'PT', 'ES'],
2626
2627      // countries that say that a person can only "consent" if the explicitly click on "I agree".
2628      // in these countries, consent cannot be implied via a timeout or by scrolling down the page
2629      explicitAction: ['HR', 'IT', 'ES'],
2630    };
2631
2632    function Law(options) {
2633      this.initialise.apply(this, arguments);
2634    }
2635
2636    Law.prototype.initialise = function(options) {
2637      // set options back to default options
2638      util.deepExtend(this.options = {}, defaultOptions);
2639
2640      // merge in user options
2641      if (util.isPlainObject(options)) {
2642        util.deepExtend(this.options, options);
2643      }
2644    };
2645
2646    Law.prototype.get = function(countryCode) {
2647      var opts = this.options;
2648      return {
2649        hasLaw: opts.hasLaw.indexOf(countryCode) >= 0,
2650        revokable: opts.revokable.indexOf(countryCode) >= 0,
2651        explicitAction: opts.explicitAction.indexOf(countryCode) >= 0,
2652      };
2653    };
2654
2655    Law.prototype.applyLaw = function(options, countryCode) {
2656      var country = this.get(countryCode);
2657
2658      if (!country.hasLaw) {
2659        // The country has no cookie law
2660        options.enabled = false;
2661      }
2662
2663      if (this.options.regionalLaw) {
2664        if (country.revokable) {
2665          // We must provide an option to revoke consent at a later time
2666          options.revokable = true;
2667        }
2668
2669        if (country.explicitAction) {
2670          // The user must explicitly click the consent button
2671          options.dismissOnScroll = false;
2672          options.dismissOnTimeout = false;
2673        }
2674      }
2675      return options;
2676    };
2677
2678    return Law;
2679  }());
2680
2681  // This function initialises the app by combining the use of the Popup, Locator and Law modules
2682  // You can string together these three modules yourself however you want, by writing a new function.
2683  cc.initialise = function(options, complete, error) {
2684    var law = new cc.Law(options.law);
2685
2686    if (!complete) complete = function() {};
2687    if (!error) error = function() {};
2688
2689    cc.getCountryCode(options, function(result) {
2690      // don't need the law or location options anymore
2691      delete options.law;
2692      delete options.location;
2693
2694      if (result.code) {
2695        options = law.applyLaw(options, result.code);
2696      }
2697
2698      complete(new cc.Popup(options));
2699    }, function(err) {
2700      // don't need the law or location options anymore
2701      delete options.law;
2702      delete options.location;
2703
2704      error(err, new cc.Popup(options));
2705    });
2706  };
2707
2708  // This function tries to find your current location. It either grabs it from a hardcoded option in
2709  // `options.law.countryCode`, or attempts to make a location service request. This function accepts
2710  // options (which can configure the `law` and `location` modules) and fires a callback with which
2711  // passes an object `{code: countryCode}` as the first argument (which can have undefined properties)
2712  cc.getCountryCode = function(options, complete, error) {
2713    if (options.law && options.law.countryCode) {
2714      complete({
2715        code: options.law.countryCode
2716      });
2717      return;
2718    }
2719    if (options.location) {
2720      var locator = new cc.Location(options.location);
2721      locator.locate(function(serviceResult) {
2722        complete(serviceResult || {});
2723      }, error);
2724      return;
2725    }
2726    complete({});
2727  };
2728
2729  // export utils (no point in hiding them, so we may as well expose them)
2730  cc.utils = util;
2731
2732  // prevent this code from being run twice
2733  cc.hasInitialised = true;
2734
2735  window.cookieconsent = cc;
2736
2737}(window.cookieconsent || {}));
2738;
2739(function ($) {
2740  var initialized = false;
2741  Drupal.behaviors.dd_sitewide_message_notification = function (context) {
2742    // stop from running again, if accidently included more than once.
2743    if (initialized) {
2744      return;
2745    }
2746    const externalCss = $('<link>').attr('type','text/css').attr('rel', 'stylesheet').attr('href', '//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.css');
2747    $('body').append(externalCss);
2748
2749    // Check if we have more than one message
2750    // Wrap Message into custom class
2751    if (Drupal.settings && Drupal.settings.dd_sitewide_message_notification) {
2752      cookieconsent.all_options = [];
2753      // For all major browsers, except IE 8 and earlier.
2754      if (window.addEventListener) {
2755        window.addEventListener("load", buildNotificationMessages());
2756      } else if (window.attachEvent) {
2757        window.attachEvent("load", buildNotificationMessages());
2758      }
2759    }
2760
2761    // Builds all messages and stack them in correct order.
2762    function buildNotificationMessages() {
2763      const options = Drupal.settings.dd_sitewide_message_notification;
2764      const id = options.dd_sitewide_message_mid;
2765      if (typeof(id) === 'object') {
2766        id.map(function(value, index) {
2767          print_message(index, options);
2768        });
2769      } else {
2770        print_message(-1, options);
2771      }
2772      // stacking of elements
2773      setTimeout(function(){
2774
2775        var bottoms = $('.cc-window.cc-bottom').not('.cc-invisible').not('.cc-left').not('.cc-right');
2776        var bottomLefts = $('.cc-window.cc-bottom.cc-left').not('.cc-invisible');
2777        var bottomRights = $('.cc-window.cc-bottom.cc-right').not('.cc-invisible');
2778        var tops = $('.cc-window.cc-top').not('.cc-invisible').not('.cc-left').not('.cc-right');
2779
2780        moveUp(bottoms);
2781        moveUpAll(bottomRights, heightOfAll(bottoms));
2782        moveUpAll(bottomLefts, heightOfAll(bottoms));
2783        moveUp(bottomRights);
2784        moveUp(bottomLefts);
2785        moveDown(tops);
2786        // Fade in effect when displayed.
2787        $('.cc-window').not('.cc-invisible').css('opacity', 1);
2788        // Set up fade in effect on click.
2789        $('.cc-window').not('.cc-invisible').find('.cc-compliance a').click(function () {
2790            $(this).parents('.cc-window').css('opacity', 0);
2791          }
2792        );
2793      }, 210);
2794    }
2795
2796    function moveUp($elements) {
2797      if ($elements.length == 0) {
2798        return;
2799      }
2800      const heightFirst = heightOfAll($elements.eq(0));
2801      var $newElements = $elements.slice(1);
2802      $newElements.each(function () {
2803        $(this).animate({
2804          'marginBottom' : '+=' + heightFirst + 'px',
2805        });
2806      });
2807      moveUp($newElements);
2808    }
2809
2810    function moveUpAll($elements, $height) {
2811      if ($elements.length == 0) {
2812        return;
2813      }
2814      $elements.animate({
2815        'marginBottom' : '+=' + $height + 'px', //moves right
2816      });
2817    }
2818
2819    function moveDown($elements) {
2820      if ($elements.length == 0) {
2821        return;
2822      }
2823      const heightFirst = heightOfAll($elements.eq(0));
2824      var $newElements = $elements.slice(1);
2825      $newElements.each(function () {
2826        $(this).animate({
2827          'marginTop' : '+=' + heightFirst + 'px',
2828        });
2829      });
2830      moveDown($newElements);
2831    }
2832
2833    function heightOfAll($elements) {
2834      var $height = 0;
2835      $elements.each(function () {
2836        $height += parseInt($(this).height());
2837        $height += parseInt($(this).css('padding-bottom'), 10) + parseInt($(this).css('padding-top'), 10);
2838      });
2839      return $height;
2840    }
2841
2842    // Print a message.
2843    function print_message($index, $options) {
2844      if ($index < 0) {
2845        var mid = $options.dd_sitewide_message_mid;
2846        var message_position = $options.dd_sitewide_message_position;
2847        var message_link = $options.dd_sitewide_message_link;
2848        var message_link_text = $options.dd_sitewide_message_link_text;
2849        var message_text = $options.dd_sitewide_message_text;
2850        var text_color = $options.dd_sitewide_message_text_color;
2851        var bg_color = $options.dd_sitewide_message_bg_color;
2852        var button_color = $options.dd_sitewide_message_button_color;
2853        var button_text_color = $options.dd_sitewide_message_button_text_color;
2854        var dismiss = $options.dd_sitewide_message_dismiss_text;
2855        var target = $options.dd_sitewide_message_link_target;
2856        var cookie_name = 'cookieconsent_status_' + mid;
2857        var cookie_version = $options.dd_sitewide_message_cookie_version;
2858      } else {
2859        var mid = $options.dd_sitewide_message_mid[$index];
2860        var message_position = $options.dd_sitewide_message_position[$index];
2861        var message_link = $options.dd_sitewide_message_link[$index];
2862        var message_link_text = $options.dd_sitewide_message_link_text[$index];
2863        var message_text = $options.dd_sitewide_message_text[$index];
2864        var text_color = $options.dd_sitewide_message_text_color[$index];
2865        var bg_color = $options.dd_sitewide_message_bg_color[$index];
2866        var button_color = $options.dd_sitewide_message_button_color[$index];
2867        var button_text_color = $options.dd_sitewide_message_button_text_color[$index];
2868        var dismiss = $options.dd_sitewide_message_dismiss_text[$index];
2869        var target = $options.dd_sitewide_message_link_target[$index];
2870        var cookie_name = 'cookieconsent_status_' + mid;
2871        var cookie_version = $options.dd_sitewide_message_cookie_version[$index];
2872      }
2873
2874      var palette = {
2875        'palette': {
2876          'popup': {
2877            'background': bg_color,
2878            'text': text_color,
2879          },
2880          'button': {
2881            'background': button_color,
2882            'text': button_text_color,
2883          },
2884        },
2885        // 'showLink': !!(message_link_text.length) && !!(message_link.length),
2886        'position': message_position,
2887        'content': {
2888          'message': message_text,
2889          'href': message_link,
2890          'link': message_link_text,
2891          'dismiss': dismiss,
2892          'target': target,
2893          'version': mid,
2894        },
2895        'cookie': {
2896          'name': cookie_name,
2897          'expiryDays': 365,
2898          'cookieVersion': cookie_version,
2899        },
2900      };
2901
2902      if (button_color === bg_color) {
2903        palette['palette']['button']['border'] = button_text_color;
2904      }
2905
2906      cookieconsent.all_options.push(palette);
2907      const k = new cookieconsent.Popup(palette);
2908      $(k.element).addClass(cookie_name);
2909      $(k.element).attr('aria-describedby', 'cookieconsent:'+ mid +':desc');
2910      if ($(k.element).find('.cc-btn').length != 0) {
2911        $(k.element).find('.cc-btn')[0].setAttribute('data-index', $index);
2912      }
2913
2914      $( '.' + cookie_name + ' .cc-btn' ).hover(
2915        function () {
2916          $(this).css({'color': button_color, 'background-color': button_text_color});
2917        },
2918        function () {
2919          $(this).css({'color': button_text_color, 'background-color': button_color});
2920        });
2921      return k;
2922    }
2923    initialized = true;
2924  };
2925}(jQuery));
2926;
2927
2928Drupal.behaviors.ddAnalytics = function(context) {
2929  // Ensure global _gaq Google Analytics queue has been initialized.
2930  var _gaq = _gaq || [];
2931
2932  // Track when video lightboxes are opened
2933  $('a.video-lightframe').click(function() {
2934    var title = $(this).parents('.node').find('.title').html();
2935    _gaq.push(['_trackEvent', 'Videos', 'Open', title]);
2936  });
2937};
2938
2939/**
2940 * @fileoverview A simple script to automatically track Facebook and Twitter
2941 * buttons using Google Analytics social tracking feature.
2942 * @author [email protected] (Nick Mihailovski)
2943 */
2944
2945/**
2946 * Namespace.
2947 * @type {Object}.
2948 */
2949var _ga = _ga || {};
2950
2951/**
2952 * Ensure global _gaq Google Analytics queue has been initialized.
2953 * @type {Array}
2954 */
2955var _gaq = _gaq || [];
2956
2957/**
2958 * Helper method to track social features. This assumes all the social
2959 * scripts / apis are loaded synchronously. If they are loaded async,
2960 * you might need to add the nextwork specific tracking call to the
2961 * a callback once the network's script has loaded.
2962 * @param {string} opt_pageUrl An optional URL to associate the social
2963 *     tracking with a particular page.
2964 * @param {string} opt_trackerName An optional name for the tracker object.
2965 */
2966_ga.trackSocial = function(opt_pageUrl, opt_trackerName) {
2967  _ga.trackFacebook(opt_pageUrl, opt_trackerName);
2968  _ga.trackTwitter(opt_pageUrl, opt_trackerName);
2969};
2970
2971/**
2972 * Tracks Facebook likes, unlikes and sends by suscribing to the Facebook
2973 * JSAPI event model. Note: This will not track facebook buttons using the
2974 * iFrame method.
2975 * @param {string} opt_pageUrl An optional URL to associate the social
2976 *     tracking with a particular page.
2977 * @param {string} opt_trackerName An optional name for the tracker object.
2978 */
2979_ga.trackFacebook = function(opt_pageUrl, opt_trackerName) {
2980  var trackerName = _ga.buildTrackerName_(opt_trackerName);
2981  try {
2982    if (FB && FB.Event && FB.Event.subscribe) {
2983      FB.Event.subscribe('edge.create', function(targetUrl) {
2984        _gaq.push([trackerName + '_trackSocial', 'facebook', 'like',
2985          targetUrl, opt_pageUrl]);
2986      });
2987      FB.Event.subscribe('edge.remove', function(targetUrl) {
2988        _gaq.push([trackerName + '_trackSocial', 'facebook', 'unlike',
2989          targetUrl, opt_pageUrl]);
2990      });
2991      FB.Event.subscribe('message.send', function(targetUrl) {
2992        _gaq.push([trackerName + '_trackSocial', 'facebook', 'send',
2993          targetUrl, opt_pageUrl]);
2994      });
2995    }
2996  } catch (e) {}
2997};
2998
2999/**
3000 * Returns the normalized tracker name configuration parameter.
3001 * @param {string} opt_trackerName An optional name for the tracker object.
3002 * @return {string} If opt_trackerName is set, then the value appended with
3003 *     a . Otherwise an empty string.
3004 * @private
3005 */
3006_ga.buildTrackerName_ = function(opt_trackerName) {
3007  return opt_trackerName ? opt_trackerName + '.' : '';
3008};
3009
3010/**
3011 * Tracks everytime a user clicks on a tweet button from Twitter.
3012 * This subscribes to the Twitter JS API event mechanism to listen for
3013 * clicks coming from this page. Details here:
3014 * http://dev.twitter.com/pages/intents-events#click
3015 * This method should be called once the twitter API has loaded.
3016 * @param {string} opt_pageUrl An optional URL to associate the social
3017 *     tracking with a particular page.
3018 * @param {string} opt_trackerName An optional name for the tracker object.
3019 */
3020_ga.trackTwitter = function(opt_pageUrl, opt_trackerName) {
3021  var trackerName = _ga.buildTrackerName_(opt_trackerName);
3022  try {
3023    if (twttr && twttr.events && twttr.events.bind) {
3024      twttr.events.bind('tweet', function(event) {
3025        if (event) {
3026          var targetUrl; // Default value is undefined.
3027          if (event.target && event.target.nodeName == 'IFRAME') {
3028            targetUrl = _ga.extractParamFromUri_(event.target.src, 'url');
3029          }
3030          _gaq.push([trackerName + '_trackSocial', 'twitter', 'tweet',
3031            targetUrl, opt_pageUrl]);
3032        }
3033      });
3034    }
3035  } catch (e) {}
3036};
3037
3038/**
3039 * Extracts a query parameter value from a URI.
3040 * @param {string} uri The URI from which to extract the parameter.
3041 * @param {string} paramName The name of the query paramater to extract.
3042 * @return {string} The un-encoded value of the query paramater. underfined
3043 *     if there is no URI parameter.
3044 * @private
3045 */
3046_ga.extractParamFromUri_ = function(uri, paramName) {
3047  if (!uri) {
3048    return;
3049  }
3050  var uri = uri.split('#')[0];  // Remove anchor.
3051  var parts = uri.split('?');  // Check for query params.
3052  if (parts.length == 1) {
3053    return;
3054  }
3055  var query = decodeURI(parts[1]);
3056
3057  // Find url param.
3058  paramName += '=';
3059  var params = query.split('&');
3060  for (var i = 0, param; param = params[i]; ++i) {
3061    if (param.indexOf(paramName) === 0) {
3062      return unescape(param.split('=')[1]);
3063    }
3064  }
3065  return;
3066};
3067;

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.