1/*global $, jQuery, alert*/ 2 3jQuery(document).ready(function ($) { 4 5 "use strict"; 6 7 window.AsiController = function () { 8 9 var $images = $('.asi-img'); 10 11 /* 12 / Rename asi-audio tags into audio ones. 13 / Param: none 14 */ 15 function renameAudioTags() { 16 $('asi-audio').each(function () { 17 var asi_id = $(this).attr('asi-id'); 18 $(this).replaceWith('<audio asi-id="' + asi_id + '">' + $(this).html() + '</audio>'); 19 }); 20 } 21 22 /* 23 / Set up audio progress tracking (drives the circular indicator via --progress) 24 / Param: none 25 */ 26 function setupProgressTracking() { 27 $('audio').on('timeupdate', function () { 28 var asi_id = $(this).attr('asi-id'); 29 var progress = 0; 30 if (this.duration) { 31 progress = (this.currentTime / this.duration) * 100; 32 } 33 $('.asi-player-icon[asi-id="' + asi_id + '"]').css('--progress', progress + '%'); 34 }); 35 36 $('audio').on('ended', function () { 37 var asi_id = $(this).attr('asi-id'); 38 $('.asi-player-icon[asi-id="' + asi_id + '"]').css('--progress', '0%'); 39 }); 40 } 41 42 /* 43 / Add the icons and style them (modern SVG + size/style classes). 44 / Param: icon_style ( default ) 45 */ 46 function addIcons(icon_style) { 47 48 var playSvg = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8 5v14l11-7z"/></svg>'; 49 var pauseSvg = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>'; 50 51 // Map the 8 historical models onto the 3 style variants 52 var styleClass = ''; 53 switch (icon_style.model) { 54 case 'model-2': 55 case 'model-3': 56 styleClass = 'asi-style-solid'; 57 break; 58 case 'model-4': 59 case 'model-5': 60 styleClass = 'asi-style-minimal'; 61 break; 62 case 'model-6': 63 case 'model-7': 64 case 'model-8': 65 styleClass = 'asi-style-outline'; 66 break; 67 default: 68 styleClass = ''; 69 } 70 71 var sizeClass = ''; 72 switch (icon_style.size) { 73 case 'small': 74 sizeClass = 'asi-size-small'; 75 break; 76 case 'medium': 77 sizeClass = 'asi-size-medium'; 78 break; 79 case 'big': 80 sizeClass = 'asi-size-big'; 81 break; 82 } 83 84 var icon_paused = '<div class="asi-player-icon asi-minus-icon ' + sizeClass + ' ' + styleClass + '">' + playSvg + '</div>'; 85 var icon_playing = '<div class="asi-player-icon asi-plus-icon ' + sizeClass + ' ' + styleClass + '">' + pauseSvg + '</div>'; 86 87 $images.each(function (index, image) { 88 var asi_id = $(this).attr('asi-id'); 89 $(this).attr('asi-id', asi_id); 90 $(icon_paused).insertAfter($(this)).attr('asi-id', asi_id); 91 $(icon_playing).insertAfter($(this)).attr('asi-id', asi_id); 92 }); 93 94 if (icon_style.color) { 95 $('.asi-player-icon svg').css('fill', icon_style.color); 96 } 97 } 98 99 /* 100 / Place the icons 101 / Param: layout object with .position ( center, top-left, top-right, bottom-left, bottom-right ) 102 */ 103 function placeIcons(layout) { 104 var $icons = $('.asi-player-icon'); 105 $icons.each(function (index, icon) { 106 107 var asi_id = $(this).attr('asi-id'), 108 $linked_image = $('.asi-img[asi-id=' + asi_id + ']'), 109 110 img_height = $linked_image.height(), 111 img_width = $linked_image.width(), 112 113 img_top = $linked_image.position().top, 114 img_left = $linked_image.position().left, 115 116 padding_vertical = 15, 117 padding_horizontal = 15, 118 119 icon_size = $(this).outerWidth(), 120 121 icon_top = img_top + padding_vertical, 122 icon_bottom = img_top + img_height - padding_horizontal - icon_size, 123 icon_left = img_left + padding_horizontal, 124 icon_right = img_left + img_width + padding_vertical, 125 icon_center_top = img_top + img_height / 2 - icon_size / 2, 126 icon_center_left = img_left + img_width / 2 - icon_size / 2; 127 128 switch (layout.position) { 129 case 'center': 130 $(this).css({ 131 top: icon_center_top, 132 left: icon_center_left 133 }); 134 break; 135 136 case 'top-left': 137 $(this).css({ 138 top: icon_top, 139 left: icon_left 140 }); 141 break; 142 143 case 'top-right': 144 $(this).css({ 145 top: icon_top, 146 right: icon_right 147 }); 148 break; 149 150 case 'bottom-left': 151 $(this).css({ 152 top: icon_bottom, 153 left: icon_left 154 }); 155 break; 156
157 case 'bottom-right': 158 $(this).css({ 159 top: icon_bottom, 160 right: icon_right 161 }); 162 break; 163 } 164 }); 165 } 166 167 /* 168 / Play or pause a sound 169 / Param: asi_id ( integer ) 170 */ 171 this.toggleAudio = function (asi_id) { 172 var $icon_minus = $('.asi-minus-icon[asi-id=' + asi_id + ']'), 173 $icon_plus = $('.asi-plus-icon[asi-id=' + asi_id + ']'), 174 $audio = $('audio[asi-id=' + asi_id + ']'); 175 176 if (!$audio.hasClass('playing')) { 177 $('audio').trigger('pause').removeClass('playing'); 178 $('.asi-plus-icon').hide(); 179 $('.asi-minus-icon').show().css('display', 'flex'); 180 $('.asi-player-icon').css('--progress', '0%'); 181 182 $audio.trigger("play"); 183 $icon_minus.hide(); 184 $icon_plus.show().css('display', 'flex'); 185 $audio.addClass('playing'); 186 } else { 187 $audio.trigger("pause"); 188 $icon_minus.show().css('display', 'flex'); 189 $icon_plus.hide(); 190 $audio.removeClass('playing'); 191 } 192 }; 193 194 /* 195 / Initialize autoplay functionality using Intersection Observer 196 / Param: none 197 */ 198 function initAutoplay() { 199 200 if (!window.IntersectionObserver) { 201 return; 202 } 203 204 var $autoplayImages = $('.asi-img[asi-autoplay="true"]'); 205 206 if ($autoplayImages.length === 0) { 207 return; 208 } 209 210 var observer = new IntersectionObserver(function (entries) { 211 entries.forEach(function (entry) { 212 if (entry.isIntersecting) { 213 var $img = $(entry.target); 214 var asi_id = $img.attr('asi-id'); 215 var $audio = $('audio[asi-id=' + asi_id + ']'); 216 217 if ($img.attr('asi-autoplay') === 'true' && !$audio.hasClass('playing')) { 218 setTimeout(function () { 219 $('audio').trigger('pause').removeClass('playing'); 220 $('.asi-plus-icon').hide(); 221 $('.asi-minus-icon').show().css('display', 'flex'); 222 223 var playPromise = $audio[0].play(); 224 225 if (playPromise !== undefined) { 226 playPromise.then(function () { 227 var $icon_minus = $('.asi-minus-icon[asi-id=' + asi_id + ']'); 228 var $icon_plus = $('.asi-plus-icon[asi-id=' + asi_id + ']'); 229 $icon_minus.hide(); 230 $icon_plus.show().css('display', 'flex'); 231 $audio.addClass('playing'); 232 }).catch(function (error) { 233 var $img = $('.asi-img[asi-id=' + asi_id + ']'); 234 var $icon = $('.asi-minus-icon[asi-id=' + asi_id + ']'); 235 236 $icon.addClass('asi-autoplay-ready'); 237 238 $img.attr('data-autoplay-attempted', 'true'); 239 240 if (!$('body').hasClass('asi-autoplay-notice-shown')) { 241 $('body').addClass('asi-autoplay-notice-shown'); 242 243 var $notice = $('<div class="asi-autoplay-notice">' + 244 '<div class="asi-autoplay-notice-content">' + 245 '<span class="asi-autoplay-notice-icon">ð</span>' + 246 '<span class="asi-autoplay-notice-text">Click anywhere to enable audio</span>' + 247 '</div>' + 248 '</div>'); 249 250 $('body').append($notice); 251 252 setTimeout(function () { 253 $notice.addClass('asi-autoplay-notice-visible'); 254 }, 100); 255 256 var removeNotice = function () { 257 $notice.removeClass('asi-autoplay-notice-visible'); 258 setTimeout(function () { 259 $notice.remove(); 260 }, 300); 261 }; 262 263 $(document).one('click touchstart', removeNotice); 264 setTimeout(removeNotice, 5000); 265 } 266 }); 267 } 268 }, 200); 269 } 270 } 271 }); 272 }, { 273 threshold: 0.5, 274 rootMargin: '0px 0px -50px 0px' 275 }); 276 277 $autoplayImages.each(function () { 278 observer.observe(this); 279 }); 280 281 // Recover from browser autoplay block: once the user interacts, play the queued ones. 282 $(document).one('click touchstart', function () { 283 $('.asi-img[data-autoplay-attempted="true"]').each(function () { 284 var $img = $(this); 285 var asi_id = $img.attr('asi-id'); 286 var $audio = $('audio[asi-id=' + asi_id + ']'); 287 288 if ($img[0].getBoundingClientRect().top < window.innerHeight && !$audio.hasClass('playing')) { 289 $audio[0].play().then(function () { 290 var $icon_minus = $('.asi-minus-icon[asi-id=' + asi_id + ']'); 291 var $icon_plus = $('.asi-plus-icon[asi-id=' + asi_id + ']'); 292 $icon_minus.hide().removeClass('asi-autoplay-ready'); 293 $icon_plus.show().css('display', 'flex'); 294 $audio.addClass('playing'); 295 $img.removeAttr('data-autoplay-attempted'); 296 }).catch(function (error) {
297 // Still blocked â give up silently. 298 }); 299 } 300 }); 301 }); 302 } 303 304 this.init = function (options) { 305 var layout = options.layout, 306 icon_style = options.icon_style; 307 308 renameAudioTags(); 309 addIcons(icon_style); 310 placeIcons(layout, icon_style); 311 setupProgressTracking(); 312 initAutoplay(); 313 }; 314 315 this.placeIcons = placeIcons; 316 317 }; 318 319});
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.