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: '❌', 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> ', 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.