1/* 2 * ADOBE CONFIDENTIAL 3 * 4 * Copyright 2016 Adobe Systems Incorporated 5 * All Rights Reserved. 6 * 7 * NOTICE: All information contained herein is, and remains 8 * the property of Adobe Systems Incorporated and its suppliers, 9 * if any. The intellectual and technical concepts contained 10 * herein are proprietary to Adobe Systems Incorporated and its 11 * suppliers and may be covered by U.S. and Foreign Patents, 12 * patents in process, and are protected by trade secret or copyright law. 13 * Dissemination of this information or reproduction of this material 14 * is strictly forbidden unless prior written permission is obtained 15 * from Adobe Systems Incorporated. 16 */ 17(function () { 18 'use strict'; 19 20 var COMP_SELECTOR = '.s7dm-dynamic-media'; 21 var VIEWER_JS = { 22 BasicZoomViewer: 'html5/js/BasicZoomViewer.js', 23 FlyoutViewer: 'html5/js/FlyoutViewer.js', 24 MixedMediaViewer: 'html5/js/MixedMediaViewer.js', 25 SpinViewer: 'html5/js/SpinViewer.js', 26 VideoViewer: 'html5/js/VideoViewer.js', 27 ZoomViewer: 'html5/js/ZoomViewer.js', 28 ZoomVerticalViewer: 'html5/js/ZoomVerticalViewer.js', 29 Responsive: 'libs/responsive_image.js', 30 }; 31 var responsiveViewerCSS = 's7responsiveViewer'; //defined in responsive.css 32 33 const VideoSize = { 34 Size800 : 800, 35 Size1050 : 1050, 36 Size1080 : 1080, 37 } 38 39 const VideoBitrate = { 40 Bitrate730 : 730, 41 Bitrate2000 : 2000, 42 Bitrate3000 : 3000, 43 } 44 45 const VideoBreakpoints = { 46 MaxSmall : 767, 47 MaxTablet : 1199, 48 } 49 50 $(document).ready(function () { 51 //do nothing if there is no component due to no asset yet or DM disabled 52 if ($(COMP_SELECTOR).length > 0) { 53 var viewers = new S7dmUtils.Viewer(); 54 var viewerRootPath = $(COMP_SELECTOR).data('viewer-path'); 55 var viewerType = $(COMP_SELECTOR).data('viewer-type'); 56 var pageLocale = $(COMP_SELECTOR).data('page-locale'); 57 var s7i18nPath = '/libs/dam/components/scene7/common/clientlibs/i18n.js'; 58 if (typeof Granite != 'undefined' && typeof Granite.I18n != 'undefined') { 59 Granite.I18n.setLocale(pageLocale); 60 s7i18nPath = Granite.HTTP.externalize(s7i18nPath); 61 } 62 $('head').append('<script type="text/javascript" src="' + s7i18nPath + '"></script>'); 63 var viewerJs = Object.keys(VIEWER_JS).reduce(function (accumulator, type) { 64 if (viewerType.indexOf(type) !== -1) { 65 accumulator[type] = VIEWER_JS[type]; 66 } 67 return accumulator; 68 }, {}); 69 viewers.load(viewerJs, viewerRootPath).ready({ 70 success: initViewers, 71 fail: failViewerLoader, 72 }); 73 } 74 }); 75 76 const format = ({ videoPath, size, bitRate, contentserver}) => { 77 return videoPath.replace('{height}', size.toString()) 78 .replace('{bitrate}', bitRate.toString()) 79 .replace(contentserver, ''); 80 } 81 82 const getVideoSource = (videoPath, contentserver) => { 83 const width = window.innerWidth; 84 85 if (width <= VideoBreakpoints.MaxSmall) { 86 return format({ 87 videoPath, 88 size: VideoSize.Size800, 89 bitRate: VideoBitrate.Bitrate730, 90 contentserver, 91 }); 92 } 93 94 if (width <= VideoBreakpoints.MaxTablet) { 95 return format({ 96 videoPath, 97 size: VideoSize.Size1050, 98 bitRate: VideoBitrate.Bitrate2000, 99 contentserver, 100 }); 101 } 102 103 return format({ 104 videoPath, 105 size: VideoSize.Size1080, 106 bitRate: VideoBitrate.Bitrate3000, 107 contentserver, 108 }); 109 }; 110 111 /** 112 * Viewer intialization for interactive media component 113 */ 114 function initViewers() { 115 var components = $(COMP_SELECTOR); 116 components.each(function () { 117 setupViewer($(this)); 118 }); 119 } 120 121 /** 122 * Setup viewer based on component 123 * @param component a div component contains viewer config 124 */ 125 function setupViewer(component) { 126 const { assetType, viewerType, mode, stagesize: stageSize, encodesPath } = component.data(); 127 var compId = component.attr('id'); 128 var viewer = S7dmUtils[compId]; 129 130 if (typeof viewer != 'undefined' && viewer != null) { 131 if (viewer.containerId === compId) { 132 return; 133 } 134 135 viewer.dispose(); 136 S7dmUtils[compId] = null; 137 component.removeClass(responsiveViewerCSS); 138 } 139 140 component.html(''); 141 142 if (viewerType) { 143 var viewerParams = buildViewerParams(component);
144 var viewerSettings = { containerId: component.attr('id'), params: viewerParams }; 145 if (!(viewerType == 'FlyoutViewer' && stageSize)) { 146 component.addClass(responsiveViewerCSS); 147 } 148 viewer = new s7viewers[viewerType](viewerSettings); 149 if (typeof Granite != 'undefined' && typeof Granite.I18n != 'undefined') { 150 viewer.setLocalizedTexts(getLocalizedText()); //passing localized text to the viewer 151 } 152 S7dmUtils[compId] = viewer; 153 154 viewer.init(); 155 var videoViewerElement = $('#' + compId); 156 var videoElement ; 157 158 const observer = new MutationObserver(mutationList => { 159 mutationList.filter(m => m.type === 'childList').forEach(m => { 160 m.addedNodes.forEach(node => { 161 if (node.tagName === 'VIDEO') { 162 videoElement = videoViewerElement.find('video'); 163 //inject alt text on video element 164 videoViewerElement.find('video:not([alt])').attr('alt', videoViewerElement.attr('data-alt')); 165 //inject lazy loading on video element 166 videoElement.addClass('lazyload'); 167 videoElement.attr('preload', 'metadata'); 168 videoElement.attr('data-encodes', encodesPath ? 'yes' : 'no'); 169 //inject autoplay on video if applicable 170 if (videoViewerElement.attr('data-autoplay') !== undefined) { 171 videoElement.attr('autoplay', true); 172 videoElement[0].muted = true; 173 } 174 videoElement.attr('data-should-always-play', true); 175 //inject loop on video if applicable 176 videoElement.attr('loop', videoViewerElement.attr('data-loop')); 177 if (!videoElement.find('track').length) { 178 //inject subtitles after source 179 var subtitleSource = videoViewerElement.attr('data-subtitlePath'); 180 var locale = videoViewerElement.attr('data-page-locale'); 181 if (subtitleSource) { 182 $('<track src="' + subtitleSource + '" kind="subtitles" srclang="' + locale + '" default>').insertAfter( 183 videoViewerElement.find('video source') 184 ); 185 } 186 } 187 } 188 }); 189 }); 190 }); 191 observer.observe(videoViewerElement[0], { childList: true, subtree: true }); 192 } else if (assetType === 'image') { 193 if (component.data('breakpoints') || mode == 'smartcrop') { 194 buildResponsiveImage(component); 195 } else { 196 buildImage(component); 197 } 198 } 199 } 200 201 /** 202 * Build Image and anchor when there is no viewer constructor defined for IMAGE ONLY 203 * @param component a div component contains viewer config 204 */ 205 function buildImage(component) { 206 var stageSize = component.data('stagesize'), 207 linkURL = component.data('linkurl'), 208 linkTarget = component.data('linktarget'), 209 title = component.data('title'), 210 altText = component.data('alt'), 211 imagePreset = component.data('imagepreset'), 212 imageServer = component.data('imageserver'), 213 modifiers = component.data('urlmodifiers'); 214 215 var imgSetup = { 216 src: imageServer + component.data('asset-path'), 217 }; 218 if (imagePreset) { 219 imgSetup['src'] += '?$' + imagePreset + '$'; 220 } 221 if (modifiers) { 222 imgSetup['src'] += (imagePreset ? '&' : '?') + modifiers; 223 } 224 225 if (stageSize) { 226 var stageSizeParts = stageSize.split(','); 227 imgSetup['width'] = stageSizeParts[0]; 228 if (stageSizeParts.length > 1) { 229 imgSetup['height'] = stageSizeParts[1]; 230 } 231 } 232 233 var imgComp = $('<img>', imgSetup); 234 //if there is link, wrap it around image 235 if (linkURL) { 236 var anchorSetup = { 237 href: linkURL, 238 target: linkTarget, 239 }; 240 if (altText) { 241 anchorSetup['alt'] = altText; 242 anchorSetup['title'] = altText; // default to alt text 243 imgComp.attr('alt', altText); 244 } 245 if (title) { 246 anchorSetup['title'] = title; 247 } 248 var anchor = $('<a>', anchorSetup); 249 anchor.append(imgComp); 250 component.append(anchor); 251 } else { 252 //Add alt text if it's set 253 if (altText) { 254 imgComp.attr('alt', altText); 255 } 256 component.append(imgComp); 257 } 258 } 259 260 /** 261 * Build responsive image 262 * @param component container 263 */ 264 function buildResponsiveImage(component) { 265 var imagePreset = component.data('imagepreset'), 266 imageServer = component.data('imageserver'), 267 linkURL = component.data('linkurl'), 268 linkTarget = component.data('linktarget'), 269 title = component.data('title'), 270 altText = component.data('alt'); 271 var modifiers = component.data('urlmodifiers'); 272 var respCon = $('<div>', { class: 's7responsiveContainer ' + component.data('aspectratio') }); 273 var respImgId = component.attr('id') + '_resp'; 274 var respImgSetup = { 275 id: respImgId, 276 src: imageServer + component.data('asset-path') + '?qlt=10', 277 'data-src': imageServer + component.data('asset-path'), 278 class: 'img-fluid', 279 'data-mode': component.data('mode'), 280 'data-breakpoints': component.data('breakpoints'), 281 }; 282 if (imagePreset) { 283 respImgSetup['data-src'] += '?$' + imagePreset + '$'; 284 } 285 if (modifiers) { 286 respImgSetup['data-src'] += (imagePreset ? '&' : '?') + modifiers; 287 } 288 var respImg = $('<img>', respImgSetup); 289 //if there is link, wrap it around image 290 if (linkURL) { 291 var anchorSetup = { 292 href: linkURL, 293 target: linkTarget, 294 }; 295 if (altText) { 296 anchorSetup['alt'] = altText; 297 anchorSetup['title'] = altText; // default to alt text 298 respImg.attr('alt', altText); 299 } 300 if (title) { 301 anchorSetup['title'] = title; 302 } 303 var anchor = $('<a>
303', anchorSetup); 304 anchor.append(respImg); 305 respCon.append(anchor); 306 } else { 307 if (altText) { 308 respImg.attr('alt', altText); 309 } 310 respCon.append(respImg); 311 } 312 component.append(respCon); 313 var isEditor = document.cookie.indexOf('wcmmode=edit') !== -1; 314 315 //Handle lazy loading of Dynamic Image 316 $(respImg).attr('loaded', false); 317 318 var lazyloadImage = function () { 319 var $image = $('#' + respImgId); 320 var alreadyLoaded = $image.attr('loaded'); 321 if (isEditor || visibleDynamicElement(respImgId, alreadyLoaded)) { 322 $image.attr('loaded', true); 323 s7responsiveImage(document.getElementById(respImgId)); 324 } 325 }; 326 327 lazyloadImage(); 328 329 window.addEventListener('scroll', function () { 330 lazyloadImage(); 331 }); 332 333 $(document).on('click', '.nav-link', function () { 334 if ($('#cmp-top-navigation')) { 335 lazyloadImage(); 336 } 337 }); 338 339 //Handle lazy loading of Dynamic Images that are not visible by default (e.g. accordion items) 340 var respondToVisibility = function respondToVisibility(element, callback) { 341 var options = { 342 root: document.documentElement 343 }; 344 var observer = new IntersectionObserver(function (entries) { 345 entries.forEach(function (entry) { 346 callback(entry.intersectionRatio > 0); 347 }); 348 }, options); 349 350 observer.observe(element); 351 }; 352 353 respondToVisibility(document.getElementById(respImgId), function (visible) { 354 if (visible) { 355 lazyloadImage(); 356 } 357 }); 358 } 359 360 /** 361 * @param component a div element with viewer setup 362 * @return viewer parameters in JSON 363 */ 364 function buildViewerParams(component) { 365 var assetPath = component.data('asset-path'), 366 assetType = component.data('asset-type'), 367 viewerType = component.data('viewer-type'), 368 imageserver = component.data('imageserver'), 369 videoserver = component.data('videoserver'), 370 contentserver = component.data('contenturl'), 371 config = component.data('config'), 372 wcmdisabled = component.data('wcmdisabled'), 373 stageSize = component.data('stagesize'), 374 viewerModifiers = component.data('viewermodifiers'), 375 isdms7 = component.data('dms7'), 376 encodesPath = component.data('encodes-path'), 377 videoPath = encodesPath ? getVideoSource(encodesPath, contentserver) : assetPath; 378 379 var viewerParams = { 380 asset: videoPath, 381 serverurl: imageserver, 382 contenturl: contentserver, 383 }; 384 385 //Add extra viewer modifiers if any 386 if (viewerModifiers) { 387 $.extend(viewerParams, getViewerModifiers(viewerModifiers)); 388 } 389 390 var isAuthor = !wcmdisabled && wcmdisabled != ''; 391 392 //Add cache=off when WCM is not disabled (author side) 393 if (isAuthor) { 394 viewerParams['asset'] += '?cache=off'; 395 } 396 if (stageSize) { 397 viewerParams['stagesize'] = stageSize; 398 } 399 if (viewerType == 'FlyoutViewer') { 400 viewerParams['imagereload'] = '1,breakpoint,100;320;480'; 401 } 402 if (assetPath.indexOf('/') != 0) { 403 viewerParams['aemmode'] = '0'; 404 } 405 406 //For video, we add videoserverurl 407 if (viewerType.indexOf('Video') >= 0 || viewerType.indexOf('Mixed') >= 0) { 408 viewerParams['videoserverurl'] = videoserver; 409 // force native if 410 // Asset is non-AVS video (apply to both DMS7 and DM Hybrid for playing progressively) OR 411 // (Asset is DMS7 video regardless of AVS or non-AVS AND the page is on author side) 412 if (assetType == 'video' || (typeof isdms7 != 'undefined' && isAuthor)) { 413 viewerParams['playback'] = 'native'; 414 viewerParams['progressivebitrate'] = '20000'; 415 } 416 } 417 //check for viewer preset setup 418 if (typeof config != 'undefined' && config.length > 0) { 419 viewerParams['config'] = getViewerPresetPath(config); 420 } 421 return viewerParams; 422 } 423 424 /** 425 * Parse viewer preset path for viewer-usable format 426 * @param config viewer preset path stored in the component node 427 * @return viewer-usable format viewer preset path 428 */ 429 function getViewerPresetPath(config) { 430 var configParts = config.split('|'); 431 return configParts[0]; 432 } 433 434 /** 435 * Parse viewer preset path for isCustom Preset flag 436 * @param config viewer preset string 437 * @return flag for custom preset 438 */ 439 function isCustomPreset(config) { 440 var configParts = config.split('|'); 441 return configParts[2]; 442 } 443 444 /** 445 * Fail viewer loader handler 446 */
447 function failViewerLoader() { 448 console.log('S7Viewer is missing'); 449 } 450 451 /** 452 * Parse free-form viewer modifiers 453 * @param viewerModifiers string for free-form viewer modifiers 454 * @return JSON representing viewer modifiers 455 */ 456 function getViewerModifiers(viewerModifiers) { 457 var modifiers = {}; 458 if (viewerModifiers) { 459 var items = viewerModifiers.split('&'); 460 for (var i = 0; i < items.length; i++) { 461 //only when there is key=val pair 462 if (items[i].indexOf('=') > 0) { 463 var key = items[i].substring(0, items[i].indexOf('=')); 464 var val = items[i].substring(items[i].indexOf('=') + 1); 465 modifiers[key] = val; 466 } 467 } 468 } 469 return modifiers; 470 } 471 472 /** 473 * Get localized text for viewer from s7sdk_i18n.localizedText 474 * @return JSON for localized text 475 */ 476 function getLocalizedText() { 477 var localizedText = {}; 478 localizedText[Granite.I18n.getLocale()] = formatLocalizedText(s7sdk_i18n.localizedText); 479 localizedText['defaultLocale'] = Granite.I18n.getLocale(); 480 return localizedText; 481 } 482 483 /** 484 * Format localized text into a correct viewer format 485 * @param localizedText localized text parsed from s7sdk 486 * @return formatted localized text with correct key for viewer 487 */ 488 function formatLocalizedText(localizedText) { 489 var formatTexts = {}; 490 for (var compKey in localizedText) { 491 var compObj = localizedText[compKey]; 492 for (var symbolKey in compObj) { 493 var modKey = compKey + '.' + symbolKey; 494 var symbolVal = compObj[symbolKey]; 495 formatTexts[modKey] = symbolVal; 496 } 497 } 498 return formatTexts; 499 } 500 501 //Check if element is within view 502 function visibleDynamicElement(elementId, alreadyLoaded) { 503 if (alreadyLoaded === 'true') { 504 return false; 505 } 506 var imageOffset = $('#' + elementId).offset().top; 507 var imageOuterHeight = imageOffset + $('#' + elementId).outerHeight(); 508 var imageScrollTop = $(window).scrollTop(); 509 var imageDistance = imageScrollTop + $(window).height(); 510 return imageOuterHeight > imageScrollTop && imageOffset < imageDistance; 511 } 512}.call(this));
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.