1extend(saffire, { 2 widgets: { 3 sponsors: function(el, opts) { 4 var $el = el; 5 var widget = {}; 6 var defaults = { 7 bw: false, 8 contentType: 'application/json; charset=utf-8', 9 dataType: 'json', 10 legacy: true, 11 rotate: true, 12 timePerSponsor: 2.5, 13 trackClick: true, 14 imageWidthRestriction: 600, 15 defaultDirection: 'left', 16 playPauseButtons: true, 17 sponsorWidgetLoaded: function(data, hasData) {} 18 }; 19 20 opts = extend(defaults, opts); 21 22 /** 23 * Retreives data for the widget. 24 * @protected 25 */ 26 widget.data = saffire.utils.getWidgetData(opts); 27 widget.data.then(function(res) { 28 widget.items = res.Sponsors || res.Slides; 29 (widget.items.length < 1) ? widget.noData(res) : widget.setup(res); 30 }); 31 32 /** 33 * Sets up the HTML for the Widget. 34 * @protected 35 */ 36 widget.setup = function(data) { 37 $el.addClass('loading'); 38 var sponsorTitle = data.SponsorText || data.WidgetModuleItemDisplayName || null; 39 var majorSponsorTitle = data.MajorSponsorText || null; 40 var optionClass = !opts.rotate ? ' never-animate' : ''; 41 42 // convert image links to SSL if the page is encrypted 43 PAGESSL ? convertSources() : null; 44 45 // initialize Major sponsor counter 46 widget.majorSponsors = 0; 47 48 // build contatiners
49 widget.$container = $('<div class="sponsorWidgetContainer'+ optionClass +'"/>'); 50 widget.$logoContainer = $('<div class="sponsorCarouselImageContainer"/>'); 51 widget.$majorSponsorContainer = $('<div class="majorSponsor"/>'); 52 53 // if this is a legacy client then put a container in the DOM to simply get it's css height (yuck) 54 opts.legacy ? $el.append('<div id="sponsorCarouselContainer" class="sponsorCarouselContainer" style="display:none"/>') : null; 55 56 // build the arrows 57 widget.$leftArrow = $('<span class="sponsorArrow" id="sponsorArrowLeft" role="button" tabindex="0" aria-disabled="true" aria-label="Change sponsor scroll direction - left"><span></span></span>'); 58 widget.$rightArrow = $('<span class="sponsorArrow" id="sponsorArrowRight" role="button" tabindex="0" aria-disabled="false" aria-label="Change sponsor scroll direction - right"><span></span></span>'); 59 60 // Iterate through the data and build image HTML for each sponsor 61 for (var i = 0; i < widget.items.length; i++) { 62 var $sponsor = widget.buildSponsor(widget.items[i]); 63 if (widget.items[i].IsMajorSponsor) { 64 widget.$majorSponsorContainer.append($sponsor); 65 widget.majorSponsors++; 66 } else { 67 widget.$logoContainer.append($sponsor); 68 } 69 } 70 71 // Add titles to the widget if they are present 72 if (sponsorTitle) { 73 widget.sponsorTitle = $('<div id="sponsorsTitleContainer"><h1 class="homepageh1">'+ sponsorTitle +'</h1></div>'); 74 $el.append(widget.sponsorTitle); 75 } 76 if (widget.majorSponsors > 0 && majorSponsorTitle) { 77 widget.majorSponsorTitle = $('<div id="majorSponsorTitleContainer"><h1 class="homepageh1">'+ majorSponsorTitle +'</h1></div>'); 78 // widget.$majorSponsorContainer.prepend(widget.majorSponsorTitle); 79 $el.append(widget.majorSponsorTitle); 80 } 81 82 // Add the sponsors to the DOM 83 (widget.majorSponsors > 0) ? $el.append(widget.$majorSponsorContainer) : $el.addClass('no-major-sponsor'); 84 widget.$container.append(widget.$logoContainer); 85 opts.rotate ? widget.$container.append(widget.$logoContainer.clone()) : null; 86 $el.append(widget.$container); 87 $el.append(widget.$leftArrow).append(widget.$rightArrow); 88 89 // Add play / pause buttons for animation, (for ADA requirements) 90 if (opts.playPauseButtons) { 91 widget.$container.prepend('<div class="sponsor-controls-container"><button type="button" class="sponsor-playpause-btn" tabindex="0" aria-live="assertive"></button></div>'); 92 } 93 94 // initialize widget 95 widget.initialize(); 96 } 97 98 /** 99 * initalzes width calculations, click events, and starts animations 100 * @public 101 */ 102 widget.initialize = function() { 103 var imgs = $el.find('.logo-image'); 104 var count = widget.items.length; 105 widget.direction = widget.direction || opts.defaultDirection; 106 107 //set the direction for the widget 108 widget.$container.addClass('animate-' + widget.direction); 109 110 // attach click events to the arrows 111 widget.$leftArrow.on('click keydown', function(event) { 112 if (event.key === 'Enter' || event.type === 'click') { 113 animate('left'); 114 } 115 }); 116 widget.$rightArrow.on('click keydown', function(event) { 117 if (event.key === 'Enter' || event.type === 'click') { 118 animate('right'); 119 } 120 }); 121 122 // attach click events to the sponsor play pause button 123 $('.sponsor-playpause-btn').click(function() { 124 const $playpausebtn = $(this); 125 const $container = widget.$container; 126 127 // Toggle the paused state on the widget container 128 $container.toggleClass('sponsors-paused'); 129 130 // Check if the ticker is paused or playing and update the button's ARIA attributes accordingly 131 if ($container.hasClass('sponsors-paused')) { 132 // If paused 133 $playpausebtn.attr('aria-label', 'Resume the sponsor ticker'); 134 $playpausebtn.attr('aria-pressed', 'false'); 135 } else { 136 // If playing (not paused) 137 $playpausebtn.attr('aria-label', 'Pause the sponsor ticker'); 138 $playpausebtn.attr('aria-pressed', 'true'); 139 } 140 }); 141 142 // wait until all of the images have loaded befor initializing 143 imgs.load(function() { 144 count--; 145 if (!count) { 146 // images are loaded for this widget 147 // Find the duration of the animation 148 var duration = {
149 '-webkit-animation-duration': opts.timePerSponsor * widget.items.length + 's', 150 'animation-duration': opts.timePerSponsor * widget.items.length + 's' 151 } 152 153 widget.$container.css(duration); 154 155 // done loading... 156 $el.removeClass('loading'); 157 158 // find DOM widths and start animationLogic 159 widget.findWidths(); 160 // wait 500ms and check once more 161 setTimeout(function () { 162 widget.findWidths(); 163 }, 500); 164 165 opts.sponsorsInitialized ? opts.sponsorsInitialized() : null; 166 opts.sponsorWidgetLoaded(widget, true); // callback if needed for the constructor 167 168 169 // Set up the intersection observer 170 const observer = new IntersectionObserver(function (entries, observer) { 171 entries.forEach(function (entry) { 172 const $link = $(entry.target).closest('a'); // Find the closest <a> element 173 174 if (entry.isIntersecting) { 175 // When image is in view, make it tabbable 176 $link.attr('tabindex', '0'); 177 } else { 178 // When image is out of view, remove it from the tab order 179 $link.attr('tabindex', '-1'); 180 } 181 }); 182 }, { threshold: 0.5 }); // 50% of the image should be in view to be tabbable 183 184 // Select all image links in the carousel and observe each one 185 $('.sponsorCarouselImageContainer a').each(function () { 186 observer.observe(this); // Observe each <a> element 187 }); 188 189 } 190 }).filter(function() {return this.complete;}).load(); 191 192 } 193 194 /** 195 * finds the width of the widget and sponsorCarouselImageContainer 196 * @protected 197 */ 198 widget.findWidths = function() { 199 widget.width = $el.width(); 200 widget.sponsorsWidth = widget.$logoContainer.width(); 201 opts.rotate ? animationLogic() : null; 202 } 203 204 /** 205 * Determines if the wdiget should animate or not 206 * @protected 207 */ 208 function animationLogic() { 209 const $playpausebtn = widget.$container.find('.sponsor-playpause-btn'); 210 if ( widget.sponsorsWidth >= widget.width ) { 211 widget.$container.addClass('animate'); 212 $playpausebtn.attr('aria-label', 'Pause the sponsor ticker'); 213 $playpausebtn.attr('aria-pressed', 'true'); 214 } else { 215 widget.$container.removeClass('animate'); 216 $playpausebtn.attr('aria-label', 'Resume the sponsor ticker'); 217 $playpausebtn.attr('aria-pressed', 'false'); 218 } 219 } 220 221 /** 222 * Arrow movement function 223 * @public 224 */ 225 function animate(direction) { 226 if (widget.direction !== direction) { 227 widget.$container.addClass('changing-direction'); 228 setTimeout(function(){ 229 widget.$container.removeClass('animate-' + widget.direction) 230 widget.$container.removeClass('changing-direction'); 231 widget.direction = direction; 232 widget.$container.addClass('animate-' + widget.direction); 233 234 if (widget.direction == 'left') { 235 $('#sponsorArrowLeft').attr('aria-disabled', 'true'); 236 $('#sponsorArrowRight').attr('aria-disabled', 'false'); 237 } else { 238 $('#sponsorArrowLeft').attr('aria-disabled', 'false'); 239 $('#sponsorArrowRight').attr('aria-disabled', 'true'); 240 } 241 }, 500); 242 } 243 } 244 245 246 /** 247 * Constructs each image and container. 248 * @protected 249 */ 250 widget.buildSponsor = function (sponsorData) { 251 var link = sponsorData.URL || null; 252 var spanClass = sponsorData.IsMajorSponsor ? 'majorSponsorImage' : 'sponsorCarouselImage'; 253 var spanID = sponsorData.IsMajorSponsor && opts.legacy ? 'id="majorSponsorImageContainer"' : ''; 254 // var target = sponsorData.URLOpenInANewWindow ? '_blank' : '_self'; // URLOpenInANewWindow not in widget data or option in spark? *shrug* 255 var target = '_blank'; 256 var title = sponsorData.Name || sponsorData.Title; 257 var imageAlternateText = ""; 258 259 if (sponsorData.ImageAlternateText != null) { 260 imageAlternateText = (sponsorData.ImageAlternateText); 261 } 262 263 var trackingStr = opts.trackClick ? 'onclick="ga(\'send\', \'event\', {eventCategory: \'Sponsors Link\', eventAction: \'Click\', eventLabel: \'' + title + '\'});"' : ''; 264 265 if (opts.imageWidthRestriction) { 266 sponsorData.ImageWithPath = sponsorData.ImageWithPath.concat('&w='+ opts.imageWidthRestriction); 267 } 268 269 if (opts.legacy) { 270 var height = $el.children('#sponsorCarouselContainer').height() || $el.children('.sponsorCarouselContainer').height(); 271 sponsorData.ImageWithPath = sponsorData.ImageWithPath.concat('&h='+ height); 272 } 273 274 var $image = $('<span ' + spanID + ' class="' + spanClass + '"><img class="logo-image" src="' + sponsorData.ImageWithPath + '&bw=' + opts.bw + '" alt="' + imageAlternateText + '"/></span>'); 275 276 if (link != null) { 277 var sponsName = sponsorData.Name ? sponsorData.Name : ''; 278 var $anchor = $('<a href="' + link + '" aria-label="visit sponsor: '+sponsName+'" target="' + target + '" ' + trackingStr + ' />'); 279 } else { 280 var $anchor = $('<a href="javascript:void(0)" class="no-link" />'); 281 } 282 283 $anchor.append($image); 284 return $anchor; 285 } 286 287 288 /** 289 * Converts the image links in widget.items to be SSL call. 290 * @protected 291 */ 292 function convertSources() { 293 for (var i = 0; i < widget.items.length; i++) { 294 widget.items[i].ImageWithPath = widget.items[i].ImageWithPath.replace(/^http:\/\//i,'https://'); 295 } 296 } 297 298 299 /** 300 * There were'nt any sponsors 301 * @protected 302 */ 303 widget.noData = function(data) { 304 var id = $el.attr('id'); 305 console.log("Service returned no data for " + id, data); 306 $('body').addClass(id + '-no-data'); 307 opts.sponsorWidgetLoaded(widget, false); 308 }; 309 310 /** 311 * Handle resize event 312 * @protected 313 */ 314 if (opts.rotate) { 315 window.addEventListener("resize", sponsorResize); 316 }; 317 318 function sponsorResize() { 319 widget.findWidths(); 320 }; 321 322 323 /** 324 * public API. 325 * @public 326 */ 327 return { 328 a: function() { 329 alert($el); 330 }, 331 data: widget.data, 332 moveLeft: function() { 333 animate('left'); 334 }, 335 moveRight: function() { 336 animate('right'); 337 }, 338 options: opts, 339 reinitialize: function() { 340 widget.initialize(); 341 } 342 } 343 344 } 345 346 } 347});
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.