1redesign.define('productPartial/pdp_productThumbnailButton',['handlebars','dsPartial/thumbnail'], function(Handlebars) { 2 3this["ANF"] = this["ANF"] || {}; 4this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 5 6Handlebars.registerPartial("pdp_productThumbnailButton", this["ANF"]["templates"]["pdp_productThumbnailButton"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 7 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 8 9 return " <button class=\"product-thumbnail-button\" tabindex=\"-1\" data-image-type=\"" 10 + alias2(alias1((depth0 != null ? depth0.imageType : depth0), depth0)) 11 + "\" data-image-id=\"" 12 + alias2(alias1((depth0 != null ? depth0.imageId : depth0), depth0)) 13 + "\">\n" 14 + ((stack1 = container.invokePartial(partials.thumbnail,depth0,{"name":"thumbnail","hash":{"selectedText":"Selected","selected":(depth0 != null ? depth0.selected : depth0),"alt":(depth0 != null ? depth0.alt : depth0),"data-src":(depth0 != null ? depth0["data-src"] : depth0)},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 15 + " </button>\n"; 16},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 17 var stack1; 18 19 return "<div class=\"product-thumbnails slider js-slider js-thumbnails thumbnail-slider\" style=\"display:none;\">\n" 20 + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.thumbnails : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 21 + "</div>"; 22},"usePartial":true,"useData":true})); 23 24return this["ANF"]; 25 26}); 27/* 28* Usage 29* To get individual segments 30* segment = xSeg.get(xSeg.XSEG_GLOBAL); 31* 32* To check if the segment's page is a value 33* var isInPageTest = xSeg.checkPageSegment(xSeg.XSEG_GLOBAL, "A"); 34* 35* To check if the segment's module is a value 36* var isInModuleTest = xSeg.checkModuleSegment(xSeg.XSEG_GLOBAL, "1"); 37*/ 38redesign.define('coreServices/xSeg',['anfUtil/testFramework', 'jsCookie'], function (test, cookies) { 39 'use strict'; 40 41 var partRegex = new RegExp(/([a-zA-Z]+)=([a-zA-Z]+)-(\d+)/); 42 var nullValue = Object.create(Object.prototype, { 43 'page': { 44 value: null, 45 writable: false 46 }, 47 'module': { 48 value: null, 49 writable: false 50 } 51 }); 52 var constants = [ 53 { 54 name: 'XSEG_BAG', 55 value: 'b' 56 }, 57 { 58 name: 'XSEG_CATALOG', 59 value: 'c' 60 }, 61 { 62 name: 'XSEG_CHECKOUT', 63 value: 'co' 64 }, 65 { 66 name: 'XSEG_CUSTOMER_SERVICE', 67 value: 'cs' 68 }, 69 { 70 name: 'XSEG_GLOBAL', 71 value: 'g' 72 }, 73 { 74 name: 'XSEG_HOMEPAGE', 75 value: 'h' 76 }, 77 { 78 name: 'XSEG_PRODUCT', 79 value: 'p' 80 }, 81 { 82 name: 'XSEG_SEARCH', 83 value: 's' 84 }, 85 { 86 name: 'XSEG_SORT_RULE_MANAGER', 87 value: 'sq' 88 } 89 ]; 90 var module = { 91 /*
92 * @description get the specific segment of the xSeg cookie 93 * @param {string} segment the part of the xSeg cookie you are trying to get 94 * @return {object} a standard object that contains the page and module portions of the segment, if segment was not found these values will be null 95 */ 96 get: function (segment) { 97 var returnValue = nullValue; 98 99 if (test.object.containsKey(this.xSeg, segment, false)) { 100 returnValue = this.xSeg[segment]; 101 } 102 return returnValue; 103 }, 104 /* 105 * @description check if the page part of the segment equals the value supplied 106 * @param {string} segment : the part of the xSeg cookie you are trying to get 107 * @param {string} value : the value to be evaluated against the segment's page value 108 * @return {boolean} if the value is the same as the segment's page value 109 */ 110 checkPageSegment: function (segment, value) { 111 var segmentValue = this.get(segment); 112 return (segmentValue.page === value); 113 }, 114 /* 115 * @description check if the module part of the segment equals the value supplied 116 * @param {string} segment : the part of the xSeg cookie you are trying to get 117 * @param {string} value : the value to be evaluated against the segment's module value 118 * @return {boolean} if the value is the same as the segment's module value 119 */ 120 checkModuleSegment: function (segment, value) { 121 var segmentValue = this.get(segment); 122 return (segmentValue.module === value); 123 } 124 }; 125 126 /* Start Section For processing the xSegValue */ 127 128 /* 129 * @description Private method to break down the x=y-0 parts of the xSeg value 130 * @param {string} part : the individual segments from the xSeg cookie 131 * @return {object}
131 the processed object containing the key, page, and module 132 */ 133 function processPart(part) { 134 var returnValue = { 135 key: null, 136 page: null, 137 module: null 138 }; 139 140 var partBreakDown = partRegex.exec(part); 141 142 try { 143 returnValue.key = partBreakDown[1]; 144 returnValue.page = partBreakDown[2]; 145 returnValue.module = partBreakDown[3]; 146 } catch (e) { 147 // Do nothing on failure 148 } 149 150 return returnValue; 151 } 152 153 /* 154 * @description Private method to add a processed part to the rest of the segments object 155 * @param {object} segmentGroups : the object that holds the segments 156 * @param {object} segment : the segment being added to the segments. should contain a key, page, and module value. 157 * @return {object} the segmentGroups with the added segment 158 */ 159 function combineProcessedParts(segmentGroups, segment) { 160 if ( 161 test.string.not.empty(segment.key, false) && 162 test.string.not.empty(segment.page, false) && 163 test.string.not.empty(segment.module, false) 164 ) { 165 var segmentDescription = Object.create(Object.prototype, { 166 'page': { 167 value: segment.page, 168 writable: false 169 }, 170 'module': { 171 value: segment.module, 172 writable: false 173 } 174 }); 175 176 Object.defineProperty(segmentGroups, segment.key, { 177 value: segmentDescription, 178 writable: false 179 }); 180 } 181 182 return segmentGroups; 183 } 184 185 Object.defineProperty(module, 'xSeg', { 186 configurable: false, 187 enumerable: false, 188 get: function () { 189 var cookie = cookies.get('xSeg'); 190 var returnValue = {}; 191 if (test.string.not.empty(cookie, false) && test.string.not.equalsIgnoreCase(cookie, 'null', false)) { 192 returnValue = cookie.split('&') 193 .map(processPart) 194 .reduce(combineProcessedParts, {}); 195 } 196 return returnValue; 197 } 198 }); 199 /* End Section For processing the xSegValue */ 200 201 /* Start Section For setting up CONSTANTS */ 202 constants.forEach(function (constant) { 203 Object.defineProperty(module, constant.name, { 204 value: constant.value, 205 writable: false, 206 enumerable: false 207 }); 208 }); 209 /* End Section For setting up CONSTANTS */ 210 211 return module; 212}); 213 214redesign.define('productModules/helpers/Utils',[ 215 'coreServices/xSeg' 216], function defineUtils( 217 xSeg 218) { 219 'use strict'; 220 221 var instance = null; 222 223 function Utils() { 224 if (instance !== null) { 225 throw new Error('Cannot instantiate more than one Utils, use Utils.getInstance()'); 226 } 227 228 this.initialize(); 229 } 230 231 Utils.prototype = { 232 initialize: function init() { 233 // Initializes the singleton. 234 }, 235 236 urlSearchParams: new URLSearchParams(window.location.search), 237 238 /** 239 * Detect if user is coming to PDP from an internal source 240 * e.g. category page or not google! 241 */ 242 243 isInternalBrowsingCustomer: function isInternalBrowsingCustomer(document) { 244 var documentReferrer = document.referrer.toLowerCase(); 245 return documentReferrer.indexOf('abercrombie') !== -1 || documentReferrer.indexOf('hollisterco') !== -1; 246 }, 247 248 /** 249 * Detect if quickview is opened 250 */ 251 get isQuickviewOpened() { 252 return $('.product-quickview__modal[data-open="true"]').length > 0; 253 }, 254 255 get isPdpPage() { 256 return $('body').is('.product-page-v2'); 257 }, 258 259 /** 260 * Detect if exchange quickview is opened 261 */ 262 get isQuickviewExchangesOpened() { 263 return $('.exchanges-quickview__modal[data-open="true"]').length > 0; 264 }, 265 266 isKidsProduct: function isKidsProduct($productSection) { 267 return $productSection.hasClass('is-kids-product'); 268 }, 269 270 getProductBrand: function getProductBrand(productId) { 271 return this.getProductData(productId).productAttrs.brand; 272 }, 273 274 /** 275 * Return the correct product section based on productID 276 * @param {String} productID The Product ID 277 * @return {Object} the jquery object of the correct product section 278 */ 279 getProductSection: function getProductSection() { 280 var productSelectorString = '.product-page-v2-modules';
281 return this.isQuickviewOpened ? $('.product-quickview__modal[data-open="true"]').find(productSelectorString) : $(productSelectorString); 282 }, 283 284 baseGet: function baseGet(object, path) { 285 var pathArr = path.split('.'); 286 var index = 0; 287 var length = pathArr.length; 288 var tempObj = object; 289 290 while (tempObj !== null && typeof tempObj !== 'undefined' && index < length) { 291 tempObj = tempObj[pathArr[index++]] || null; 292 } 293 return index && index === length ? tempObj : null; 294 }, 295 296 /** 297 * Gets the value at `path` of `object`. If the resolved value is 298 * `undefined`, the `defaultValue` is returned in its place. 299 * 300 * @param {Object} object The object to query. 301 * @param {string} path The path of the property to get. 302 * @param {*} [defaultValue] The value returned for `undefined` resolved values. 303 * @returns {*} Returns the resolved value. 304 * @example 305 * 306 * var object = { 'a': { 'b': { 'c': 3 } } }; 307 * 308 * Utils.getValueFromObject(object, 'a.b.c'); 309 * // => 3 310 * 311 * Utils.getValueFromObject(object, 'a.b.c.d', 'default'); 312 * // => 'default' 313 */ 314 getValueFromObject: function getValueFromObject(object, path, defaultValue) { 315 var result = object === null || object === typeof 'undefined' ? null : this.baseGet(object, path); 316 return typeof result === 'undefined' || result === null ? defaultValue : result; 317 }, 318 319 getParentProductSection: function getParentProductSection($child) { 320 return $child.parents('.product-page-v2-modules'); 321 }, 322 323 getANFObject: function getANFObject() { 324 return ANF; 325 }, 326 327 getProductData: function getProductData(productId) { 328 return productCatalog[productId] || {}; 329 }, 330 331 /** 332 * @description Return an object with modelHeight and modelSize based on the selectedModel 333 * @param {String} productId product id 334 * @return {modelSpecs} return modelHeight and modelSize 335 */ 336 getModelSpecs: function getModelSpecs(productId) { 337 var productData = this.getProductData(productId); 338 var defaultModelStr = window.selectedModelSegment || this.getValueFromObject(productData, 'productAttrs.ModelData.defaultModel', null); 339 var selectedModel = defaultModelStr && productData.productAttrs.ModelData ? productData.productAttrs.ModelData[defaultModelStr] : null; 340 341 var modelHeight = selectedModel ? selectedModel.height : this.getValueFromObject(productData, 'productAttrs.OFPModelHeight', ''); 342 var modelSize = selectedModel ? selectedModel.size : this.getValueFromObject(productData, 'productAttrs.OFPModelSize', ''); 343 344 return { 345 modelHeight: modelHeight, 346 modelSize: modelSize 347 }; 348 }, 349 350 hasFinalSaleEnabled: function hasFinalSaleEnabled() { 351 return window.digitalData.get('flag.has-pdp-final-sale', false); 352 }, 353 354 getProductDepartmentId: function getProductDepartmentId(productId) { 355 return this.getProductData(productId).kicId.split('_')[1].split('-')[0]; 356 }, 357 358 getProductKicId: function getProductKicId(productId) { 359 return this.getProductData(productId).kicId; 360 }, 361 362 isJeansProduct: function isJeansProduct(productId) { 363 var jeansDepartmentIds = this.getANFObject().params.storeAttributes.jeansDepartmentIds.split(','); 364 var productDepartmentId = this.getProductDepartmentId(productId); 365 return jeansDepartmentIds.includes(productDepartmentId); 366 }, 367 368 productHasEarlyAccessIndicator: function productHasEarlyAccessIndicator(productId) { 369 var earlyAccessIndicator = this.getValueFromObject(this.getProductData(productId).productAttrs, 'EarlyAccessIndicator', ''); 370 return earlyAccessIndicator.toString().toLowerCase() === 'true'; 371 }, 372 373 hasProductMFEEnabled: function hasProductMFEEnabled() { 374 var hasProductMFEEnabledStoreAttr = this.getANFObject().params.storeAttributes.catalogV2.hasProductMFEEnabled; 375 var hasProductMFEEnabledParam = this.urlSearchParams.get('hasProductMFEEnabled'); 376 var isDevEnv = this.getANFObject().params.env.name === 'DEV'; 377 378 return hasProductMFEEnabledStoreAttr || (hasProductMFEEnabledParam && isDevEnv); 379 }, 380 381 hasPDPMFEBelowTheFold: function hasPDPMFEBelowTheFold() { 382 return this.getANFObject().params.storeAttributes.catalogV2.hasPDPMFEBelowTheFold; 383 }, 384 385 isExternalUser: function isExternalUser() { 386 var paramNames = ['cmp', 'source', 'utm_source']; 387 var isExternalVisitor = false;
388 var pdpExternalVisitorEnabledCheck = ANF.params.storeAttributes.catalogV2.hasPDPExternalVisitorEnabled && xSeg.checkPageSegment(xSeg.XSEG_PRODUCT, 'CT'); 389 390 for (var i = 0; i < paramNames.length; i++) { 391 if (new URLSearchParams(window.location.search).has(paramNames[i])) { 392 isExternalVisitor = true; 393 } 394 } 395 return pdpExternalVisitorEnabledCheck && isExternalVisitor; 396 }, 397 398 hasQuickviewMfeEnabled: function hasQuickviewMfeEnabled() { 399 var hasQuickviewMfeEnabledStoreAttr = this.getANFObject().params.storeAttributes.catalogV2.hasQuickviewMfeEnabled; 400 var hasQuickviewMfeEnabledParam = this.urlSearchParams.get('hasQuickviewMFEEnabled'); 401 var isDevEnv = this.getANFObject().params.env.name === 'DEV'; 402 403 return hasQuickviewMfeEnabledStoreAttr || (hasQuickviewMfeEnabledParam && isDevEnv); 404 }, 405 406 isEyewearProduct: function isEyewearProduct(productAttrsData) { 407 var hasEyewearEnabled = this.getANFObject().params.storeAttributes.catalogV2.hasEyewearEnabled; 408 return !!(hasEyewearEnabled && productAttrsData && ( 409 productAttrsData.EyewearDimensionLensWidth || 410 productAttrsData.EyewearDimensionBridgeWidth || 411 productAttrsData.EyewearDimensionArmLength 412 )); 413 } 414 }; 415 416 Utils.getInstance = function getInstance() { 417 // Gets an instance of the singleton. 418 if (instance === null) { 419 instance = new Utils(); 420 } 421 return instance; 422 }; 423 424 return Utils.getInstance(); 425}); 426 427redesign.define('productModules/helpers/imageLoadHelper',['coreClass/ANFImage'], 428 function imageLoadHelper(ANFImage) { 429 'use strict'; 430 431 function removeBrokenImageFromArray($productSection, failedImageId) { 432 var imageProcessedArray = publicMembers.getProcessedImageArray($productSection); 433 434 return imageProcessedArray.filter(function removeBrokenImageId(item) { 435 return failedImageId !== item.id; 436 }); 437 } 438 439 function removeBrokenImageFromImageObject($productSection, failedImageId, imageType) { 440 var imageDataModel = publicMembers.getImageDataModel($productSection); 441 var imageSetType = imageType; 442 443 if (imageType === 'product') { 444 imageSetType = 'prod'; 445 } else if (imageType === 'lifestyle') { 446 imageSetType = 'life';
447 } else if (imageType === 'user') { // ugc images alt text is "user generated #" 448 imageSetType = 'ugc'; 449 } 450 451 imageDataModel[imageSetType] = imageDataModel[imageSetType].filter(function removeBrokenImageFromObjectArray(item) { 452 return failedImageId !== item.id; 453 }); 454 455 return imageDataModel; 456 } 457 458 var publicMembers = { 459 removeFailedImage: function reomoveFailedImage(failedImageElement, $productSection, imageType, imageId) { 460 var $brokenImage = $(failedImageElement); 461 462 var newImageProcessedArray = removeBrokenImageFromArray($productSection, imageId); 463 var newImageDataModel = removeBrokenImageFromImageObject($productSection, imageId, imageType); 464 465 466 return { 467 imageArray: newImageProcessedArray, 468 imageObject: newImageDataModel, 469 $brokenImage: $brokenImage 470 }; 471 }, 472 storeImageData: function storeImageData($productSection, processedImageArray, imageDataModel) { 473 $productSection.data('processedImageArray', processedImageArray); 474 $productSection.data('imageDataModel', imageDataModel); 475 }, 476 getProcessedImageArray: function getProcessedImageArray($productSection) { 477 return $productSection.data('processedImageArray'); 478 }, 479 getImageDataModel: function getImageDataModel($productSection) { 480 return $productSection.data('imageDataModel'); 481 }, 482 483 loadImages: function loadImages(images, successCallback, failCallback) { 484 // The object that will be returned in the callback 485 var log = { 486 success: [], 487 error: [], 488 totalImageCount: images.length 489 }; 490 491 // Executed everytime an img is successfully or wrongly loaded 492 var verifier = function verifier() { 493 /* total count should be successful loads + error loads. 494 But we dont want to call success if all images failed. 495 inifite slider setup loop caused EPIC-9359 */ 496 if (log.success.length + log.error.length === log.totalImageCount && log.error.length !== log.totalImageCount) { 497 successCallback.call(null, log); 498 } 499 }; 500 501 // Loop through all the images URLs 502 for (var index = 0; index < images.length; index++) { 503 // Prevent that index has the same value by wrapping it inside an anonymous fn 504 (function setListeners(i) { 505 // Image path providen in the array e.g image.png 506 var el = images[i]; 507 var src = el.getAttribute('data-src'); 508 var img = new Image(); 509 510 img.addEventListener('load', function load() { 511 if (!!el.parent) { 512 el.parent.replaceChild(img, el); 513 } else { 514 el.src = src; 515 } 516 log.success.push(el); 517 verifier(); 518 }, false); 519 520 img.addEventListener('error', function error() { 521 log.failedImageElement = el; 522 log.error.push(el); 523 failCallback.call(null, log); 524 verifier(); 525 }, false); 526 527 img.src = src; 528 })(index); 529 } 530 }, 531 532 getProd1ImageId: function getProd1ImageId(imageSets) { 533 var imageId; 534 if (imageSets && imageSets.prod.length > 0) { 535 imageId = imageSets.prod[0].imageId; 536 } else { 537 imageId = this.getNoImageAvailableAssetImageId(); 538 } 539 540 return imageId; 541 }, 542 543 getNoImageAvailableAssetUrl: function getNoImageAvailableAssetUrl() { 544 var id = this.getNoImageAvailableAssetImageId(); 545 546 return ANFImage.instance() 547 .setImageName(id) 548 .setSizePresetEnabled(false) 549 .url; 550 }, 551 552 getNoImageAvailableAssetImageId: function getNoImageAvailableAssetImageId() { 553 var currentBrand = $('header').attr('data-brand'); 554 var brandFlag; 555 556 switch (currentBrand) { 557 case 'anf': 558 brandFlag = 1; 559 break; 560 case 'kids': 561 brandFlag = 2; 562 break; 563 default: 564 brandFlag = 3; 565 break; 566 } 567 568 return 'KIC_' + brandFlag + 'XX-XXXX-XXXX-XXX'; 569 } 570 }; 571 572 return publicMembers; 573 } 574); 575
576redesign.define('productModules/product/productThumbnails',[ 577 'anfcore/components/slider', 578 'productPartial/pdp_productThumbnailButton', 579 'productModules/helpers/Utils', 580 'coreClass/ANFImage', 581 'productModules/helpers/imageLoadHelper' 582], function ProductThumbnails( 583 dsSlider, 584 productPartialPdpProductThumbnailButton, 585 Utils, 586 ANFImage, 587 imageLoadHelper 588) { 589 'use strict'; 590 591 var thumbnailContainerSelectorClass = '.product-thumbnails__container .product-thumbnails'; 592 var productThumbnailWrapperSelectorClass = '.product-thumbnails__container'; 593 var productMainImagesWrapperSelectorClass = '.product-main-images__wrapper'; 594 var productMainImagesSelectorClass = '.product-main-images'; 595 var thumbnailInnerWrapperSelectorClass = '.thumbnail-inner-wrapper'; 596 var thumbnailButtonSelectorClass = '.product-thumbnail-button'; 597 598 var messageBus = $(ANF); 599 var $newThumbnailsContainer; 600 var $oldThumbnailsContainer; 601 602 function onSwatchChanged(e, data) { 603 var $productSection = data.$productSection; 604 var processedImageArray = data.processedImageArray; 605 $oldThumbnailsContainer = $productSection.find(thumbnailContainerSelectorClass).first(); 606 removeHalfLoadingThumbnails(); 607 608 imageLoadHelper.storeImageData($productSection, processedImageArray, data.imageDataModel); 609 610 if (processedImageArray.length > 1) { 611 // convert data to html and insert add it to .product-thumbnails__container 612 var thumbnailsData = getThumbnailsData(processedImageArray); 613 var thumbnailsHtml = productPartialPdpProductThumbnailButton.templates.pdp_productThumbnailButton({thumbnails: thumbnailsData}); 614 $newThumbnailsContainer = $(thumbnailsHtml) 615 .appendTo($productSection.find(productThumbnailWrapperSelectorClass)); 616 // load thumbnails images 617 var $images = $newThumbnailsContainer.find('img'); 618 imageLoadHelper.loadImages($images, onThumbnailsImagesLoaded, onThumbnailLoadFailure); 619 } else { 620 removeOldThumbnails($oldThumbnailsContainer); 621 messageBus.trigger('thumbnailSlider:InitComplete'); 622 } 623 } 624 625 function getThumbnailsData(imageData) { 626 var thumbnailsData = []; 627 imageData.forEach(function mapImageData(image, idx) { 628 var altText = ANF.messaging.catalogV2.thumbnailTMNTs[image.type + 'AltImageText']; 629 var thumbnail = { 630 'selected': (parseInt(idx, 10) === 0), 631 'data-src': ANFImage.instance() 632 .setImageName(image.id) 633 .setWidth(60) 634 .url, 635 'alt': altText.replace('{0}', image.index), 636 'imageId': image.id, 637 'imageType': image.type 638 }; 639 640 thumbnailsData.push(thumbnail); 641 }); 642 643 return thumbnailsData; 644 } 645 646 function onThumbnailsImagesLoaded() { 647 removeOldThumbnails($oldThumbnailsContainer); 648 // fade new one in 649 if ($newThumbnailsContainer) { 650 if (Utils.isQuickviewOpened) { 651 $newThumbnailsContainer.fadeIn(0, onCrossfadeEnd); 652 } else { 653 $newThumbnailsContainer.fadeIn(200, onCrossfadeEnd); 654 } 655 } 656 } 657 658 function onThumbnailLoadFailure(log) { 659 var $failedImage = $(log.failedImageElement); 660 var $thumbnailButton = $failedImage.closest(thumbnailButtonSelectorClass); 661 var failedImageType = $thumbnailButton.data('image-type'); 662 var failedImageId = $thumbnailButton.data('image-id'); 663 var $productSection = Utils.getParentProductSection($failedImage); 664 var cleanedImageDataObject = imageLoadHelper.removeFailedImage(log.failedImageElement, $productSection, failedImageType, failedImageId); 665 666 $failedImage.closest(thumbnailButtonSelectorClass).remove(); 667 imageLoadHelper.storeImageData($productSection, cleanedImageDataObject.imageArray, cleanedImageDataObject.imageObject); 668 } 669 670 function onCrossfadeEnd() { 671 publicMembers.slider = $newThumbnailsContainer; 672 // turn to slider 673 $newThumbnailsContainer.on('beforeChange', onThumbnailsBeforeChange); 674 $newThumbnailsContainer.on('afterChange', onThumbnailsAfterChange); 675 $newThumbnailsContainer.on('init', onSliderInit); 676 $newThumbnailsContainer.on('setPosition', onSliderSetPosition);
677 $newThumbnailsContainer.on('breakpoint', onSliderBreakpoint); 678 initDSSlider($newThumbnailsContainer); 679 680 messageBus.trigger('thumbnailSlider:InitComplete'); 681 682 // reset variables 683 $oldThumbnailsContainer = null; 684 $newThumbnailsContainer = null; 685 } 686 687 function onSliderInit(e, slick) { 688 var $nextArrow = slick.$nextArrow; 689 var $prevArrow = slick.$prevArrow; 690 var $slider = slick.$slider; 691 692 if ($nextArrow.hasClass('slick-hidden')) { 693 $slider.removeClass('has-arrows'); 694 } else { 695 $slider.addClass('has-arrows'); 696 $prevArrow.attr('tabindex', -1); 697 } 698 } 699 700 function onSliderSetPosition(e, slick) { 701 var $slider = slick.$slider; 702 var $slickTrack = $slider.find('.slick-track'); 703 // prevent slider moving up when vertical 704 if (!$slider.hasClass('has-arrows') && slick.options.vertical === true) { 705 $slickTrack.css('transform', 'translate3d(0px, 0px, 0px)'); 706 } 707 } 708 709 function onThumbnailsBeforeChange(e, slick, currentSlideIndex, nextSlideIndex) { 710 var $slides = slick.$slides; 711 var currentSlide = $slides[currentSlideIndex].querySelector(thumbnailInnerWrapperSelectorClass); 712 var nextSlide = $slides[nextSlideIndex].querySelector(thumbnailInnerWrapperSelectorClass); 713 714 // unselect current slide 715 if (currentSlide) currentSlide.setAttribute('data-state', ''); 716 717 // select next slide 718 if (nextSlide) nextSlide.setAttribute('data-state', 'selected'); 719 } 720 721 function onThumbnailsAfterChange(e, slick) { 722 var $prevArrow = slick.$prevArrow; 723 var $nextArrow = slick.$nextArrow; 724 725 if ($prevArrow.hasClass('slick-disabled')) { 726 $prevArrow.attr('tabindex', -1); 727 } else { 728 $prevArrow.attr('tabindex', 0); 729 } 730 if ($nextArrow.hasClass('slick-disabled')) { 731 $nextArrow.attr('tabindex', -1); 732 } else { 733 $nextArrow.attr('tabindex', 0); 734 } 735 } 736 737 function onSliderBreakpoint(e, slick, breakpoint) { 738 if (breakpoint) { 739 var $slider = slick.$slider; 740 var $mainImage = slick.getOption('asNavFor'); 741 var $thumbnail = $slider.find(thumbnailButtonSelectorClass).first(); 742 var slidesToShow = getSlidesToShow($mainImage, $thumbnail); 743 744 var slickSlidesToShowOption = slidesToShow.slidesToShowVertical; 745 if (breakpoint === 540) slickSlidesToShowOption = slidesToShow.slidesToShowHorizontal; 746 747 slick.slickSetOption('slidesToShow', slickSlidesToShowOption); 748 slick.refresh(); 749 onSliderInit(e, slick); 750 751 var thumbnails = $slider.find(thumbnailInnerWrapperSelectorClass); 752 thumbnails.attr('data-state', ''); 753 thumbnails[0].setAttribute('data-state', 'selected'); 754 } 755 } 756 757 function getSlidesToShow($mainImage, $thumbnail) { 758 var slidesToShow = { 759 slidesToShowVertical: 1, 760 slidesToShowHorizontal: 1 761 }; 762 763 // this was a painpoint! if either the mainImageWidth and mainImageHeight are negative you get an inifite loop. - NT+JR 3/31/20 764 var paddingForArrowsHorizonal = 92; 765 var mainImageWidth = $mainImage.width() - paddingForArrowsHorizonal; 766 var thumbnailWidth = $thumbnail.width(); 767 // default to 1 if Infinity 768 slidesToShow.slidesToShowHorizontal = thumbnailWidth !== 0 ? Math.floor(mainImageWidth / thumbnailWidth) : 1; 769 770 var paddingForArrowsVertical = 80; 771 var mainImageHeight = ($mainImage.width() * 1.25) - paddingForArrowsVertical; 772 var thumbnailHeight = $thumbnail.height(); 773 // default to 1 if Infinity 774 slidesToShow.slidesToShowVertical = thumbnailHeight !== 0 ? Math.floor(mainImageHeight / thumbnailHeight) : 1; 775 776 return slidesToShow; 777 } 778 779 function initDSSlider($thumbnails) { 780 var $correspondingMainImages = $thumbnails 781 .parent(productThumbnailWrapperSelectorClass) 782 .siblings(productMainImagesWrapperSelectorClass) 783 .find(productMainImagesSelectorClass) 784 .last(); 785 var $thumbnail = $thumbnails.find(thumbnailButtonSelectorClass).first(); 786 var slidesToShow = getSlidesToShow($correspondingMainImages, $thumbnail); 787 788 var thumbnailSliderSettings = { 789 arrows: true, 790 asNavFor: $correspondingMainImages, 791 focusOnSelect: true, 792 infinite: false, 793 mobileFirst: true, 794 variableWidth: true, 795 waitForAnimate: false, 796 responsive: [ 797 { 798 breakpoint: 540, 799 settings: { 800 slidesToShow: slidesToShow.slidesToShowHorizontal 801 } 802 }, 803 { 804 breakpoint: 825, 805 settings: { 806 slidesToShow: slidesToShow.slidesToShowVertical, 807 verticalSwiping: true, 808 variableWidth: false, 809 vertical: true 810 } 811 } 812 ] 813 }; 814
815 publicMembers.thumbnailSlider.initializeSlider($thumbnails[0], thumbnailSliderSettings); 816 } 817 818 function removeOldThumbnails($oldthumbnails) { 819 $($oldthumbnails).remove(); 820 } 821 822 function removeHalfLoadingThumbnails() { 823 $($newThumbnailsContainer) // stop any animation is currently running on the element then remove 824 .stop(true, true) 825 .remove(); 826 } 827 828 var publicMembers = { 829 listen: function listen() { 830 messageBus.on('productMainImage:load productThumbnails:reload', onSwatchChanged); 831 }, 832 833 thumbnailSlider: dsSlider.default() 834 }; 835 836 return publicMembers; 837}); 838 839redesign.define('productPartial/pdp_productImages',['handlebars','dsHelper/concat','wcsHelper/i18n'], function(Handlebars) { 840 841this["ANF"] = this["ANF"] || {}; 842this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 843 844Handlebars.registerPartial("pdp_productImages", this["ANF"]["templates"]["pdp_productImages"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 845 var stack1; 846 847 return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.isUGC : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.program(4, data, 0),"data":data})) != null ? stack1 : ""); 848},"2":function(container,depth0,helpers,partials,data) { 849 var alias1=container.lambda, alias2=container.escapeExpression, alias3=depth0 != null ? depth0 : (container.nullContext || {}); 850 851 return " <div class=\"product-main-images__image product-main-images__image--ugc\" data-orig-role=\"button\" >\n <img class=\"product-main-image\" data-lazy=\"" 852 + alias2(alias1((depth0 != null ? depth0.src : depth0), depth0)) 853 + "\" alt=\"" 854 + alias2(helpers.concat.call(alias3,helpers.i18n.call(alias3,helpers.concat.call(alias3,"product.product_alt_",(depth0 != null ? depth0.type : depth0),"img",{"name":"concat","hash":{},"data":data}),{"name":"i18n","hash":{},"data":data})," ",(depth0 != null ? depth0.index : depth0),{"name":"concat","hash":{},"data":data})) 855 + "\" data-image-id=\"" 856 + alias2(alias1((depth0 != null ? depth0.imageId : depth0), depth0)) 857 + "\" itemprop=\"image\"/>\n </div>\n"; 858},"4":function(container,depth0,helpers,partials,data) { 859 var alias1=container.lambda, alias2=container.escapeExpression, alias3=depth0 != null ? depth0 : (container.nullContext || {}); 860 861 return " <button class=\"product-main-images__image product-main-images__zoom-modal-button\" data-image-type=\"" 862 + alias2(alias1((depth0 != null ? depth0.type : depth0), depth0)) 863 + "\" tabindex=\"-1\">\n <img class=\"product-main-image\" data-lazy=\"" 864 + alias2(alias1((depth0 != null ? depth0.src : depth0), depth0)) 865 + "\" alt=\"" 866 + alias2(helpers.concat.call(alias3,helpers.i18n.call(alias3,helpers.concat.call(alias3,"product.product_alt_",(depth0 != null ? depth0.type : depth0),"img",{"name":"concat","hash":{},"data":data}),{"name":"i18n","hash":{},"data":data}),(depth0 != null ? depth0.index : depth0),{"name":"concat","hash":{},"data":data})) 867 + "\" data-image-id=\"" 868 + alias2(alias1((depth0 != null ? depth0.imageId : depth0), depth0)) 869 + "\" itemprop=\"image\"/>\n </button>\n"; 870},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 871 var stack1; 872 873 return "<div class=\"product-main-images slider js-slider js-main-image loading\">\n" 874 + ((stack1 = helpers.blockHelperMissing.call(depth0,container.lambda((depth0 != null ? depth0.mainImages : depth0), depth0),{"name":"mainImages","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 875 + "</div>\n"; 876},"useData":true})); 877 878return this["ANF"]; 879 880}); 881redesign.define('productModules/product/productMainImage',[ 882 'productPartial/pdp_productImages', 883 'anfcore/components/slider', 884 'productModules/helpers/Utils', 885 'productModules/helpers/imageLoadHelper', 886 'coreClass/ANFImage' 887], 888function productMainImage( 889 productPartialPdpProductImages, 890 dsSlider, 891 Utils, 892 imageLoadHelper, 893 ANFImage
894) { 895 'use strict'; 896 897 var mainImagesContainerWrapperSelectorClass = '.product-main-images__wrapper'; 898 var loadedMainImagesSelectorClass = '.product-main-images:not(:last-of-type)'; 899 var nonUGCMainImageClass = '.product-main-images .product-main-images__image:not(.product-main-images__image--ugc)'; 900 var loadingClass = 'loading'; 901 var productLoadingContainerSelectorClass = '.product-main-images__loading-container'; 902 var mainImageZoomModalButtonSelectorClass = '.product-main-images__zoom-modal-button'; 903 var mainImageSliderSettings = { 904 accessibility: true, 905 arrows: false, 906 dots: true, 907 dotsClass: 'slick-dots ds-override', 908 hoverArrows: true, 909 infinite: false, 910 lazyLoad: 'progressive', 911 mobileFirst: true, 912 responsive: [ 913 { 914 breakpoint: 540, 915 settings: { 916 asNavFor: Utils.isQuickviewOpened ? null : '.product-thumbnails__container .product-thumbnails', 917 dots: Utils.isQuickviewOpened ? true : false, 918 fade: true, 919 waitForAnimate: false 920 } 921 }, 922 { 923 breakpoint: 825, 924 settings: { 925 asNavFor: null, 926 dots: false 927 } 928 } 929 ] 930 }; 931 // Implementing a queue for handling swatch changes in rapid succession 932 var queue = []; 933 var queueProcessing = false; 934 var noImageAvailableAssetImageId = imageLoadHelper.getNoImageAvailableAssetImageId(); 935 var noImgProcessedImageArray = [{ 936 id: noImageAvailableAssetImageId, 937 imageId: noImageAvailableAssetImageId, 938 index: 1, 939 isUGC: false, 940 src: imageLoadHelper.getNoImageAvailableAssetUrl(), 941 type: 'prod' 942 }]; 943 var mainImageWidth = 800; 944 945 var messageBus = $(ANF); 946 947 function imageLoadHandler(e, data) { 948 // push data for current swatch into queue to handle swatch clicks in rapid succession 949 queue.push({ 950 processedImageArray: data.processedImageArray.slice(0), 951 imageDataModel: data.imageDataModel, 952 productId: data.productId, 953 $productSection: data.$productSection, 954 $mainImagesContainerWrapper: data.$productSection.find(mainImagesContainerWrapperSelectorClass) 955 }); 956 957 processQueue(); 958 959 if (!Utils.isQuickviewOpened) { 960 if (Utils.isExternalUser()) { 961 $('.product-page-user-content__recommendations').addClass('hide'); 962 $('.pdp-grid-container').removeClass('hide'); 963 } 964 } 965 } 966 967 function processQueue() { 968 if (queueProcessing) { 969 return; 970 } 971 972 if (queue.length) { 973 queueProcessing = true; 974 var currentQueueObj = queue.pop(); 975 queue = []; 976 977 if (currentQueueObj.processedImageArray.length < 1) { 978 currentQueueObj.processedImageArray = noImgProcessedImageArray; 979 } 980 addNewImages(currentQueueObj); 981 } 982 } 983 984 function addNewImages(currentQueueObj) { 985 var obj = currentQueueObj; 986 obj.processedImageArray.forEach(function addImageSrc(imageData) { 987 var imgData = imageData; 988 imgData.src = ANFImage.instance() 989 .setImageName(imgData.id) 990 .setWidth(mainImageWidth) 991 .url; 992 }); 993 994 // need jquery object of the hbs output to listen for load/error on each image to be added 995 obj.$imagesMarkup = $(productPartialPdpProductImages.templates.pdp_productImages({mainImages: obj.processedImageArray})); 996 currentQueueObj.$mainImagesContainerWrapper.append(obj.$imagesMarkup); 997 998 postFadeProcess(obj); 999 initSlider(obj); 1000 } 1001 1002 function postFadeProcess(currentQueueObj) { 1003 var $productSection = currentQueueObj.$productSection; 1004 currentQueueObj.$productSection.find(loadedMainImagesSelectorClass).remove(); 1005 1006 // on reload this fires the thumbies reload 1007 messageBus.trigger('productMainImage:firstDownloadComplete', $productSection); 1008 1009 $productSection.find(productLoadingContainerSelectorClass).removeClass(loadingClass); // remove for mobile 1010 currentQueueObj.$imagesMarkup.removeClass(loadingClass); // remove for desktop 1011 1012 if (!Utils.isQuickviewOpened) { 1013 window.setTimeout(function detailsImageLoadTimeout() { // to move trigger response to the end of the JS queue 1014 messageBus.trigger('productPageReviews:updateImage', [$productSection]); 1015 }, 0); 1016 } 1017 queueProcessing = false;
1018 processQueue(); 1019 } 1020 1021 function imageFailedLoadHandler(sliderObj, failedImage) { 1022 var slickIndex = $(failedImage).closest('.slick-slide').attr('data-slick-index'); 1023 sliderObj.$slider.slick('slickRemove', slickIndex); 1024 sliderObj.$slider.slick('slickGoTo', slickIndex); 1025 } 1026 1027 function initSlider(currentQueueObj) { 1028 var $container = currentQueueObj.$imagesMarkup; 1029 $container.on('afterChange', onSlideChange); 1030 $container.on('init', onSlideInit); 1031 $container.on('lazyLoadError', function lazyLoadErrorDataPassThrough(_e, sliderObj, $img) { 1032 imageFailedLoadHandler(sliderObj, $img, currentQueueObj.$productSection); 1033 }); 1034 $container.on('lazyLoaded', function lazyLoadedFunction(_e, slick, $img) { 1035 if ($img.closest('.slick-slide').data('slick-index') === 0) { 1036 $img.fadeIn(100, function isfirstImageLoaded() { 1037 if (Utils.isQuickviewOpened) { 1038 // push this trigger to the back of the JS stack, so the quickview load sequence can begin and not be held up by magnify. 1039 window.setTimeout(function startMagnifyImages() { 1040 messageBus.trigger('quickviewImages:mainImageLoaded', slick.$slider); 1041 }, 0); 1042 } else { 1043 $('.complementary-products-container').fadeIn(100); 1044 } 1045 }); 1046 } else { 1047 $img.addClass('loaded'); 1048 } 1049 }); 1050 1051 dsSlider.default().initializeSlider($container[0], mainImageSliderSettings); 1052 } 1053 1054 function onSlideChange(event, slick, currentSlide) { 1055 var $currentSlickSlide = $($(slick.$slides).get(currentSlide)); 1056 $(ANF).trigger('productMainImage:sliderAfterChange', $currentSlickSlide); 1057 } 1058 // This function will fire again if we end up removing a slide from the Main Image Slider 1059 function onSlideInit() { 1060 $(ANF).trigger('productMainImage:sliderInit'); 1061 } 1062 1063 function mainImageClickHandler(e) { 1064 var $selectedImage = $(e.currentTarget); 1065 messageBus.trigger('productMainImage:mainImageClicked', [{ 1066 idx: $selectedImage.closest('.slick-slide').attr('data-slick-index'), 1067 $productSection: $selectedImage.closest('.product-page-v2-modules'), 1068 image: $selectedImage.find('img'), 1069 imageButton: $selectedImage.find(mainImageZoomModalButtonSelectorClass) 1070 }]); 1071 } 1072 1073 var publicMembers = { 1074 listen: function listen() { 1075 messageBus.on('productMainImage:load productMainImage:reload productPageMainImages:modelSelected', imageLoadHandler); 1076 }, 1077 bindEvents: function bindEvents() { 1078 $(mainImagesContainerWrapperSelectorClass).on('click', nonUGCMainImageClass, mainImageClickHandler); 1079 } 1080 }; 1081 1082 return publicMembers; 1083}); 1084 1085redesign.define('productModules/helpers/breakpoints',[], function BreakpointsMain() { 1086 'use strict'; 1087 1088 /** 1089 * A convenience way to track breakpoint and window resize event 1090 * Built-in to the global ANF object and already run on page 1091 * @example 1092 * // Strings for different screen size 1093 * 'small_phone' 'phone' 'tablet' 'small_desktop' 'desktop' 'large_desktop' 'max_desktop' 1094 * // To get the current screen size 1095 * console.log(ANF.breakpoints.screen) 1096 **/ 1097 1098 function Breakpoints() { 1099 this.screen = null; 1100 1101 this.options = [ 1102 { 1103 maxWidth: 359, 1104 name: 'small_phone' 1105 }, 1106 { 1107 maxWidth: 539, 1108 name: 'phone' 1109 }, 1110 { 1111 maxWidth: 699, 1112 name: 'tablet' 1113 }, 1114 { 1115 maxWidth: 824, 1116 name: 'small_desktop' 1117 }, 1118 { 1119 maxWidth: 1024, 1120 name: 'desktop' 1121 }, 1122 { 1123 maxWidth: 1499, 1124 name: 'large_desktop' 1125 }, 1126 { 1127 maxWidth: 100000, 1128 name: 'max_desktop' 1129 } 1130 ]; 1131 1132 this.screens = this.options.map(function map(obj, idx, array) { 1133 var minWidth; 1134 var maxWidth; 1135 1136 maxWidth = obj.maxWidth; 1137 1138 if (idx === 0) { 1139 minWidth = 0; 1140 } else { 1141 minWidth = array[idx - 1].maxWidth + 1; 1142 } 1143 1144 return [minWidth, maxWidth, obj.name]; 1145 }); 1146 1147 this.getCurrentScreen = function getCurrentScreen(width) { 1148 var currentScreenData; 1149 var currentScreenType; 1150 1151 currentScreenData = this.screens.filter(function filter(element) { 1152 if (element[0] <= width && width <= element[1]) { 1153 return true; 1154 } 1155 return false;
1156 }); 1157 1158 try { 1159 currentScreenType = currentScreenData[0][2]; 1160 } catch (event) { 1161 currentScreenType = this.screens[this.screens.length - 1][2]; 1162 } 1163 1164 if (currentScreenType !== this.screen) { 1165 this.screen = currentScreenType; 1166 } 1167 }; 1168 1169 this.getCurrentScreen(window.innerWidth, true); 1170 1171 return this; 1172 } 1173 1174 var breakpoints = new Breakpoints(); 1175 1176 ANF.breakpoints = breakpoints; 1177 1178 return breakpoints; 1179}); 1180 1181redesign.define('productPartial/pdp_productImagesZoom',['handlebars'], function(Handlebars) { 1182 1183this["ANF"] = this["ANF"] || {}; 1184this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 1185 1186Handlebars.registerPartial("pdp_productImagesZoom", this["ANF"]["templates"]["pdp_productImagesZoom"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 1187 var alias1=container.lambda, alias2=container.escapeExpression; 1188 1189 return " <li class=\"product-zoom__image loading\" data-collection=\"" 1190 + alias2(alias1((depth0 != null ? depth0.collectionId : depth0), depth0)) 1191 + "\" tabindex=\"-1\">\n <img data-src=\"" 1192 + alias2(alias1((depth0 != null ? depth0.src : depth0), depth0)) 1193 + "\" data-collection=\"" 1194 + alias2(alias1((depth0 != null ? depth0.collectionId : depth0), depth0)) 1195 + "\"\n alt=\"" 1196 + alias2(alias1((depth0 != null ? depth0.altText : depth0), depth0)) 1197 + "\" />\n </li>\n"; 1198},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 1199 var stack1; 1200 1201 return "<div class=\"product-zoom__thumbnails\">\n</div>\n<ul class=\"product-zoom__images\">\n" 1202 + ((stack1 = helpers.blockHelperMissing.call(depth0,container.lambda((depth0 != null ? depth0.zoomImages : depth0), depth0),{"name":"zoomImages","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 1203 + "</ul>\n"; 1204},"useData":true})); 1205 1206return this["ANF"]; 1207 1208}); 1209redesign.define('productPartial/pdp_mobileZoomImage',['handlebars'], function(Handlebars) { 1210 1211this["ANF"] = this["ANF"] || {}; 1212this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 1213 1214Handlebars.registerPartial("pdp_mobileZoomImage", this["ANF"]["templates"]["pdp_mobileZoomImage"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 1215 var alias1=container.lambda, alias2=container.escapeExpression; 1216 1217 return "<img id=\"zoom-img\" class=\"loading\" src=\"" 1218 + alias2(alias1((depth0 != null ? depth0.src : depth0), depth0)) 1219 + "\" alt=\"" 1220 + alias2(alias1((depth0 != null ? depth0.alt : depth0), depth0)) 1221 + "\" />"; 1222},"useData":true})); 1223 1224return this["ANF"]; 1225 1226}); 1227redesign.define('productPartial/pdp_zoomThumbnails',['handlebars','dsPartial/button','dsPartial/thumbnail'], function(Handlebars) { 1228 1229this["ANF"] = this["ANF"] || {}; 1230this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 1231 1232Handlebars.registerPartial("pdp_zoomThumbnails", this["ANF"]["templates"]["pdp_zoomThumbnails"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 1233 var stack1; 1234 1235 return ((stack1 = container.invokePartial(partials.button,depth0,{"name":"button","hash":{"variant":"borderless-button","type":"button"},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 1236},"2":function(container,depth0,helpers,partials,data) { 1237 var stack1; 1238 1239 return ((stack1 = container.invokePartial(partials.thumbnail,depth0,{"name":"thumbnail","hash":{"selected":false,"selectedText":(depth0 != null ? depth0.Selected : depth0),"alt":(depth0 != null ? depth0.altText : depth0),"src":(depth0 != null ? depth0.src : depth0)},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 1240},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 1241 var stack1; 1242 1243 return "<div class='zoom-thumbnail-list slider'>\n" 1244 + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.thumbImages : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 1245 + "</div>"; 1246},"usePartial":true,"useData":true})); 1247 1248return this["ANF"]; 1249 1250}); 1251redesign.define('productModules/product/productZoom',[ 1252 'productModules/helpers/breakpoints', 1253 'anfcore/components/modal', 1254 'anfcore/components/slider', 1255 'productModules/helpers/imageLoadHelper', 1256 'anfUtil/testFramework', 1257 'coreClass/ANFImage', 1258 'productPartial/pdp_productImagesZoom', 1259 'productPartial/pdp_mobileZoomImage', 1260 'productPartial/pdp_zoomThumbnails', 1261 'productModules/helpers/Utils' 1262], function ProductZoom( 1263 breakpoints, 1264 modal, 1265 slider, 1266 imageLoadHelper, 1267 test, 1268 ANFImage, 1269 productPartialPdpProductImagesZoom, 1270 productPartialPdpMobileZoomImage, 1271 productPartialPdpZoomThumbnails, 1272 Utils 1273) { 1274 'use strict'; 1275 1276 // Constants 1277 var $window = $(window); 1278 var dsModal = modal.default(); 1279 var dsSlider = slider.default(); 1280 var messageBus = $(ANF); 1281 var zoomImagesContainerSelectorClass = '.product-zoom__images'; 1282 var zoomImageWrapperSelectorClass = '.product-zoom__image'; 1283 var zoomModalContainerSelectorClass = '.product-zoom__modal'; 1284 var zoomThumbnailInnerWrapperSelector = '.thumbnail-inner-wrapper'; 1285 var zoomThumbnailSlideSelector = '.slide'; 1286 var zoomThumbWrapperSelectorClass = '.product-zoom__thumbnails .thumbnail-inner-wrapper'; 1287 var zoomThumbWrapperElement = '<div class="product-zoom__thumbnails"></div>'; 1288 var mobileImageWidth = 800; 1289 var imageWidth = 1900; 1290 var zoomThumbWidth = 60; 1291 // end Constants 1292 1293 var publicMembers = { 1294 bindEvents: function bindEvents() { 1295 $(document).on('click touchstart', '.product-zoom__modal .slick-slide .slide', thumbnailSelectEventHandler); 1296 }, 1297 1298 listen: function listen() { 1299 messageBus.on('productMainImage:mainImageClicked', initZoomModal); 1300 } 1301 }; 1302 1303 function initZoomModal(_e, data) { 1304 var $productSection = data.$productSection; 1305 var productId = $productSection.attr('data-productid'); 1306 var collectionId = Utils.getProductData(productId).collection; 1307 var $zoomModalContentBlock = $productSection.find('.product-zoom__modal .modal-block .modal-content'); 1308 var $imageButton = data.imageButton; 1309 var zoomModal; 1310 1311 var zoomImageLoaderObject = {}; 1312 zoomImageLoaderObject[productId] = {}; 1313 zoomImageLoaderObject[productId].productSection = $productSection; 1314 zoomImageLoaderObject[productId].parentCollectionId = collectionId; 1315 1316 if (isMobile()) { 1317 zoomModal = renderMobileZoom(data, collectionId, $zoomModalContentBlock, productId); 1318 } else {
1319 zoomImageLoaderObject[productId] = prepareZoomImagesForLoad(data, zoomImageLoaderObject[productId]); 1320 zoomModal = openDesktopZoomModal(zoomImageLoaderObject[productId]); 1321 $zoomModalContentBlock.html(zoomThumbWrapperElement); 1322 1323 if (zoomImageLoaderObject[productId].imageDataSrcArray.zoomImages.length > 1) { 1324 handleZoomThumbnailsLoad(zoomImageLoaderObject[productId], $zoomModalContentBlock); 1325 } 1326 downloadImages(zoomImageLoaderObject[productId], $zoomModalContentBlock, $(zoomModal), data.idx); 1327 } 1328 1329 zoomModal.addEventListener('dsModal:Close', function restoreImageFocus() { 1330 $window.off('resize.zoom'); 1331 $imageButton.focus(); 1332 }, {once: true}); 1333 1334 setTimeout(function showZoomTimeout() { 1335 // TODO: remove after image presentation test over 1336 $window.trigger('analytics.miscProductFeature', { 1337 'type': 'PDP Image', 1338 'action': 'click' 1339 }); 1340 }, 0); 1341 } 1342 1343 function openDesktopZoomModal(zoomImageLoaderObject) { 1344 var zoomModal = dsModal.openModal('product-zoom__' + zoomImageLoaderObject.parentCollectionId); 1345
1346 $window.on('resize.zoom', onResizeThumbnailSliderUpdate); 1347 1348 return zoomModal; 1349 } 1350 1351 function prepareZoomImagesForLoad(data, zoomImageLoaderObject) { 1352 var renderedHtml; 1353 var $currentProductSection = data.$productSection; 1354 var newZoomImageLoaderObject = zoomImageLoaderObject; 1355 var collectionId = zoomImageLoaderObject.parentCollectionId; 1356 var imageDataSrcArray = { 1357 zoomImages: [], 1358 thumbImages: [] 1359 }; 1360 1361 $currentProductSection.data('processedImageArray').forEach(function zoomImageDataModelCuration(image) { 1362 // we dont add UGC images to Zoom 1363 if (image.isUGC) { 1364 return false; 1365 } 1366 var zoomImageSrc = ANFImage.instance() 1367 .setImageName(image.imageId) 1368 .setWidth(imageWidth) 1369 .url; 1370 1371 var zoomThumnbailSrc = ANFImage.instance() 1372 .setImageName(image.imageId) 1373 .setWidth(zoomThumbWidth) 1374 .url; 1375 1376 var zoomImageObject = { 1377 src: zoomImageSrc, 1378 collectionId: collectionId, 1379 isOFP: false 1380 }; 1381 var zoomThumbImageObject = { 1382 src: zoomThumnbailSrc, 1383 isOFP: false 1384 }; 1385 1386 if (image.type === 'model') { 1387 zoomImageObject.isOFP = true; 1388 zoomImageObject.altText = ANF.messaging.product.PDP_TEXT_MODEL + ' ' + image.index; 1389 zoomThumbImageObject.altText = ANF.messaging.product.PDP_TEXT_MODEL + ' ' + image.index; 1390 } else if (image.type === 'life') { 1391 zoomImageObject.altText = ANF.messaging.product.PDP_TEXT_LIFESTYLE + ' ' + image.index; 1392 zoomThumbImageObject.altText = ANF.messaging.product.PDP_TEXT_LIFESTYLE + ' ' + image.index; 1393 } else { 1394 zoomImageObject.altText = ANF.messaging.product.PDP_TEXT_PRODUCT + ' ' + image.index; 1395 zoomThumbImageObject.altText = ANF.messaging.product.PDP_TEXT_PRODUCT + ' ' + image.index; 1396 } 1397 1398 imageDataSrcArray.zoomImages.push(zoomImageObject); 1399 imageDataSrcArray.thumbImages.push(zoomThumbImageObject); 1400 1401 return true; 1402 }); 1403 1404 renderedHtml = productPartialPdpProductImagesZoom.templates.pdp_productImagesZoom(imageDataSrcArray); 1405 1406 newZoomImageLoaderObject.imagesLoaded = false; 1407 newZoomImageLoaderObject.$renderedHtml = $(renderedHtml); 1408 // include the thumbnail html, for some reason $renderedHtml has 3 items in the array. 1st is zoom images 2nd is a text node? 3rd is thumbnail container
1409 newZoomImageLoaderObject.modalContent = newZoomImageLoaderObject.$renderedHtml[0].outerHTML + newZoomImageLoaderObject.$renderedHtml[2].outerHTML; 1410 newZoomImageLoaderObject.imageDataSrcArray = imageDataSrcArray; 1411 newZoomImageLoaderObject.parentCollectionId = collectionId; 1412 1413 return newZoomImageLoaderObject; 1414 } 1415 1416 function downloadImages(zoomImageLoaderObject, $zoomModalContentBlock, $zoomModal, index) { 1417 var newZoomImageLoaderObject = zoomImageLoaderObject; 1418 1419 newZoomImageLoaderObject.loadedImageCount = 0; 1420 1421 $zoomModalContentBlock.append(newZoomImageLoaderObject.modalContent); 1422 1423 $zoomModalContentBlock.find('.product-zoom__image img').each(function downloadZoomImageLoop() { 1424 var $currImgSrc = $(this).attr('src', $(this).data('src')); 1425 $currImgSrc.one('load', function zoomImageLoad() { 1426 imageLoaded(newZoomImageLoaderObject, $(this).parent(), $zoomModal, index); 1427 }); 1428 bindImageErrorEvent($currImgSrc); 1429 }); 1430 } 1431 1432 function imageLoaded(zoomImageLoaderObject, $zoomImageContainer, $zoomModal, index) { 1433 var newZoomImageLoaderObject = zoomImageLoaderObject; 1434 newZoomImageLoaderObject.loadedImageCount++; 1435 $zoomImageContainer.removeClass('loading'); 1436 1437 if (newZoomImageLoaderObject.loadedImageCount >= newZoomImageLoaderObject.imageDataSrcArray.zoomImages.length) { 1438 newZoomImageLoaderObject.imagesLoaded = true; 1439 scrollToCorrespondingImage($zoomModal, index); 1440 initWaypoints($zoomModal); 1441 } 1442 } 1443 1444 function renderMobileZoom(data, collectionId, $zoomModalContentBlock) { 1445 var $imgClicked = data.image; 1446 var imgUrl = ANFImage.instance() 1447 .setImageName($imgClicked.data().imageId) 1448 .setWidth(mobileImageWidth) 1449 .url; 1450 1451 var zoomImageDataModel = { 1452 isProdFlatImage: false, 1453 src: imgUrl, 1454 alt: $imgClicked.attr('src').indexOf('prod') > -1 ? 'prod' : 'model' 1455 }; 1456 1457 var mobileZoomImage = productPartialPdpMobileZoomImage.templates.pdp_mobileZoomImage(zoomImageDataModel); 1458 1459 $zoomModalContentBlock.html(mobileZoomImage); 1460 1461 $(zoomModalContainerSelectorClass).on('dsModal:Open', function zoomDSModalOpen() { 1462 var $currentZoomImg = $('#zoom-img'); 1463 $currentZoomImg.load(function zoomImageLoad() { 1464 $(this)[0].scrollIntoView({ 1465 behavior: 'auto', 1466 block: 'center', 1467 inline: 'center' 1468 }); 1469 $(this).removeClass('loading'); 1470 }); 1471 bindImageErrorEvent($currentZoomImg); 1472 }); 1473 var zoomModal = dsModal.openModal('product-zoom__' + collectionId); 1474 1475 $('#product-zoom__' + collectionId ).focus(); 1476 1477 return zoomModal; 1478 } 1479 1480 function handleZoomThumbnailsLoad(zoomImageLoaderObject, $zoomModalContentBlock) { 1481 var newZoomImageLoaderObject = zoomImageLoaderObject; 1482 newZoomImageLoaderObject.thumbnailContent = generateZoomThumbTemplate(newZoomImageLoaderObject); 1483 renderZoomThumbnails($zoomModalContentBlock, newZoomImageLoaderObject); 1484 initZoomThumbnailSlider(newZoomImageLoaderObject); 1485 } 1486 1487 function generateZoomThumbTemplate(zoomImageLoaderObject) { 1488 return productPartialPdpZoomThumbnails.templates.pdp_zoomThumbnails(zoomImageLoaderObject.imageDataSrcArray); 1489 } 1490 1491 function renderZoomThumbnails($zoomModalContentBlock, zoomImageLoaderObject) { 1492 var $zoomThumbnailContentBlock = $zoomModalContentBlock.find('.product-zoom__thumbnails'); 1493 $zoomThumbnailContentBlock.html(zoomImageLoaderObject.thumbnailContent); 1494 } 1495 1496 function initZoomThumbnailSlider(zoomImageLoaderObject) { 1497 var viewportHeight = window.innerHeight; 1498 var $thumbnailListContainer = $('.product-zoom__modal[data-open=true] .zoom-thumbnail-list'); 1499 var thumbnailHeight = 85; 1500 var thumbnailCount = Math.floor(viewportHeight / thumbnailHeight); 1501 var newZoomImageLoaderObject = zoomImageLoaderObject; 1502 1503 var sliderSettings = { 1504 arrows: true, 1505 infinite: false, 1506 slidesToShow: thumbnailCount, 1507 vertical: true, 1508 respondTo: 'window' 1509 }; 1510 1511 newZoomImageLoaderObject.loadedThumbnailImageCount = 0; 1512 1513 $thumbnailListContainer.children().each(function zoomThumbnailLoop() { 1514 $(this).find('img').on('load', function zoomThumbnailLoad() {
1515 newZoomImageLoaderObject.loadedThumbnailImageCount++; 1516 1517 if (newZoomImageLoaderObject.loadedThumbnailImageCount >= newZoomImageLoaderObject.imageDataSrcArray.thumbImages.length) { 1518 dsSlider.initializeSlider($thumbnailListContainer[0], sliderSettings); 1519 } 1520 }); 1521 bindImageErrorEvent($(this).find('img')); 1522 }); 1523 } 1524 1525 function scrollToCorrespondingImage($modal, imageIdx) { 1526 var $targetZoomedImage = $modal.find(zoomImagesContainerSelectorClass + ' img').eq(imageIdx); 1527 var imagePosition = $targetZoomedImage.position(); 1528 1529 // only scroll to the image if it is on the page. 1530 if (test.type.defined(imagePosition)) { 1531 $modal.scrollTop(imagePosition.top); 1532 1533 $targetZoomedImage.closest(zoomImageWrapperSelectorClass).focus(); 1534 1535 selectActiveThumbnail($modal, imageIdx); 1536 } 1537 } 1538 1539 function selectActiveThumbnail($modal, imageIdx) { 1540 var $zoomThumbs = $modal.find(zoomThumbWrapperSelectorClass); 1541 $zoomThumbs.attr('data-state', ''); 1542 $zoomThumbs.eq(imageIdx).attr('data-state', 'selected'); 1543 $zoomThumbs.eq(imageIdx).closest(zoomThumbnailSlideSelector).focus(); 1544 } 1545 1546 function initWaypoints($modal) { 1547 var Waypoint = Waypoint || window.Waypoint; 1548 var $zoomImages = $modal.find(zoomImagesContainerSelectorClass).children(); 1549 1550 $zoomImages.each(function zoomImageWaypointDestroyLoop() { 1551 var $image = $(this); 1552 var waypointContext = Waypoint.Context.findByElement($image.context); 1553 1554 if (waypointContext) { 1555 waypointContext.destroy(); 1556 } 1557 }); 1558 1559 $zoomImages.waypoint({ 1560 handler: waypointHandler, 1561 offset: '55%', 1562 context: zoomModalContainerSelectorClass 1563 }); 1564 } 1565
1566 function onResizeThumbnailSliderUpdate() { 1567 var $zoomModal = $('.product-zoom__modal[data-open=true]'); 1568 var viewportHeight = window.innerHeight; 1569 var $thumbnailListContainer = $zoomModal.find('.zoom-thumbnail-list'); 1570 var thumbnailHeight = 85; 1571 var thumbnailCount = Math.floor(viewportHeight / thumbnailHeight); 1572 var activeThumbIndex = $thumbnailListContainer.find('.thumbnail-inner-wrapper[data-state="selected"]').closest('.slick-slide').index(); 1573 1574 $thumbnailListContainer.slick('slickSetOption', 'slidesToShow', thumbnailCount); 1575 $thumbnailListContainer.slick('refresh'); 1576 1577 // reselect the active thumbnail after the refresh, since refresh whipes that state 1578 selectActiveThumbnail($zoomModal, activeThumbIndex); 1579 } 1580 1581 function waypointHandler(direction) { 1582 var idx = $(this.element).index(); 1583 var $modal = $(this.element).closest(zoomModalContainerSelectorClass); 1584 1585 idx = wayPointGetIndex(direction, idx); 1586 selectActiveThumbnail($modal, idx); 1587 } 1588 1589 function wayPointGetIndex(direction, idx) { 1590 var currentIdex = idx; 1591 if (direction === 'up') { 1592 currentIdex -= 1; 1593 return currentIdex; 1594 } 1595 1596 return currentIdex; 1597 } 1598 1599 function thumbnailSelectEventHandler(e) { 1600 var $clickedThumbnail = $(e.target); 1601 var $modal = $clickedThumbnail.closest(zoomModalContainerSelectorClass); 1602 var $zoomThumbs = $modal.find(zoomThumbWrapperSelectorClass); 1603 var targetIndex = $zoomThumbs.index($clickedThumbnail.find(zoomThumbnailInnerWrapperSelector)); 1604 1605 scrollToCorrespondingImage($modal, targetIndex); 1606 } 1607 1608 function bindImageErrorEvent($image) { 1609 $image.one('error', function zoomImageLoadError() { 1610 var imageLoadErrorFallbackUrl = imageLoadHelper.getNoImageAvailableAssetUrl(); 1611 var imagePresets = this.src.split('?')[1]; 1612 $(this).data('src', imageLoadErrorFallbackUrl + '?' + imagePresets); 1613 $(this).attr('src', imageLoadErrorFallbackUrl + '?' + imagePresets); 1614 }); 1615 } 1616 1617 function isMobile() { 1618 return breakpoints && (breakpoints.screen === 'phone' || breakpoints.screen === 'small_phone' || breakpoints.screen === 'tablet') && ANF.params.env.isMobile === 'true'; 1619 } 1620 1621 return publicMembers; 1622}); 1623 1624redesign.define('coreModulesHelpers/StringUtils',[],function () { 1625 'use strict'; 1626 1627 var instance = null; 1628 1629 // Matches non-punctuation characters except apostrophe for capitalization. 1630 var CAPITALIZE_REG = /[^\u0000-\u0040\u005B-\u0060\u007B-\u007F]+('s)?/g; 1631 1632 // Regex matching camelCase. 1633 var CAMELIZE_REG = /(^|_)([^_]+)/g; 1634 1635 function StringUtils() { 1636 if (instance !== null) { 1637 throw new Error('Cannot instantiate more than one StringUtils, use StringUtils.getInstance()'); 1638 } 1639 1640 this.initialize(); 1641 } 1642 1643 StringUtils.prototype = { 1644 initialize: function () { 1645 // Initializes the singleton. 1646 }, 1647 1648 /** 1649 * Truncate string by number of words 1650 * @param {String} str The original string 1651 * @param {Number} limit The number of words to keep 1652 * @param {String} after The appended text after the truncated copy 1653 * @return {String} The truncated text 1654 */ 1655 truncateOnWord: function (str, limit, after) { 1656 // clean up str 1657 var content = str.trim(); 1658 // Convert the content into an array of words 1659 // Remove any words above the limit 1660 content = content.split(' ').slice(0, limit); 1661 // Convert the array of words back into a string 1662 // If there's content to add after it, add it 1663 content = content.join(' ') + (after ? after : ''); 1664 // Return the content 1665 return content; 1666 }, 1667 1668 /** * 1669 * @method capitalize([lower] = false, [all] = false) 1670 * @returns String 1671 * @short Capitalizes the first character of the string. 1672 * @extra If [lower] is true, the remainder of the string will be downcased. 1673 * If [all] is true, all words in the string will be capitalized. 1674 * 1675 * @example 1676 * 1677 * 'hello'.capitalize() -> 'Hello' 1678 * 'HELLO'.capitalize(true) -> 'Hello' 1679 * 'hello kitty'.capitalize() -> 'Hello kitty' 1680 * 'hEllO kItTy'.capitalize(true, true) -> 'Hello Kitty' 1681 * 1682 * @param {boolean} [lower] 1683 * @param {boolean} [all] 1684 * 1685 ***/ 1686 capitalize: function (str, lower, all) { 1687 return this.stringCapitalize(str, lower, all); 1688 }, 1689 1690 /** * 1691 * @method camelize([upper] = true) 1692 * @returns String 1693 * @short Converts string to camel case.
1694 * @extra If [upper] is true, the string will be UpperCamelCase. 1695 * 1696 * @example 1697 * 1698 * 'caps_lock'.camelize() -> 'CapsLock' 1699 * 'capsLock'.camelize() -> 'CapsLock' 1700 * 'MozBorderRadius'.camelize(false) -> 'mozBorderRadius' 1701 * 1702 * @param {boolean} [upper] 1703 * 1704 ***/ 1705 camelize: function (str, upper) { 1706 return this.stringCamelize(str, upper); 1707 }, 1708 1709 stringCamelize: function (str, upper) { 1710 str = this.stringUnderscore(str); 1711 var _this = this; 1712 return str.replace(CAMELIZE_REG, function (match, pre, word, index) { 1713 var cap = upper !== false || index > 0; 1714 return cap ? _this.stringCapitalize(word, true) : word; 1715 }); 1716 }, 1717 1718 stringUnderscore: function (str) { 1719 return str 1720 .replace(/[-\s]+/g, '_') 1721 .replace(/([A-Z\d]+)([A-Z][a-z])/g, '$1_$2') 1722 .replace(/([a-z\d])([A-Z])/g, '$1_$2') 1723 .toLowerCase(); 1724 }, 1725 1726 stringCapitalize: function (str, downcase, all) { 1727 if (downcase) { 1728 str = str.toLowerCase(); 1729 } 1730 return all ? str.replace(CAPITALIZE_REG, this.simpleCapitalize) : this.simpleCapitalize(str); 1731 }, 1732 1733 simpleCapitalize: function (str) { 1734 return str.charAt(0).toUpperCase() + str.slice(1); 1735 } 1736 }; 1737 1738 StringUtils.getInstance = function () { 1739 // Gets an instance of the singleton. 1740 if (instance === null) { 1741 instance = new StringUtils(); 1742 } 1743 return instance; 1744 }; 1745 1746 return StringUtils.getInstance(); 1747}); 1748 1749redesign.define('productModules/product/productImageLoader',[ 1750 'coreServices/url', 1751 'coreModulesHelpers/StringUtils', 1752 'anfUtil/testFramework', 1753 'productModules/helpers/imageLoadHelper', 1754 'productModules/helpers/Utils' 1755], 1756function productImageLoader(urlService, StringUtils, test, imageLoadHelper, Utils) { 1757 'use strict'; 1758 1759 var quickviewModalProductSectionSelectorClass = '.product-quickview__content .product-page-v2-modules'; 1760 var productSectionSelectorClass = '.product-page-v2-modules'; 1761 var productIdAttributeName = 'productid'; 1762 var messageBus = $(ANF); 1763 1764 var isReload = false; 1765 1766 function getValidatedModelSegments(productDataObject) { 1767 if ( 1768 test.type.not.defined(productDataObject.productAttrs, false) || 1769 test.type.not.defined(productDataObject.productAttrs.ModelData, false) 1770 ) { 1771 return false; 1772 } 1773 1774 var imageDataObj = productDataObject.imageSets; 1775 var modelDataObj = productDataObject.productAttrs.ModelData; 1776 var verifiedModelSegments = []; 1777 1778 $.each(modelDataObj, function pimModelDataLoop(key) { 1779 var segment = key.split('Model')[1]; 1780 1781 // Check if image data and model data exist 1782 if (key.indexOf('default') < 0 && modelDataObj[key] && imageDataObj['model' + segment]) { 1783 // the variable key here is 'ModelA' 1784 verifiedModelSegments.push(key); 1785 } 1786 }); 1787 1788 return verifiedModelSegments; 1789 } 1790 1791 function reorderImageArray(productDataObject, firstImageSet, modelSegment) { 1792 var imageDataModel = productDataObject.imageSets; 1793 var orderedImageArray = []; 1794 1795 // store model group name 1796 var modelGroupName = modelSegment ? StringUtils.camelize(modelSegment, false) : 'model'; 1797 var imageTypes = [modelGroupName, 'life', 'prod', 'ugc']; 1798 1799 // make sure the data arrays exist before trying to concat them together 1800 for (var i = 0; i < imageTypes.length; i++) { 1801 if (!imageDataModel[imageTypes[i]]) { 1802 imageDataModel[imageTypes[i]] = []; 1803 } 1804 } 1805 1806 if (firstImageSet.includes('model')) { 1807 orderedImageArray = orderedImageArray.concat(imageDataModel[modelGroupName], imageDataModel.prod, imageDataModel.ugc, imageDataModel.life); 1808 } else if (firstImageSet.includes('life')) { 1809 orderedImageArray = orderedImageArray.concat(imageDataModel.prod, imageDataModel[modelGroupName], imageDataModel.ugc, imageDataModel.life); 1810 } else if (firstImageSet.includes('ugc')) { 1811 orderedImageArray = orderedImageArray.concat(imageDataModel.ugc, imageDataModel.prod, imageDataModel[modelGroupName], imageDataModel.life); 1812 } else { 1813 orderedImageArray = orderedImageArray.concat(imageDataModel.prod, imageDataModel[modelGroupName], imageDataModel.ugc, imageDataModel.life); 1814 } 1815 1816 return orderedImageArray; 1817 } 1818 1819 function reorderImageSetToMatchDefaultModel(productDataObject, processedImageArray, isQuickview) { 1820 var imageDataModel = productDataObject.imageSets; 1821 var reorderedImageArray = processedImageArray; 1822 1823 if (isQuickview) { 1824 return processedImageArray; 1825 } 1826 1827 var verifiedModelSegments = getValidatedModelSegments(productDataObject); 1828 1829 if (verifiedModelSegments.length > 1) { 1830 var productModelData = productDataObject.productAttrs.ModelData; 1831 var selectedModelString = window.selectedModelSegment || ''; 1832 var defaultModelSegment; 1833 var modelImageSegment; 1834 1835 if (verifiedModelSegments.includes(productModelData.defaultModel)) { 1836 defaultModelSegment = productModelData.defaultModel; 1837 } else { 1838 defaultModelSegment = verifiedModelSegments[0]; 1839 } 1840 1841 productModelData.verifiedDataFlag = true; 1842 productModelData.availableModelImageSetSegments = verifiedModelSegments.slice(); 1843 1844 // check to see if the selected model exisits and we have data for it 1845 if (imageDataModel[StringUtils.camelize(selectedModelString, false)] && productModelData[window.selectedModelSegment]) { 1846 modelImageSegment = window.selectedModelSegment; 1847 } else { 1848 modelImageSegment = defaultModelSegment; 1849 window.selectedModelSegment = defaultModelSegment; 1850 } 1851 1852 reorderedImageArray = reorderImageArray(productDataObject, window.firstImageSetType, modelImageSegment); 1853 } else { 1854 window.selectedModelSegment = ''; 1855 } 1856 1857 return reorderedImageArray; 1858 } 1859 1860 function prepareImageDataModel(productDataObject) { 1861 var imageIndexObj = {}; 1862 1863 $.each(productDataObject.imageSets, function preparingImageDataOuterEach(i, subArray) { 1864 $.each(subArray, function preparingImageDataInnerEach(n, img) { 1865 var typeExec = /(prod|life|ugc|model)([a-zA-Z]?)/g.exec(img.id) || ['prod', 'prod']; // When using fallback image, hardcode type and index group to prod 1866 var imgType = typeExec[1]; 1867 var imgIdxGroup = typeExec[0]; 1868 1869 img.isUGC = img.id.indexOf('ugc') > 0; 1870 img.imageId = img.id; 1871 imageIndexObj[imgIdxGroup + 'Idx'] = (imageIndexObj[imgIdxGroup + 'Idx'] || 0) + 1; // If index is null set to 0 + 1 1872 img.index = imageIndexObj[imgIdxGroup + 'Idx']; 1873 img.type = imgType; 1874 }); 1875 }); 1876 1877 productDataObject.imageDataModelPrepared = true; 1878 } 1879 1880 function getFaceoutImageSetType(url) { 1881 var faceoutImageSetType; 1882 1883 $.each(urlService.deconstruct(url).search, function urlParamLoop() { 1884 if (this.name === 'faceout') { 1885 faceoutImageSetType = this.value; 1886 return; 1887 } 1888 }); 1889 return faceoutImageSetType; 1890 } 1891 1892 function getFirstImageSetType(url) { 1893 // check first from URL, then window.firstImageSetType 1894 var firstImageSetType; 1895 var faceoutVarFromUrl = getFaceoutImageSetType(url); 1896 1897 if (faceoutVarFromUrl) { 1898 firstImageSetType = faceoutVarFromUrl; 1899 } else if (window.firstImageSetType) { 1900 firstImageSetType = window.firstImageSetType; 1901 } else { 1902 firstImageSetType = 'prod'; 1903 } 1904 1905 return firstImageSetType; 1906 } 1907 1908 function orderAndRouteImages(productDataObject, productId, $productSection, firstImageSetType, isQuickview) { 1909 var orderedImageArray = reorderImageArray(productDataObject, firstImageSetType); 1910 imageLoadRouter(orderedImageArray, productDataObject, productId, $productSection, isQuickview); 1911 } 1912 1913 function loadHandler(e, data) {
1914 var isQuickview = data.isQuickview || Utils.isQuickviewOpened; 1915 var $productSection = data.$productSection; 1916 1917 isReload = e.type === 'productPageImages:reload'; 1918 1919 if (!isQuickview && Utils.hasProductMFEEnabled()) { 1920 return; 1921 } 1922 1923 if (!$productSection) { 1924 $productSection = isQuickview ? $(quickviewModalProductSectionSelectorClass) : $(productSectionSelectorClass); 1925 } 1926 1927 var productId = $productSection.data(productIdAttributeName); 1928 var productDataObject = Utils.getProductData(productId); 1929 1930 // If imageSets is empty add the "image not found" asset 1931 if ($.isEmptyObject(productDataObject.imageSets)) { 1932 productDataObject.imageSets = {}; 1933 productDataObject.imageSets.prod = [{ 1934 id: imageLoadHelper.getNoImageAvailableAssetImageId() 1935 }]; 1936 } 1937 1938 var firstImageSetType = getFirstImageSetType(data.URL, $productSection); 1939 1940 window.firstImageSetType = window.firstImageSetType !== firstImageSetType ? firstImageSetType : window.firstImageSetType; 1941 1942 // if the imageDataModel hasn't yet been prepared (i.e. this is a fresh swatch), prepare it. 1943 if (!productDataObject.imageDataModelPrepared) { 1944 prepareImageDataModel(productDataObject); 1945 } 1946 1947 orderAndRouteImages(productDataObject, productId, $productSection, firstImageSetType, isQuickview); 1948 } 1949 1950 function imageLoadRouter(orderedImageArray, productDataObject, productId, $productSection, isQuickview) { 1951 var reorderedImageArray = reorderImageSetToMatchDefaultModel(productDataObject, orderedImageArray, isQuickview); 1952 1953 var imageLoaderObject = { 1954 processedImageArray: reorderedImageArray, 1955 imageDataModel: productDataObject.imageSets, 1956 productId: productId, 1957 $productSection: $productSection, 1958 isQuickview: isQuickview 1959 }; 1960 1961 if (isReload) { 1962 messageBus.one('productMainImage:firstDownloadComplete', function messageBusFunction() { 1963 messageBus.trigger('productThumbnails:reload', imageLoaderObject); 1964 }); 1965 1966 messageBus.trigger('productMainImage:reload', imageLoaderObject); 1967 } else { 1968 if (isQuickview && ($('.product-quickview__modal').attr('data-open') === 'false')) { 1969 $('.product-quickview__modal .product-quickview__content').empty(); 1970 return; 1971 } 1972 1973 messageBus.trigger('productMainImage:load', imageLoaderObject); 1974 } 1975 } 1976 1977 var publicMembers = { 1978 listen: function listen() { 1979 messageBus.on('productPageImages:load', loadHandler); 1980 messageBus.on('productPageImages:reload', loadHandler); 1981 }, 1982 reorderImageArray: reorderImageArray 1983 }; 1984 1985 return publicMembers; 1986}); 1987 1988redesign.define('productPartial/pdp_modelSelectorButton',['handlebars','dsPartial/button','dsHelper/concat','dsPartial/button-state'], function(Handlebars) { 1989 1990this["ANF"] = this["ANF"] || {}; 1991this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 1992 1993Handlebars.registerPartial("pdp_modelSelectorButton", this["ANF"]["templates"]["pdp_modelSelectorButton"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 1994 var stack1, alias1=container.lambda; 1995 1996 return " <span class=\"model-specs model-selector__button__model-specs\">\n <span class=\"model-specs__height\">" 1997 + ((stack1 = alias1((depth0 != null ? depth0.modelHeight : depth0), depth0)) != null ? stack1 : "") 1998 + "</span>\n <span class=\"model-specs__separator\">|</span>\n <span class=\"model-specs__size\">" 1999 + ((stack1 = alias1((depth0 != null ? depth0.modelSize : depth0), depth0)) != null ? stack1 : "") 2000 + "</span>\n </span>\n" 2001 + ((stack1 = container.invokePartial(partials["button-state"],depth0,{"name":"button-state","hash":{"active":true,"aria-label":(depth0 != null ? depth0.buttonLabel : depth0),"label":(depth0 != null ? depth0.buttonLabel : depth0),"state":"initial"},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 2002},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2003 var stack1; 2004 2005 return ((stack1 = container.invokePartial(partials.button,depth0,{"name":"button","hash":{"data-
2005prop":helpers.concat.call(depth0 != null ? depth0 : (container.nullContext || {}),"data-modal=",(depth0 != null ? depth0.modalID : depth0),{"name":"concat","hash":{},"data":data}),"class-list":"model-selector__button js-open-toaster","current-state":"initial","variant":"borderless-button","value":(depth0 != null ? depth0.buttonLabel : depth0),"type":"button"},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 2006},"usePartial":true,"useData":true})); 2007 2008return this["ANF"]; 2009 2010}); 2011redesign.define('productPartial/pdp_modelSelector',['handlebars','dsPartial/toaster','dsPartial/modal','dsPartial/button'], function(Handlebars) { 2012 2013this["ANF"] = this["ANF"] || {}; 2014this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2015 2016Handlebars.registerPartial("pdp_modelSelector", this["ANF"]["templates"]["pdp_modelSelector"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 2017 var stack1; 2018 2019 return ((stack1 = container.invokePartial(partials.modal,depth0,{"name":"modal","hash":{"class-list":"model-selector__modal ds-override","variant":"headerless","id":(depth0 != null ? depth0.modalID : depth0),"title":"Models"},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 2020},"2":function(container,depth0,helpers,partials,data) { 2021 var stack1; 2022 2023 return " <ul class=\"model-selector__model-card-list\">\n" 2024 + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.models : depth0),{"name":"each","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2025 + " </ul>\n"; 2026},"3":function(container,depth0,helpers,partials,data) { 2027 var stack1; 2028 2029 return " <li class=\"model-selector__model-card-container " 2030 + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.isSelected : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2031 + " \" data-segment=\"" 2032 + container.escapeExpression(container.lambda((depth0 != null ? depth0.segment : depth0), depth0)) 2033 + "\">\n" 2034 + ((stack1 = container.invokePartial(partials.button,depth0,{"name":"button","hash":{"class-list":"button ds-override model-selector__model-card no-linkify","variant":"borderless-button","type":"button"},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2035 + " </li>\n"; 2036},"4":function(container,depth0,helpers,partials,data) { 2037 return "selected"; 2038},"6":function(container,depth0,helpers,partials,data) { 2039 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 2040 2041 return " <img src=\"" 2042 + alias2(alias1((depth0 != null ? depth0.src : depth0), depth0)) 2043 + "\" alt=\"" 2044 + alias2(alias1((depth0 != null ? depth0.name : depth0), depth0)) 2045 + " wearing " 2046 + ((stack1 = alias1((depth0 != null ? depth0.size : depth0), depth0)) != null ? stack1 : "") 2047 + "\"/>\n <div class=\"model-card__model-info\">\n <div class=\"h4 model-card__name\">" 2048 + alias2(alias1((depth0 != null ? depth0.name : depth0), depth0)) 2049 + "</div>\n <div class=\"model-specs model-card__model-demensions\">\n <span class=\"model-specs__height\">" 2050 + ((stack1 = alias1((depth0 != null ? depth0.height : depth0), depth0)) != null ? stack1 : "") 2051 + "</span>\n <span class=\"model-specs__separator\">|</span>\n <span class=\"model-specs__size\">" 2052 + ((stack1 = alias1((depth0 != null ? depth0.size : depth0), depth0)) != null ? stack1 : "") 2053 + "</span>\n </div>\n </div>\n"; 2054},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2055 var stack1; 2056 2057 return "<div class=\"model-selector__toaster-container scope-1892\">\n" 2058 + ((stack1 = container.invokePartial(partials.toaster,depth0,{"name":"toaster","hash":{"class-list":"model-selector__toaster","direction":(depth0 != null ? depth0.toasterOpenDirection : depth0)},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2059 + "</div>\n"; 2060},"usePartial":true,"useData":true})); 2061 2062return this["ANF"]; 2063 2064}); 2065redesign.define('productModules/product/productModelSelector',[ 2066 'productModules/helpers/Utils', 2067 'productModules/product/productImageLoader', 2068 'coreClass/ANFImage', 2069 'productPartial/pdp_modelSelectorButton', 2070 'productPartial/pdp_modelSelector' 2071], function ProductModelSelector( 2072 Utils, 2073 imageLoader, 2074 ANFImage, 2075 productPartialPdpModelSelectorButton, 2076 productPartialPdpModelSelector 2077) { 2078 'use strict'; 2079 2080 var messageBus = $(ANF); 2081 2082 var $button; 2083 var $buttonOverlay; 2084 var $toaster; 2085 var $productSection; 2086 var productId; 2087 2088 var publicMembers = { 2089 listen: function listen() { 2090 messageBus.on('productMainImage:load', onImageDataChanged); 2091 messageBus.on('productMainImage:reload', onImageDataChanged); 2092 }, 2093 2094 bindEvents: function bindEvents() { 2095 $('.product-page-v2-modules').on('click', '.model-selector__model-card-list .model-selector__model-card-container', modelSelected); 2096 $('.product-page-v2-modules').on('click', 'button[data-modal*="model-selector-toaster"]', openM
2096odelToaster); 2097 } 2098 }; 2099 2100 function onImageDataChanged(eventObj, eventData) { 2101 if (Utils.isQuickviewOpened) { 2102 return; 2103 } 2104 2105 // reset data and view 2106 $productSection = eventData.$productSection; 2107 productId = eventData.productId; 2108 2109 if ($button) { 2110 $button.remove(); 2111 $button = null; 2112 } 2113 2114 if ($toaster) { 2115 $toaster.remove(); 2116 $toaster = null; 2117 } 2118 2119 if ($buttonOverlay) { 2120 $buttonOverlay.remove(); 2121 $buttonOverlay = null; 2122 } 2123 2124 messageBus.off('productMainImage:sliderInit', onSliderInitMainImage); 2125 messageBus.off('productMainImage:sliderAfterChange', onSliderAfterChangeMainImage); 2126 2127 var productModelDataObj = Utils.getProductData(productId).productAttrs.ModelData; 2128 2129 if (productModelDataObj && productModelDataObj.verifiedDataFlag) { 2130 var modelSelectorDataHBS = getModelSelectorDataHBS(); 2131 2132 // add pdp_modelSelector.hbs to the DOM 2133 var buttonTemplate = productPartialPdpModelSelectorButton.templates.pdp_modelSelectorButton(modelSelectorDataHBS); 2134 var toasterTemplate = productPartialPdpModelSelector.templates.pdp_modelSelector(modelSelectorDataHBS); 2135 2136 $productSection.find('.product-main-images__wrapper').append(buttonTemplate); 2137 $productSection.append(toasterTemplate); 2138 2139 $button = $productSection.find('.model-selector__button'); 2140 $buttonOverlay = $productSection.find('.model-selector-button--overlay'); 2141 $toaster = $productSection.find('.model-selector__toaster'); 2142 2143 // need to init DS js everytime to bind button and toaster 2144 ANF.globalV2.designsystem.init(); 2145 2146 messageBus.on('productMainImage:sliderInit', onSliderInitMainImage); 2147 messageBus.on('productMainImage:sliderAfterChange', onSliderAfterChangeMainImage); 2148 } 2149 } 2150 2151 function onSliderInitMainImage() { 2152 if (Utils.isQuickviewOpened) { 2153 return; 2154 } 2155 2156 var mainImageType = $productSection.find('.product-main-images .slick-current .product-main-images__image').data('image-type'); 2157 showHideButton(mainImageType === 'model'); 2158 } 2159 2160 function onSliderAfterChangeMainImage(e, currentImageSlide) { 2161 if (Utils.isQuickviewOpened) { 2162 return; 2163 } 2164 2165 var mainImageType = $(currentImageSlide).find('.product-main-images__image').data('image-type'); 2166 showHideButton(mainImageType === 'model'); 2167 } 2168 2169 function showHideButton(isButtonShown) { 2170 if (isButtonShown) { 2171 $button.stop(true, true).fadeTo(500, 0.92); 2172 // add class to move the slider dots to the right if on mobile 2173 $productSection.find('.product-main-images .slick-dots').addClass('right-align'); 2174 } else { 2175 $button.stop(true, true).fadeOut(500); 2176 $productSection.find('.product-main-images .slick-dots').removeClass('right-align'); 2177 } 2178 } 2179 2180 function updateModelButtonInfo(defaultModel, productData) { 2181 var currentModel = Utils.getProductData(productData.productid).productAttrs.ModelData[defaultModel]; 2182 2183 $button.find('.model-specs__height').html(currentModel.height); 2184 $button.find('.model-specs__size').html(currentModel.size); 2185 2186 $buttonOverlay.find('.model-specs__height').html(currentModel.height); 2187 $buttonOverlay.find('.model-specs__size').html(currentModel.size); 2188 } 2189 2190 function getModelSelectorDataHBS() { 2191 var productData = Utils.getProductData(productId); 2192 var defaultModelStr = window.selectedModelSegment || Utils.getValueFromObject(productData, 'productAttrs.ModelData.defaultModel', void 0); 2193 var selectedModel = productData.productAttrs.ModelData[defaultModelStr]; 2194 2195 return { 2196 productId: productId, 2197 buttonLabel: 'Change Model', 2198 modelHeight: selectedModel.height, 2199 modelSize: selectedModel.size, 2200 modalID: 'model-selector-toaster__' + productId, 2201 toasterOpenDirection: window.innerWidth >= 700 ? 'from-left' : 'from-bottom', 2202 models: buildModelCardData() 2203 }; 2204 } 2205 2206 function buildModelCardData() { 2207 var productData = Utils.getProductData(productId); 2208 var tempArray = []; 2209 var productModelDataObj = productData.productAttrs.ModelData; 2210 var verifiedModelSegments = productData.productAttrs.ModelData.availableModelImageSetSegments.slice(0); 2211 2212 for (var i = 0; i < verifiedModelSegments.length; i++) { 2213 var modelCard = productModelDataObj[verifiedModelSegments[i]]; 2214 var modelSegmentLetter = verifiedModelSegments[i].split('Model')[1]; 2215 2216 // we are using the second model image for this feature per business requirements hence the below [1] 2217 var imageSrc = ANFImage.instance() 2218 .setImageName(productData.imageSets['model' + modelSegmentLetter][1].id) 2219 .setWidth(96) 2220 .url; 2221 2222 modelCard.src = imageSrc; 2223 modelCard.segment = verifiedModelSegments[i]; 2224 modelCard.isSelected = window.selectedModelSegment.indexOf(modelCard.segment) > -1; 2225 2226 tempArray = tempArray.concat(modelCard); 2227 } 2228 2229 return tempArray; 2230 } 2231 2232 function openModelToaster() { 2233 $(window).trigger('analytics.miscProductFeature', {'type': 'Change Model Button', 'message': 'Click'}); 2234 } 2235 2236 function modelSelected(e) { 2237 var $selectedModel = $(e.currentTarget); 2238 var selectedModelSegment = window.selectedModelSegment = $selectedModel.attr('data-segment'); 2239 $productSection = Utils.getParentProductSection($selectedModel); 2240 var productData = $productSection.data(); 2241 2242 $productSection.find('.model-selector__toaster .model-selector__model-card-container.selected').removeClass('selected'); 2243 $selectedModel.addClass('selected'); 2244 2245 window.firstImageSetType = 'model'; 2246 2247 var reorderedImageArray = imageLoader.reorderImageArray(Utils.getProductData(productData.productid), 'model', selectedModelSegment); 2248 var imageLoaderObject = { 2249 processedImageArray: reorderedImageArray, 2250 imageDataModel: productData.imageDataModel, 2251 productId: productData.productid, 2252 $productSection: $productSection 2253 }; 2254 2255 updateModelButtonInfo(selectedModelSegment, productData); 2256
2257 messageBus.trigger('productThumbnails:reload', imageLoaderObject); 2258 messageBus.trigger('productPageMainImages:modelSelected', imageLoaderObject); 2259 2260 $(window).trigger('analytics.miscProductFeature', {'type': 'Model Image Click', 'action': $selectedModel.data('segment').split('Model')[1], 'message': $selectedModel.closest('ul').children().length}); 2261 } 2262 2263 return publicMembers; 2264}); 2265 2266redesign.define('productModules/helpers/featureSwitchHelper',[ 2267 'productModules/helpers/Utils' 2268], 2269function FeatureSwitchHelper(Utils) { 2270 'use strict'; 2271 2272 var messageBus = $(ANF); 2273 2274 var publicMembers = { 2275 featureTestSwitchValue: function featureTestSwitchValue(testValue, testSwitchName) { 2276 var storeAttribute = testValue || 0; 2277 var optimizely = window[testSwitchName] || false; 2278 2279 switch (storeAttribute) { 2280 case 1: 2281 return optimizely; 2282 case 2: 2283 return true; 2284 default: 2285 return false; 2286 } 2287 }, 2288 2289 checkFeatureSwitchAndSplitTest: function checkFeatureSwitchAndSplitTest(featureAndSplitTestKey, cb) { 2290 var deferredResult = new $.Deferred(); 2291 var featureFlagKey = 'flag.' + featureAndSplitTestKey; 2292 var splitTestKey = 'test.' + featureAndSplitTestKey; 2293 if (window.digitalData.get(featureFlagKey, false ) || window.digitalData.get(splitTestKey, false)) { 2294 deferredResult.resolve(true); 2295 } else { 2296 window.digitalData.subscribe(splitTestKey, function checkTestSegment(segment) { 2297 if (typeof segment !== 'undefined') { 2298 this.unsubscribe(); 2299 deferredResult.resolve(segment); 2300 } 2301 }); 2302 } 2303 2304 if (cb) { 2305 deferredResult.promise() 2306 .then(function heckSegment(isEnabled) { 2307 if (isEnabled) cb(); 2308 }); 2309 } 2310 2311 return deferredResult.promise(); 2312 }, 2313 2314 styleFitDesc: function styleFitDescSwitcher() { 2315 publicMembers.checkFeatureSwitchAndSplitTest('pdp-style-fit-desc-enabled', function styleFitDesc() { 2316 messageBus.trigger('styleFitDesc:init'); 2317 }); 2318 }, 2319
2320 shortDescriptorSplitTest: function shortDescriptorSplitTest() { 2321 var subscriptionTimeout = null; 2322 if (window.digitalData.get('test.pdpHyperlinkDescriptor', false)) { 2323 messageBus.trigger('shortDescriptorTest:init', window.digitalData.get('test.pdpHyperlinkDescriptor', false)); 2324 } else { 2325 var subscription = window.digitalData.subscribe('test.pdpHyperlinkDescriptor', function checkTestSegment(segment) { 2326 if (typeof segment !== 'undefined') { 2327 this.unsubscribe(); 2328 clearTimeout(subscriptionTimeout); 2329 messageBus.trigger('shortDescriptorTest:init', segment); 2330 } 2331 }); 2332 2333 subscriptionTimeout = setTimeout( function setSubscrptionTimeout() { 2334 subscription.unsubscribe(); 2335 }, 5000); 2336 } 2337 }, 2338 2339 stylitics: function styliticsSwitcher() { 2340 if (window.digitalData.get('flag.pdp-classic-outfitting-solution', false)) { 2341 messageBus.trigger('styliticsClassic:init'); 2342 } 2343 2344 this.checkFeatureSwitchAndSplitTest('pdp-hotspots-outfitting-solution', function hasStyliticsV3() { 2345 if (!Utils.hasPDPMFEBelowTheFold()) { 2346 messageBus.trigger('styliticsV3:init'); 2347 } 2348 }); 2349 } 2350 }; 2351 2352 return publicMembers; 2353}); 2354 2355redesign.define('productServices/product/headerHeightCalculator',[],function HeaderHeightCalculator() { 2356 'use strict'; 2357 2358 var headerPrimary = 'header.rs-header:not(:hidden)'; 2359 var globalMessagingSystem = '.rs-nav__gms:not(:hidden)';// ESPOT above/below header 2360 var banner = '.intl-banner:not(:hidden)'; // Banner that may or may not exist 2361 var breadcrumb = '.upper-breadcrumb:not(:hidden)'; 2362 var brandTabs = '.rs-nav__tabs-container:not(hidden)'; 2363 var anfPasId = ANF.params.storeAttributes.pasList.anfPasId; 2364 var holPasId = ANF.params.storeAttributes.pasList.holPasId; 2365 var $rsHeaderWrapper = $('.rs-nav__wrapper'); 2366 2367 // Returns the height of the header plus espots 2368 function getFullHeaderHeight(passId) { 2369 var gmsTotalHeight = 0; 2370 2371 // for multiple gms messages 2372 $.each($(globalMessagingSystem), function each(index, value) { 2373 gmsTotalHeight += $(value).height(); 2374 2375 if (index === 0) { 2376 gmsTotalHeight -= $(window).scrollTop(); // if we have scrolled but not enough to hide top gms banner 2377 } 2378 }); 2379 var defaultHeight = $(headerPrimary).height() + $(banner).height() + $(breadcrumb).height() + $(brandTabs).height(); 2380 if (passId === anfPasId || $rsHeaderWrapper.length) { 2381 return defaultHeight + gmsTotalHeight; 2382 } else if (passId === holPasId) { 2383 return defaultHeight + $(globalMessagingSystem).height(); 2384 } 2385 2386 return 0; 2387 } 2388 2389 var publicMembers = { 2390 2391 // Returns the height of the header section above the nav 2392 getFixedHeaderThresholdHeight: function getFixedHeaderThresholdHeight(passId, withBreadcrumb) { 2393 var breadcrumbHeight = withBreadcrumb ? $(breadcrumb).height() : 0; 2394 var initHeight = $(globalMessagingSystem).height() + $(banner).height() + $(brandTabs).height(); 2395 if (passId === anfPasId || $rsHeaderWrapper.length) { 2396 return breadcrumbHeight + initHeight; 2397 } else if (passId === holPasId) { 2398 return initHeight; 2399 } 2400 2401 return 0; 2402 }, 2403 // Returns the height of the header that will remain on the page when the user scrolls (the sticky header) 2404 getCondensedHeaderHeight: function getCondensedHeaderHeight() { 2405 return $(brandTabs).height() || 0; 2406 }, 2407 2408 // Returns the current total height of all things header, should be the thing modules need most often 2409 // only sticky portion will be used if user is scrolled passed 2410 getCurrentHeaderHeight: function getCurrentHeaderHeight(passId) { 2411 var currentHeaderHeight = getFullHeaderHeight(passId); 2412 var fixedHeaderThresholdHeight = this.getFixedHeaderThresholdHeight(passId, false); 2413 var scrollTop = $(window).scrollTop(); 2414 if (scrollTop > fixedHeaderThresholdHeight) { // if we're beyond the threshold at which the header should be fixed 2415 currentHeaderHeight = this.getCondensedHeaderHeight(); 2416 } 2417 return currentHeaderHeight; 2418 } 2419 }; 2420 2421 return publicMembers; 2422}); 2423 2424redesign.define('productPartial/pdp_addToBagNotification',['handlebars','wcsHelper/i18n','dsPartial/icon-block','coreParti
2424al/freeShippingProgress','corePartial/checkoutButton','dsPartial/button'], function(Handlebars) { 2425 2426this["ANF"] = this["ANF"] || {}; 2427this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2428 2429Handlebars.registerPartial("pdp_addToBagNotification", this["ANF"]["templates"]["pdp_addToBagNotification"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 2430 return " <span class=\"product-page__atb-notification-warning\" data-property=\"GLB_ATB_NOTIFICATION_WARNING\">" 2431 + container.escapeExpression(helpers.i18n.call(depth0 != null ? depth0 : (container.nullContext || {}),"product.GLB_ATB_NOTIFICATION_WARNING",{"name":"i18n","hash":{},"data":data})) 2432 + "</span>\n"; 2433},"3":function(container,depth0,helpers,partials,data) { 2434 var stack1; 2435 2436 return ((stack1 = container.invokePartial(partials.freeShippingProgress,depth0,{"name":"freeShippingProgress","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 2437},"5":function(container,depth0,helpers,partials,data) { 2438 var stack1; 2439 2440 return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.props : depth0)) != null ? stack1.showCheckoutButton : stack1),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 2441},"6":function(container,depth0,helpers,partials,data) { 2442 var stack1; 2443 2444 return ((stack1 = container.invokePartial(partials.checkoutButton,depth0,{"name":"checkoutButton","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 2445},"8":function(container,depth0,helpers,partials,data) { 2446 var stack1; 2447 2448 return " " 2449 + ((stack1 = helpers.i18n.call(depth0 != null ? depth0 : (container.nullContext || {}),"product.GLB_VIEWBAG",{"name":"i18n","hash":{},"data":data})) != null ? stack1 : "") 2450 + "\n"; 2451},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2452 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 2453 2454 return "<div class=\"product-page__atb-notification scope-1892\">\n <span class=\"product-page__atb-notification-added h2\" data-property=\"GLB_ITEM_ADDED_TO_BAG\">" 2455 + container.escapeExpression(helpers.i18n.call(alias1,"product.GLB_ITEM_ADDED_TO_BAG",{"name":"i18n","hash":{},"data":data})) 2456 + "</span>\n <div class=\"atb-notification-warning-wrapper\">\n" 2457 + ((stack1 = container.invokePartial(partials["icon-block"],depth0,{"name":"icon-block","hash":{"data-icon":"clock"},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2458 + " </div>\n\n" 2459 + ((stack1 = helpers["with"].call(alias1,(depth0 != null ? depth0.freeShippingMessage : depth0),{"name":"with","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2460 + "\n" 2461 + ((stack1 = helpers["with"].call(alias1,(depth0 != null ? depth0.checkoutButton : depth0),{"name":"with","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2462 + "\n" 2463 + ((stack1 = container.invokePartial(partials.button,depth0,{"name":"button","hash":{"data-property":"GLB_VIEWBAG","class-list":"product-page__atb-notification-cta ds-override","href":(depth0 != null ? depth0.cartUrl : depth0),"variant":"secondary"},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2464 + "</div>\n"; 2465},"usePartial":true,"useData":true})); 2466 2467return this["ANF"]; 2468 2469}); 2470redesign.define('productPartial/pdp_detailsAccordionContent',['handlebars','wcsHelper/i18n','wcsHelper/siteRegion','dsPartial/button'], function(Handlebars) { 2471 2472this["ANF"] = this["ANF"] || {}; 2473this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2474 2475Handlebars.registerPartial("pdp_detailsAccordionContent", this["ANF"]["templates"]["pdp_detailsAccordionContent"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 2476 var stack1; 2477
2478 return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.imported : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 2479},"2":function(container,depth0,helpers,partials,data) { 2480 return " <span>" 2481 + container.escapeExpression(helpers.i18n.call(depth0 != null ? depth0 : (container.nullContext || {}),"product.DETAILS_ACCORDION.PDP_DETAILS_IMPORTED_TEXT",{"name":"i18n","hash":{},"data":data})) 2482 + "</span>\n"; 2483},"4":function(container,depth0,helpers,partials,data) { 2484 var stack1; 2485 2486 return " <div class=\"anf-cares\">\n <hr>\n <p class=\"text-small\">\n <span>" 2487 + ((stack1 = helpers.i18n.call(depth0 != null ? depth0 : (container.nullContext || {}),"product.DETAILS_ACCORDION.PDP_ACCORDION_DETAILS_CARES_PAGE",{"name":"i18n","hash":{},"data":data})) != null ? stack1 : "") 2488 + " </span>\n </p>\n </div>\n"; 2489},"6":function(container,depth0,helpers,partials,data) { 2490 var stack1; 2491 2492 return " <p class=\"let-us-know below-links text-small\">\n <span class=\"title let-us-know__label\">\n " 2493 + container.escapeExpression(helpers.i18n.call(depth0 != null ? depth0 : (container.nullContext || {}),"product.DETAILS_ACCORDION.GLB_LETUSKNOW",{"name":"i18n","hash":{},"data":data})) 2494 + "\n </span>\n" 2495 + ((stack1 = container.invokePartial(partials.button,depth0,{"name":"button","hash":{"current-state":"initial","variant":"button-as-link","type":"button"},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2496 + " </p>\n"; 2497},"7":function(container,depth0,helpers,partials,data) { 2498 return " <span data-state=\"initial\" data-hidden=\"false\">\n " 2499 + container.escapeExpression(helpers.i18n.call(depth0 != null ? depth0 : (container.nullContext || {}),"product.DETAILS_ACCORDION.PDP_ARE_THESE_DETAILS_HELPFUL_LABEL",{"name":"i18n","hash":{},"data":data})) 2500 + "\n </span>\n"; 2501},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2502 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.escapeExpression, alias3=container.lambda; 2503 2504 return "<div class=\"details-info\">\n <div class=\"h2 details-header\" data-property=\"GLB_DETAILS\">" 2505 + alias2(helpers.i18n.call(alias1,"product.DETAILS_ACCORDION.GLB_DETAILS",{"name":"i18n","hash":{},"data":data})) 2506 + "</div>\n <div class=\"product-title\">" 2507 + alias2(alias3((depth0 != null ? depth0.productName : depth0), depth0)) 2508 + "</div>\n <p class=\"details__description\" itemprop=\"description\">\n " 2509 + ((stack1 = alias3((depth0 != null ? depth0.longDescription : depth0), depth0)) != null ? stack1 : "") 2510 + "\n" 2511 + ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.isChinaSite : depth0),{"name":"unless","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2512 + " </p>\n</div>\n<div class=\"fabric-care\">\n <div class=\"fabric-care__container\">\n <div class=\"h4 fiber-content__label\"></div>\n <ul class=\"fabric-care__list bullet-list\"></ul>\n </div>\n</div>\n<hr>\n\n<p class=\"details__store-item-number text-small\" data-property=\"CAT_STOREITEMNUMBER\">\n <span class=\"details__store-item-title\">" 2513 + alias2(helpers.i18n.call(alias1,"product.DETAILS_ACCORDION.CAT_STOREITEMNUMBER",{"name":"i18n","hash":{},"data":data})) 2514 + " </span>\n <span class=\"number\">" 2515 + alias2(alias3((depth0 != null ? depth0.storeItemNumber : depth0), depth0)) 2516 + "</span>\n</p>\n\n<p class=\"details__web-item-number text-small\" data-property=\"CAT_WEBITEMNUMBER\">\n <span class=\"details__web-item-title\">" 2517 + alias2(helpers.i18n.call(alias1,"product.DETAILS_ACCORDION.CAT_WEBITEMNUMBER",{"name":"i18n","hash":{},"data":data})) 2518 + " </span>\n <span class=\"number\">" 2519 + alias2(alias3((depth0 != null ? depth0.webItemNumber : depth0), depth0)) 2520 + "</span>\n</p>\n\n" 2521 + ((stack1 = helpers.siteRegion.call(alias1,{"name":"siteRegion","hash":{"is":"EU"},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2522 + "\n" 2523 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showForsee : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 2524},"usePartial":true,"useData":true})); 2525 2526return this["ANF"]; 2527 2528}); 2529redesign.define('productPartial/pdp_garmentMeasurements',['handlebars','wcsHelper/i18n','dsPartial/tooltip'], function(Handlebars) { 2530 2531this["ANF"] = this["ANF"] || {}; 2532this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2533 2534Handlebars.registerPartial("pdp_garmentMeasurements", this["ANF"]["templates"]["pdp_garmentMeasurements"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 2535 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.escapeExpression; 2536 2537 return " <div class=\"hr above-garment-measurements\"></div>\n\n <div class=\"garment-header\" data-variant=\"\">\n <h3 class=\"h3\">" 2538 + alias2(helpers.i18n.call(alias1,"product.GARMENT_MEASUREMENT_LABELS.GARMENT_MEASUREMENTS",{"name":"i18n","hash":{},"data":data})) 2539 + "</h3>\n <h4 class=\"h4\">" 2540 + alias2(helpers.i18n.call(alias1,"product.GARMENT_MEASUREMENT_LABELS.GARMENT_MEASUREMENTS",{"name":"i18n","hash":{},"data":data})) 2541 + "</h4>\n" 2542 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.baseSize : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
2543 + " </div>\n\n <ul>\n" 2544 + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.measurements : depth0),{"name":"each","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2545 + " </ul>\n\n"; 2546},"2":function(container,depth0,helpers,partials,data) { 2547 var alias1=container.escapeExpression; 2548 2549 return " <p class=\"garment-taken-from\">\n <span>" 2550 + alias1(helpers.i18n.call(depth0 != null ? depth0 : (container.nullContext || {}),"product.GARMENT_MEASUREMENT_LABELS.BASE_MEASUREMENT_SIZE",{"name":"i18n","hash":{},"data":data})) 2551 + " " 2552 + alias1(container.lambda((depth0 != null ? depth0.baseSize : depth0), depth0)) 2553 + "</span>\n </p>\n"; 2554},"4":function(container,depth0,helpers,partials,data) { 2555 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 2556 2557 return " <li>\n <div class=\"measurement-wrapper\">\n <span class=\"garment-label\">" 2558 + alias2(alias1((depth0 != null ? depth0.garmentLabel : depth0), depth0)) 2559 + ":</span>\n <div class=\"garment-tooltip__wrapper\">\n" 2560 + ((stack1 = container.invokePartial(partials.tooltip,depth0,{"name":"tooltip","hash":{"variant":"small","reference-point":"parent","position":"top-right","id":(depth0 != null ? depth0.tooltipId : depth0)},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2561 + " </div>\n </div>\n <span class=\"garment-value\">" 2562 + alias2(alias1((depth0 != null ? depth0.garmentValue : depth0), depth0)) 2563 + "\"</span>\n </li>\n"; 2564},"5":function(container,depth0,helpers,partials,data) { 2565 var alias1=container.lambda, alias2=container.escapeExpression; 2566 2567 return " <h3 class=\"h3\">" 2568 + alias2(alias1((depth0 != null ? depth0.tooltipHeader : depth0), depth0)) 2569 + "</h3>\n <span>" 2570 + alias2(alias1((depth0 != null ? depth0.tooltipBody : depth0), depth0)) 2571 + "</span>\n"; 2572},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2573 var stack1; 2574 2575 return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.measurements : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 2576},"usePartial":true,"useData":true})); 2577 2578return this["ANF"]; 2579 2580}); 2581redesign.define('productPartial/reviews/pdp_noReviewsData',['handlebars'], function(Handlebars) { 2582 2583this["ANF"] = this["ANF"] || {}; 2584this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2585 2586Handlebars.registerPartial("reviews/pdp_noReviewsData", this["ANF"]["templates"]["reviews/pdp_noReviewsData"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2587 return "<p class=\"reviews-no-results\" data-prop=\"PDP_NO_SEARCH_REVIEWS\">" 2588 + container.escapeExpression(container.lambda((depth0 != null ? depth0.label : depth0), depth0)) 2589 + "</p>"; 2590},"useData":true})); 2591 2592return this["ANF"]; 2593 2594}); 2595redesign.define('productPartial/quickview/pdp_quickviewPreload',['handlebars','dsPartial/loader'], function(Handlebars) { 2596 2597this["ANF"] = this["ANF"] || {}; 2598this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2599 2600Handlebars.registerPartial("quickview/pdp_quickviewPreload", this["ANF"]["templates"]["quickview/pdp_quickviewPreload"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2601 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 2602 2603 return "<section class=\"product-page-v2-modules quickview-preload\" data-product-brand=\"" 2604 + alias2(alias1((depth0 != null ? depth0.productBrand : depth0), depth0)) 2605 + "\">\n <section class=\"product-page__main-container scope-1892\">\n\n <section class=\"product-page-images\">\n <div class=\"product-thumbnails__container\"></div>\n\n <div class=\"product-main-images__wrapper\">\n <div class=\"product-main-images\">\n " 2606 + ((stack1 = alias1((depth0 != null ? depth0.img : depth0), depth0)) != null ? stack1 : "") 2607 + "\n </div>\n </div>\n </section>\n\n <section class=\"product-page__info-container\">\n <div class=\"product-page__info\">\n\n <div class=\"product-page-info__title\">\n <p class=\"short-description\">" 2608 + alias2(alias1((depth0 != null ? depth0.shortDescription : depth0), depth0)) 2609 + " </p>\n <div class=\"product-name product-name-font-size pdp-product-name\" data-brand=\"" 2610 + alias2(alias1((depth0 != null ? depth0.productBrand : depth0), depth0)) 2611 + "\">\n <h1>" 2612 + ((stack1 = alias1((depth0 != null ? depth0.productName : depth0), depth0)) != null ? stack1 : "") 2613 + "</h1>\n </div>\n </div>\n\n <div class=\"product-price-v2\">\n <div class=\"product-price-v2__inner\">\n " 2614 + ((stack1 = alias1((depth0 != null ? depth0.productPrice : depth0), depth0)) != null ? stack1 : "") 2615 + "\n </div>\n </div>\n <div class=\"loader-container\">\n" 2616 + ((stack1 = container.invokePartial(partials.loader,depth0,{"name":"loader","hash":{"label":"loading...","variant":"spinner"},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2617 + " </div>\n\n <div class=\"product-page__atb-saves-wrapper\">\n <div class=\"atb-wrapper\">\n\n <div class=\"drop-down js-drop-down product-quantity-dropdown ds-override \" data-brand=\"" 2618 + alias2(alias1((depth0 != null ? depth0.productBrand : depth0), depth0)) 2619 + "\">\n <select data-filled=\"true\" name=\"quantity\" id=\"product-quantity-dropdown-preload\" disabled>\n <optgroup label=\"Qty\">\n <option value=\"1\">1</option>\n </optgroup>\n </select>\n <label for=\"product-quantity-dropdown-preload\" class=\"placeholder\" data-property=\"GLB_QTY\">" 2620 + alias2(alias1((depth0 != null ? depth0.qtyLabel : depth0), depth0)) 2621 + " </label>
2621\n <span class=\"float-label\" data-property=\"GLB_QTY\">" 2622 + alias2(alias1((depth0 != null ? depth0.qtyLabel : depth0), depth0)) 2623 + "</span>\n <span class=\"arrow-icon\" aria-hidden=\"true\"></span>\n </div>\n\n <button class=\"button ds-override primary-button\" type=\"button\" disabled data-brand=\"" 2624 + alias2(alias1((depth0 != null ? depth0.productBrand : depth0), depth0)) 2625 + "\">\n " 2626 + alias2(alias1((depth0 != null ? depth0.atbLabel : depth0), depth0)) 2627 + "\n </button>\n </div>\n </div>\n </div>\n </section>\n </section>\n</section>"; 2628},"usePartial":true,"useData":true})); 2629 2630return this["ANF"]; 2631 2632}); 2633redesign.define('productPartial/reviews/pdp_reviewFitSection',['handlebars','dsPartial/review-summary-content','dsPartial/icon-block'], function(Handlebars) { 2634 2635this["ANF"] = this["ANF"] || {}; 2636this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2637 2638Handlebars.registerPartial("reviews/pdp_reviewFitSection", this["ANF"]["templates"]["reviews/pdp_reviewFitSection"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 2639 var stack1; 2640 2641 return ((stack1 = container.invokePartial(partials["review-summary-content"],depth0,{"name":"review-summary-content","fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 2642},"2":function(container,depth0,helpers,partials,data) { 2643 var stack1; 2644 2645 return ((stack1 = container.invokePartial(partials["icon-block"],depth0,{"name":"icon-block","hash":{"data-icon":"size"},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 2646},"3":function(container,depth0,helpers,partials,data) { 2647 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 2648 2649 return " <div class=\"review-recommendation review-recommendation-fit\">\n <span class=\"review-recommendation-most-recommended-pre\" data-property=\"GLB_MOST_RECOMMEND\">" 2650 + alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.content : depth0)) != null ? stack1.fitRec : stack1)) != null ? stack1.fitRecText : stack1), depth0)) 2651 + "</span>\n <b class=\"recommendation-value\">" 2652 + alias2(alias1((depth0 != null ? depth0.fitRecommendation : depth0), depth0)) 2653 + "</b>\n </div>\n"; 2654},"5":function(container,depth0,helpers,partials,data) { 2655 var stack1; 2656 2657 return ((stack1 = container.invokePartial(partials["review-summary-content"],depth0,{"name":"review-summary-content","fn":container.program(6, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 2658},"6":function(container,depth0,helpers,partials,data) { 2659 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 2660 2661 return " <div class=\"review-recommendation review-recommendation-percentage\" data-property=\"GLB_RECOMMEND_THIS_ITEM\">\n <b class=\"recommendation-value\">" 2662 + alias2(alias1((depth0 != null ? depth0.recommendedPercentage : depth0), depth0)) 2663 + "% </b>" 2664 + alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.content : depth0)) != null ? stack1.fitRec : stack1)) != null ? stack1.recPercentageText : stack1), depth0)) 2665 + "\n </div>\n"; 2666},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2667 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 2668 2669 return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showFitRecommendation : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2670 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showPercentage : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 2671},"usePartial":true,"useData":true})); 2672 2673return this["ANF"]; 2674 2675}); 2676redesign.define('productPartial/sizeGuide/pdp_sizeGuideCharts',['handlebars','wcsHelper/sizeGuide_tableCellClassNames','wcsHelper/sizeGuide_formatMeasurementUnit','wcsHelper/sizeGuide_zipRowData'], function(Handlebars) { 2677 2678this["ANF"] = this["ANF"] || {}; 2679this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2680 2681Handlebars.registerPartial("sizeGuide/pdp_sizeGuideCharts", this["ANF"]["templates"]["sizeGuide/pdp_sizeGuideCharts"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data,blockParams,depths) { 2682 var stack1, alias1=container.lambda, alias2=container.escapeExpression, alias3=depth0 != null ? depth0 : (container.nullContext || {}); 2683 2684 return " <h3 class=\"size-guide__title h2\">" 2685 + alias2(alias1((depth0 != null ? depth0.name : depth0), depth0)) 2686 + "</h3>\n <p class=\"size-guide__description\">" 2687 + alias2(alias1((depth0 != null ? depth0.description : depth0), depth0)) 2688 + "</p>\n\n <div class=\"size-guide__table-scroll-wrapper\">\n <div class=\"size-guide__table-scroll\">\n\n <table class=\"size-guide__guide " 2689 + alias2(alias1(((stack1 = (data && data.root)) && stack1.defaultUnitClass), depth0)) 2690 + "\">\n <caption class=\"visually-hidden\">\n " 2691 + ((stack1 = alias1(((stack1 = (depth0 != null ? depth0.header : depth0)) != null ? stack1.name : stack1), depth0)) != null ? stack1 : "") 2692 + "\n " 2693 + alias2(alias1((depth0 != null ? depth0.name : depth0), depth0)) 2694 + "\n " 2695 + alias2(alias1(((stack1 = (data && data.root)) && stack1.sizeChartHeader), depth0)) 2696 + "\n </caption>
2696\n\n <span class=\"size-guide__table-shadow size-guide__table-shadow-left size-guide__table-shadow--hide\"></span>\n\n <thead>\n <tr>\n <th scope=\"col \"class=\"size-guide__primary-column\">\n " 2697 + ((stack1 = alias1(((stack1 = (depth0 != null ? depth0.header : depth0)) != null ? stack1.name : stack1), depth0)) != null ? stack1 : "") 2698 + "\n </th>\n\n" 2699 + ((stack1 = helpers.each.call(alias3,((stack1 = ((stack1 = (depth0 != null ? depth0.sizes : depth0)) != null ? stack1["0"] : stack1)) != null ? stack1.types : stack1),{"name":"each","hash":{},"fn":container.program(2, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "") 2700 + " </tr>\n </thead>\n\n <tbody>\n" 2701 + ((stack1 = helpers.each.call(alias3,(depth0 != null ? depth0.sizes : depth0),{"name":"each","hash":{},"fn":container.program(7, data, 1, blockParams, depths),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "") 2702 + " </tbody>\n\n <span class=\"size-guide__table-shadow size-guide__table-shadow-right size-guide__table-shadow--hide\"></span>\n </table>\n\n </div>\n </div>\n"; 2703},"2":function(container,depth0,helpers,partials,data) { 2704 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 2705 2706 return " <th scope=\"col\" class=\"" 2707 + container.escapeExpression(helpers.sizeGuide_tableCellClassNames.call(alias1,(depth0 != null ? depth0.unit : depth0),(depth0 != null ? depth0.country : depth0),((stack1 = (data && data.root)) && stack1.defaultRegion),{"name":"sizeGuide_tableCellClassNames","hash":{},"data":data})) 2708 + "\">" 2709 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.country : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2710 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.measurement : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2711 + "</th>\n"; 2712},"3":function(container,depth0,helpers,partials,data) { 2713 return container.escapeExpression(container.lambda((depth0 != null ? depth0.country : depth0), depth0)); 2714},"5":function(container,depth0,helpers,partials,data) { 2715 var stack1; 2716 2717 return ((stack1 = container.lambda((depth0 != null ? depth0.measurement : depth0), depth0)) != null ? stack1 : "") 2718 + " " 2719 + container.escapeExpression(helpers.sizeGuide_formatMeasurementUnit.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.unit : depth0),{"name":"sizeGuide_formatMeasurementUnit","hash":{},"data":data})); 2720},"7":function(container,depth0,helpers,partials,data,blockParams,depths) { 2721 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 2722 2723 return ((stack1 = helpers.each.call(alias1,helpers.sizeGuide_zipRowData.call(alias1,((stack1 = blockParams[0][0]) != null ? stack1.types : stack1),{"name":"sizeGuide_zipRowData","hash":{},"data":data,"blockParams":blockParams}),{"name":"each","hash":{},"fn":container.program(8, data, 2, blockParams, depths),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : ""); 2724},"8":function(container,depth0,helpers,partials,data,blockParams,depths) { 2725 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 2726 2727 return " <tr " 2728 + ((stack1 = helpers["if"].call(alias1,(depths[1] != null ? depths[1].hasAltBackground : depths[1]),{"name":"if","hash":{},"fn":container.program(9, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "") 2729 + ">\n" 2730 + ((stack1 = helpers.unless.call(alias1,blockParams[0][1],{"name":"unless","hash":{},"fn":container.program(11, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "") 2731 + "\n" 2732 + ((stack1 = helpers.each.call(alias1,blockParams[0][0],{"name":"each","hash":{},"fn":container.program(13, data, 1, blockParams, depths),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "") 2733 + " </tr>\n"; 2734},"9":function(container,depth0,helpers,partials,data) { 2735 return "class=\"alt-background-color\""; 2736},"11":function(container,depth0,helpers,partials,data,blockParams) { 2737 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 2738 2739 return " <th class=\"size-guide__primary-column\" scope=\"row\" rowspan=\"" 2740 + alias2(alias1(((stack1 = ((stack1 = ((stack1 = ((stack1 = blockParams[2][0]) != null ? stack1.types : stack1)) != null ? stack1["0"] : stack1)) != null ? stack1.values : stack1)) != null ? stack1.length : stack1), depth0)) 2741 + "\">\n " 2742 + alias2(alias1(((stack1 = blockParams[2][0]) != null ? stack1.size : stack1), depth0)) 2743 + "\n </th>\n"; 2744},"13":function(container,depth0,helpers,partials,data,blockParams) { 2745 var stack1, alias1=container.escapeExpression; 2746 2747 return " <td class=\"" 2748 + alias1(helpers.sizeGuide_tableCellClassNames.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.unit : depth0),(depth0 != null ? depth0.country : depth0),((stack1 = (data && data.root)) && stack1.defaultRegion),{"name":"sizeGuide_tableCellClassNames","hash":{},"data":data})) 2749 + "\">\n " 2750 + alias1(container.lambda(((stack1 = blockParams[0][0]) != null ? stack1.value : stack1), depth0)) 2751 + "\n </td>\n"; 2752},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) { 2753 var stack1; 2754 2755 return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.sizeCharts : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : ""); 2756},"useData":true,"useDepths":true,"useBlockParams":true})); 2757 2758return this["ANF"]; 2759 2760}); 2761redesign.define('productPartial/sizeGuide/pdp_sizeGuideImages',['handlebars'], function(Handlebars) { 2762 2763this["ANF"] = this["ANF"] || {}; 2764this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2765 2766Handlebars.registerPartial("sizeGuide/pdp_sizeGuideImages", this["ANF"]["templates"]["sizeGuide/pdp_sizeGuideImages"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 2767 var alias1=container.lambda, alias2=container.escapeExpression; 2768 2769 return " <div class='size-guide-image'>\n <img src='" 2770 + alias2(alias1((depth0 != null ? depth0.src : depth0), depth0)) 2771 + "' data-image-id='" 2772 + alias2(alias1((depth0 != null ? depth0.imageId : depth0), depth0)) 2773 + "' alt='" 2774 + alias2(alias1((depth0 != null ? depth0.altText : depth0), depth0)) 2775 + "' itemprop='image'/>\n </div>\n"; 2776},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2777 var stack1; 2778 2779 return ((stack1 = helpers.blockHelperMissing.call(depth0,container.lambda((depth0 != null ? depth0.images : depth0), depth0),{"name":"images","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 2780},"useData":true})); 2781 2782return this["ANF"]; 2783 2784}); 2785redesign.define('productPartial/sizeGuide/pdp_sizeGuideRegionSelector',['handlebars','dsPartial/drop-down'], function(Handlebars) { 2786 2787this["ANF"] = this["ANF"] || {}; 2788this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2789 2790Handlebars.registerPartial("sizeGuide/pdp_sizeGuideRegionSelector", this["ANF"]["templates"]["sizeGuide/pdp_sizeGuideRegionSelector"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2791 var stack1; 2792 2793 return "<div class='size-guide__region-selector'>\n"
2794 + ((stack1 = container.invokePartial(partials["drop-down"],depth0,{"name":"drop-down","hash":{"brand":(depth0 != null ? depth0.productBrand : depth0),"options":(depth0 != null ? depth0.options : depth0),"data-property":(depth0 != null ? depth0.dataProperty : depth0),"value":(depth0 != null ? depth0.value : depth0),"label":(depth0 != null ? depth0.label : depth0),"id":(depth0 != null ? depth0.id : depth0),"class-list":(depth0 != null ? depth0.classList : depth0)},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2795 + "</div>\n"; 2796},"usePartial":true,"useData":true})); 2797 2798return this["ANF"]; 2799 2800}); 2801redesign.define('productPartial/sizeGuide/pdp_sizeGuideUnitToggle',['handlebars','dsPartial/toggle'], function(Handlebars) { 2802 2803this["ANF"] = this["ANF"] || {}; 2804this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2805 2806Handlebars.registerPartial("sizeGuide/pdp_sizeGuideUnitToggle", this["ANF"]["templates"]["sizeGuide/pdp_sizeGuideUnitToggle"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2807 var stack1; 2808 2809 return "<div class='size-guide__unit-toggle-wrapper'>\n\n <div class=\"visually-hidden\" id=\"size-guide-legend\" data-property=\"SIZE_GUIDE_UNIT_TOGGLE_LABEL\">\n " 2810 + container.escapeExpression(container.lambda(((stack1 = (data && data.root)) && stack1.toggleLabel), depth0)) 2811 + "\n </div>\n\n" 2812 + ((stack1 = container.invokePartial(partials.toggle,depth0,{"name":"toggle","hash":{"brand":(depth0 != null ? depth0.productBrand : depth0),"labelled-by":"size-guide-legend","class-list":(depth0 != null ? depth0.classList : depth0),"toggle-block":(depth0 != null ? depth0.toggleBlock : depth0)},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2813 + "</div>\n"; 2814},"usePartial":true,"useData":true})); 2815 2816return this["ANF"]; 2817 2818}); 2819redesign.define('productPartial/pdp_productSizeMessaging',['handlebars'], function(Handlebars) { 2820 2821this["ANF"] = this["ANF"] || {}; 2822this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2823 2824Handlebars.registerPartial("pdp_productSizeMessaging", this["ANF"]["templates"]["pdp_productSizeMessaging"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2825 var stack1, alias1=container.lambda; 2826 2827 return "<h3 class=\"size-messaging__header\">" 2828 + ((stack1 = alias1((depth0 != null ? depth0.header : depth0), depth0)) != null ? stack1 : "") 2829 + "</h3>\n<div class=\"size-messaging__subtitle\">" 2830 + ((stack1 = alias1((depth0 != null ? depth0.subtitle : depth0), depth0)) != null ? stack1 : "") 2831 + "</div>\n<div class=\"size-messaging__message-body\">" 2832 + ((stack1 = alias1((depth0 != null ? depth0.messaging : depth0), depth0)) != null ? stack1 : "") 2833 + "</div>"; 2834},"useData":true})); 2835 2836return this["ANF"]; 2837 2838}); 2839redesign.define('productPartial/stylitics/pdp_styliticsWidget',['handlebars','wcsHelper/i18n'], function(Handlebars) { 2840 2841this["ANF"] = this["ANF"] || {}; 2842this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2843 2844Handlebars.registerPartial("stylitics/pdp_styliticsWidget", this["ANF"]["templates"]["stylitics/pdp_styliticsWidget"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2845 var alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.escapeExpression; 2846 2847 return "<h2 class=\"stylitics-title h1\" data-property=\"PDP_STYLITICS-WIDGET-TITLE\">\n " 2848 + alias2(helpers.i18n.call(alias1,"product.PDP_STYLITICS_WIDGET_TITLE",{"name":"i18n","hash":{},"data":data})) 2849 + "\n</h2>\n<span class=\"stylitics-subtitle\">\n " 2850 + alias2(helpers.i18n.call(alias1,"product.PDP_STYLITICS_WIDGET_SUBTITLE",{"name":"i18n","hash":{},"data":data})) 2851 + "\n</span>\n<span class=\"stylitics-mobile-tip\">\n " 2852 + alias2(helpers.i18n.call(alias1,"product.PDP_STYLITICS_WIDGET_MOBILE_TIP",{"name":"i18n","hash":{},"data":data})) 2853 + "\n</span>\n<div id=\"stylitics-widget\" data-aui=\"stylitics\"></div>"; 2854},"useData":true})); 2855 2856return this["ANF"]; 2857 2858}); 2859redesign.define('productPartial/stylitics/pdp_styliticsWidgetIcons',['handlebars','dsPartial/icon-block'], function(Handlebars) { 2860 2861this["ANF"] = this["ANF"] || {}; 2862this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2863 2864Handlebars.registerPartial("stylitics/pdp_styliticsWidgetIcons", this["ANF"]["templates"]["stylitics/pdp_styliticsWidgetIcons"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 2865 return ""; 2866},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2867 var stack1; 2868 2869 return ((stack1 = container.invokePartial(partials["icon-block"],depth0,{"name":"icon-block","hash":{"data-icon":(depth0 != null ? depth0.iconKey : depth0)},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2870 + "\n"; 2871},"usePartial":true,"useData":true})); 2872 2873return this["ANF"]; 2874 2875}); 2876redesign.define('productPartial/pdp_measurements',['handlebars','dsPartial/tooltip'], function(Handlebars) { 2877 2878this["ANF"] = this["ANF"] || {}; 2879this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 2880 2881Handlebars.registerPartial("pdp_measurements", this["ANF"]["templates"]["pdp_measurements"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 2882 var stack1; 2883 2884 return " <div class=\"measurements\">\n <ul>\n" 2885 + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.measurements : depth0),{"name":"each","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 2886 + " </ul>\n </div>\n"; 2887},"2":function(container,depth0,helpers,partials,data) { 2888 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 2889 2890 return " <li>\n <div class=\"measurement-wrapper\">
2890\n <span class=\"measurement-label\">" 2891 + alias2(alias1((depth0 != null ? depth0.lensMeasurementsLabel : depth0), depth0)) 2892 + ":</span>\n <div class=\"measurement-tooltip__wrapper\">\n" 2893 + ((stack1 = container.invokePartial(partials.tooltip,depth0,{"name":"tooltip","hash":{"variant":"small","reference-point":"parent","position":"top-right","id":(depth0 != null ? depth0.tooltipId : depth0)},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 2894 + " </div>\n </div>\n <span class=\"measurement-value\">" 2895 + alias2(alias1((depth0 != null ? depth0.lensMeasurementsValue : depth0), depth0)) 2896 + " mm</span>\n </li>\n"; 2897},"3":function(container,depth0,helpers,partials,data) { 2898 var alias1=container.lambda, alias2=container.escapeExpression; 2899 2900 return " <h3 class=\"h3\">" 2901 + alias2(alias1((depth0 != null ? depth0.tooltipHeader : depth0), depth0)) 2902 + "</h3>\n <span>" 2903 + alias2(alias1((depth0 != null ? depth0.tooltipBody : depth0), depth0)) 2904 + "</span>\n"; 2905},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 2906 var stack1; 2907 2908 return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.measurements : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 2909},"usePartial":true,"useData":true})); 2910 2911return this["ANF"]; 2912 2913}); 2914redesign.define('productServices/product/template',[ 2915 'productPartial/pdp_addToBagNotification', 2916 'productPartial/pdp_detailsAccordionContent', 2917 'productPartial/pdp_garmentMeasurements', 2918 'productPartial/reviews/pdp_noReviewsData', 2919 'productPartial/quickview/pdp_quickviewPreload', 2920 'productPartial/reviews/pdp_reviewFitSection', 2921 'productPartial/sizeGuide/pdp_sizeGuideCharts', 2922 'productPartial/sizeGuide/pdp_sizeGuideImages', 2923 'productPartial/sizeGuide/pdp_sizeGuideRegionSelector', 2924 'productPartial/sizeGuide/pdp_sizeGuideUnitToggle', 2925 'productPartial/pdp_productSizeMessaging', 2926 'dsPartial/error-message', 2927 'productPartial/stylitics/pdp_styliticsWidget', 2928 'productPartial/stylitics/pdp_styliticsWidgetIcons', 2929 'productPartial/pdp_measurements' 2930], function TemplateService( 2931 productPartialPdpAddToBagNotification, 2932 productPartialPdpDetailsAccordionContent, 2933 productPartialPdpGarmentMeasurements, 2934 productPartialReviewsPdpNoReviewsData, 2935 productPartialQuickviewPdpQuickviewPreload, 2936 productPartialReviewsPdpReviewFitSection, 2937 productPartialSizeGuidePdpSizeGuideCharts, 2938 productPartialSizeGuidePdpSizeGuideImages, 2939 productPartialSizeGuidePdpSizeGuideRegionSelector, 2940 productPartialSizeGuidePdpSizeGuideUnitToggle, 2941 productPartialPdpProductSizeMessaging, 2942 dsPartialErrorMessage, 2943 productPartialPdpStyliticsWidget, 2944 productPartialPdpStyliticsWidgetIcons, 2945 productPartialPdpMeasurements 2946) { 2947 'use strict'; 2948 2949 /* 2950 * Keep this alphabetized 2951 */ 2952 var templateMap = { 2953 addToBagNotification: productPartialPdpAddToBagNotification.templates.pdp_addToBagNotification, 2954 detailsAccordion: productPartialPdpDetailsAccordionContent.templates.pdp_detailsAccordionContent, 2955 errorMessage: dsPartialErrorMessage.templates['error-message'], 2956 garmentMeasurements: productPartialPdpGarmentMeasurements.templates.pdp_garmentMeasurements, 2957 measurements: productPartialPdpMeasurements.templates.pdp_measurements, 2958 noReviewsData: productPartialReviewsPdpNoReviewsData.templates['reviews/pdp_noReviewsData'], 2959 productSizeMessaging: productPartialPdpProductSizeMessaging.templates.pdp_productSizeMessaging, 2960 quickviewPreload: productPartialQuickviewPdpQuickviewPreload.templates['quickview/pdp_quickviewPreload'], 2961 reviewFitSection: productPartialReviewsPdpReviewFitSection.templates['reviews/pdp_reviewFitSection'], 2962 sizeGuideCharts: productPartialSizeGuidePdpSizeGuideCharts.templates['sizeGuide/pdp_sizeGuideCharts'], 2963 sizeGuideImages: productPartialSizeGuidePdpSizeGuideImages.templates['sizeGuide/pdp_sizeGuideImages'], 2964 sizeGuideRegionSelector: productPartialSizeGuidePdpSizeGuideRegionSelector.templates['sizeGuide/pdp_sizeGuideRegionSelector'], 2965 sizeGuideUnitToggle: productPartialSizeGuidePdpSizeGuideUnitToggle.templates['sizeGuide/pdp_sizeGuideUnitToggle'], 2966 styliticsWidget: productPartialPdpStyliticsWidget.templates['stylitics/pdp_styliticsWidget'], 2967 styliticsWidgetIcon: productPartialPdpStyliticsWidgetIcons.templates['stylitics/pdp_styliticsWidgetIcons'] 2968 }; 2969 2970 function renderTemplate(name, data) { 2971 return templateMap[name](data); 2972 } 2973 2974 return { 2975 renderTemplate: renderTemplate 2976 }; 2977}); 2978 2979redesign.define('productIntegrations/stylitics',[
2980 'productServices/product/headerHeightCalculator', 2981 'anfUtil/constants', 2982 'productModules/helpers/Utils', 2983 'productServices/product/template' 2984], 2985function Stylitics(headerHeightCalculator, coreConstants, Utils, templateService) { 2986 'use strict'; 2987 2988 var messageBus = $(ANF); 2989 var quickviewButtonClass = 'product-quickview__button'; 2990 var styliticsItemPanel = '.stylitics-widget-item-container'; 2991 var jumpLinkButtonSelectorClass = '.stylitics-jumplink'; 2992 var widgetWrapperSelectorClass = '.stylitics-widget-wrapper'; 2993 var containerSelector = '.stylitics-container-js'; 2994 var widgetTargetIdSelector = '#stylitics-widget'; 2995 2996 var styliticsBrandToThemeBrand = { 2997 'ANF': 'anf', 2998 'HCo.': 'hol', 2999 'ANF-Kids': 'kids', 3000 'Glh': 'gh', 3001 'st': 'st' 3002 }; 3003 3004 function loadStylitics() { 3005 var $productSection = $('.product-page-v2-modules'); 3006 var $styliticsWidgetWrapper = $(widgetWrapperSelectorClass); 3007 3008 $styliticsWidgetWrapper.prepend($(templateService.renderTemplate('styliticsWidget'))); 3009 var styliticsWidgetJSUrl = '//widget.stylitics.com/v2/widget.js'; 3010 3011 window.$LAB.script(styliticsWidgetJSUrl).wait(function styliticsRetrieved() { 3012 initStylitics($productSection); 3013 }); 3014 3015 messageBus.on('productPageInfo:swatchSelected mfe:SwatchSelected', handleWidgetRefresh); 3016 } 3017 3018 function initStylitics($productSection) { 3019 var productId = $productSection.data('productid'); 3020 var productData = Utils.getProductData(productId); 3021 3022 var options = { 3023 api: { 3024 total: 3, 3025 item_number: productData.kicId 3026 }, 3027 display: { 3028 cols: 3, 3029 ctaText: coreConstants.anf('messaging.catalogV2.STYLITICS.widgetCTAText') || '', 3030 outfitBg: 'transparent', 3031 clickToReveal: true 3032 } 3033 }; 3034 var brandMap = { 3035 'anf': 'abercrombie', 3036 'kids': 'abercrombie', 3037 'hol': 'hollister', 3038 'gh': 'hollister', 3039 'st': 'hollister' 3040 }; 3041 var brand = $('header').data('brand'); 3042 var styliticsBrand = brandMap[brand]; 3043 3044 if (brand === 'gh') { 3045 options.api.tags = 'gillyhicks_outfit'; 3046 } 3047 3048 // The StyliticsWidget comes from a script loaded for a 3rd party post page load. 3049 /* eslint-disable-next-line no-undef */ 3050 var styliticsWidget = new StyliticsWidget(styliticsBrand, 'stylitics-widget', options); 3051 3052 $(widgetTargetIdSelector).data('widget-instance', styliticsWidget); 3053 styliticsWidget.load(onLoadCustom); 3054 } 3055 3056 function onLoadCustom(outfits) { 3057 toggleCustomComponents(outfits); 3058 setupQuickviewButtons(outfits); 3059 $(ANF).trigger('stylitics:initIntersectObserver'); 3060 $(ANF).trigger('stylitics:wireAnalyticTriggers'); 3061 return outfits; 3062 } 3063 3064 function toggleCustomComponents(outfits) { 3065 var $jumpLinkButton = $(jumpLinkButtonSelectorClass); 3066 var $widgetContainer = $(containerSelector); 3067 3068 if (outfits.length > 2) { 3069 $jumpLinkButton.removeClass('hide'); 3070 $widgetContainer.removeClass('hide'); 3071 } else { 3072 $jumpLinkButton.addClass('hide'); 3073 $widgetContainer.addClass('hide'); 3074 } 3075 } 3076 3077 function setupQuickviewButtons(outfits) { 3078 $.each(outfits, function quickviewLinkOutfitLoop(i, outfit) { 3079 $.each(outfit.items, function outfitItemLoop(idx, item) { 3080 var $itemCTAPanel = $(styliticsItemPanel + '[data-item-id="' + item.item_id + '"]'); 3081 if (item.hasOwnProperty('other_client_item_ids')) { 3082 var itemIdObject = item.other_client_item_ids; 3083 $itemCTAPanel.addClass(quickviewButtonClass); 3084 $itemCTAPanel.attr({ 3085 'data-collectionid': itemIdObject.hasOwnProperty('collection_ids') ? itemIdObject.collection_ids[0] : 0, 3086 'data-seq': itemIdObject.hasOwnProperty('csequence_ids') ? itemIdObject.csequence_ids[0] : 0, 3087 'data-productid': itemIdObject.hasOwnProperty('product_ids') ? itemIdObject.product_ids[0] : 0, 3088 'data-product-brand': styliticsBrandToThemeBrand[item.brand] 3089 }); 3090 } 3091 }); 3092 }); 3093 } 3094 3095 function handleWidgetRefresh(_e, $selectedSwatch, $productSection, productId) { 3096 if (!Utils.isQuickviewOpened) {
3097 var selectedKIC = Utils.getProductData(productId).kicId; 3098 var $widgetTarget = $(widgetTargetIdSelector); 3099 var styliticsWidget = $widgetTarget.data('widget-instance'); 3100 3101 var currentHeight = $widgetTarget.height(); 3102 $widgetTarget.height(currentHeight); 3103 3104 styliticsWidget.refresh(selectedKIC, function styliticsSwatchRefresh(outfits) { 3105 $widgetTarget.height('auto'); 3106 onLoadCustom(outfits); 3107 }); 3108 } 3109 } 3110 3111 var publicMembers = { 3112 'listen': function listen() { 3113 messageBus.on('styliticsClassic:init', loadStylitics); 3114 } 3115 }; 3116 3117 return publicMembers; 3118}); 3119 3120redesign.define('productIntegrations/styliticsAnalytics',[], 3121 function StyliticsAnalytics() { 3122 'use strict'; 3123 3124 var jumplinkSelectorClass = '.stylitics-jumplink'; 3125 var widgetIdSelector = '#stylitics-widget'; 3126 var outfitInnerWrapperSelectorClass = '.stylitics-widget-outfit-inner'; 3127 3128 return function StytlicsAnalyticsClosure(productIntersectionObserver) { 3129 function wireIntersectionObserver() { 3130 var $styliticsWidget = $(widgetIdSelector); 3131 var styliticsWidgetObserver = new productIntersectionObserver.IntersectionObserver( 3132 function productIntersectionObserverCallback(entries) { 3133 for (var i = 0, len = entries.length; i < len; i++) { 3134 var styliticsWidget = entries[i]; 3135 3136 if (styliticsWidget.isIntersecting) { 3137 styliticsWidgetObserver.unobserve(styliticsWidget.target); 3138 $(window).trigger('analytics.scrollReachIndicator', {'name': 'Scrolled to Stylitics Module'}); 3139 } 3140 } 3141 }, 3142 { 3143 threshold: 1 3144 } 3145 ); 3146 styliticsWidgetObserver.observe($styliticsWidget[0]); 3147 } 3148 3149 function viewMoreOutfitsClickTrigger() { 3150 $(window).trigger('analytics.miscFeatureSessionCap', {'type': 'Stylitics', 'action': 'click', 'message': 'shop this link'}); 3151 } 3152 3153 function jumplinkClickAnalyticTrigger() { 3154 $(window).trigger('analytics.miscFeature', {type: 'Stylitics', action: 'click', message: 'how to style this'}); 3155 } 3156 3157 function wireUserInteractionTriggers() { 3158 $(widgetIdSelector).on('click', outfitInnerWrapperSelectorClass, viewMoreOutfitsClickTrigger); 3159 } 3160 3161 var publicMembers = { 3162 'listen': function listen() { 3163 $(ANF).on('stylitics:wireAnalyticTriggers', wireUserInteractionTriggers); 3164 $(ANF).on('stylitics:initIntersectObserver', wireIntersectionObserver); 3165 }, 3166 'bindEvents': function bindEvents() { 3167 $(jumplinkSelectorClass).on('click select', jumplinkClickAnalyticTrigger); 3168 } 3169 }; 3170 3171 return publicMembers; 3172 }; 3173 } 3174); 3175 3176redesign.define('productModules/helpers/productIntersectionObserver',[], function productIntersectionObserver() { 3177 'use strict'; 3178 3179 var helpers = { 3180 create: function create(getName, getValue, isConfigurable) { 3181 if (Object.hasOwnProperty(this, getName) && Object.getOwnPropertyDescriptor(this, getName).configurable) { 3182 delete this[getName]; 3183 } 3184 3185 Object.defineProperty(this, getName, { 3186 get: getValue, 3187 configurable: isConfigurable || false 3188 }); 3189 } 3190 }; 3191 3192 return helpers; 3193}); 3194 3195redesign.define('productIntegrations/styliticsV3',[ 3196 'productModules/helpers/Utils', 3197 'productServices/product/template' 3198], 3199function StyliticsV3(Utils, templateService) { 3200 'use strict'; 3201 3202 var messageBus = $(ANF); 3203 var widgetWrapperSelector = '.stylitics-widget-wrapper'; 3204 var containerSelector = '.stylitics-container-js'; 3205 var widgetCtaEyeIconContainerSelector = '.stylitics-view-items-cta-icon'; 3206 var productSectionSelector = '.product-page-v2-modules'; 3207 var styliticsModalBadgeContainerSelector = '.stylitics-bundle-badge-modal'; 3208 var styliticsModalBadgeContentClass = 'stylitics-modal-badge-js'; 3209 var styliticsBadgeContainerSelector = '.stylitics-bundle-badge'; 3210 var styliticsBadgeContentClass = 'stylitics-bundle-badge-js'; 3211 var widgetTargetIdSelector = '#stylitics-widget'; 3212 var viewItemsButton = '.stylitics-view-items-button'; 3213 var brandMap = { 3214 'anf': 'abercrombie', 3215 'kids': 'abercrombie', 3216 'hol': 'hollister', 3217 'gh': 'hollister', 3218 'st': 'hollister' 3219 }; 3220 3221 var styliticsBrandToThemeBrand = { 3222 'ANF': 'anf', 3223 'HCo.': 'hol', 3224 'ANF-Kids': 'kids', 3225 'Glh': 'gh', 3226 'st': 'st' 3227 }; 3228 3229 function listen() { 3230 messageBus.on('styliticsV3:init', loadStylitics); 3231 } 3232 3233 function loadStylitics() { 3234 if (Utils.isExternalUser()) { 3235 return; 3236 } 3237 3238 var $productSection = $(productSectionSelector); 3239 var $styliticsWidgetWrapper = $(widgetWrapperSelector); 3240 var $widgetContainer = $(containerSelector); 3241 3242 if ($styliticsWidgetWrapper.length === 0 || $widgetContainer.length === 0) { 3243 return; 3244 } 3245 if (Utils.getANFObject().params.env.region === 'EU') { 3246 $styliticsWidgetWrapper.addClass('hide-price'); 3247 } 3248 3249 var styliticsWidgetJSUrl = '//web-assets.stylitics.com/v3-hotspots/latest/hotspots.release.js'; 3250 $styliticsWidgetWrapper.prepend($(templateService.renderTemplate('styliticsWidget'))); 3251 window.$LAB.script(styliticsWidgetJSUrl).wait(function styliticsRetrieved() { 3252 initStylitics($productSection); 3253 }); 3254 3255 messageBus.on('productPageInfo:swatchSelected mfe:SwatchSelected', handleWidgetRefresh); 3256 } 3257 3258 function initStylitics($productSection) { 3259 var productId = $productSection.data('productid'); 3260 var params = buildParameters(productId); 3261 var productData = Utils.getProductData(productId); 3262 3263 var showStylitics = shouldShowStylitics(productData.kicId); 3264 3265 if ($(widgetTargetIdSelector).length === 0) { 3266 return; 3267 } 3268 3269 /* eslint-disable-next-line no-undef */ 3270 var styliticsWidget = new StyliticsHotspotsWidget(params.styliticsBrand, 'stylitics-widget', params.options); 3271 3272 $(widgetTargetIdSelector).data('widget-instance-v3', styliticsWidget); 3273 3274 styliticsWidget.on('mount', 'bundle', onLoadSingleBundle); 3275 styliticsWidget.on('mount', 'bundles', sendBundlesMfe); 3276 styliticsWidget.on('mount', 'item', replaceImages); 3277 styliticsWidget.override('click', 'item', handleStyliticsItemClick); 3278 if (showStylitics) { 3279 styliticsWidget.start(); 3280 } 3281 } 3282 3283 function shouldShowStylitics(selectedKIC) { 3284 var departmentId = selectedKIC.split(/[_-]/)[1]; 3285 var dressOrRomperDepartment = '359';
3286 var hideStyliticsHcoDressesAndRompers = window.digitalData.get('test.hideStyliticsHcoDressesAndRompers') === true; 3287 3288 return !(hideStyliticsHcoDressesAndRompers && departmentId === dressOrRomperDepartment); 3289 } 3290 3291 function buildParameters(productId) { 3292 var productData = Utils.getProductData(productId); 3293 var options = { 3294 api: { 3295 min: 3, 3296 max: 3, 3297 item_number: productData.kicId 3298 }, 3299 display: { 3300 clickableCarouselDots: true, 3301 swipeableCarouselDots: true, 3302 disableMnM: true, 3303 bundleBackgroundColor: 'transparent' 3304 }, 3305 price: { 3306 hideDoubleZeroCents: true 3307 }, 3308 customer: { 3309 locale: Utils.getANFObject().params.storeAttributes.STYLITICS_LOCALE 3310 }, 3311 text: { 3312 viewDetailsCTA: Utils.getANFObject().messaging.product.PDP_STYLITICS_WIDGET_CTA 3313 } 3314 }; 3315 var brand = $('header').data('brand'); 3316 var styliticsBrand = brandMap[brand]; 3317 if (brand === 'gh') { 3318 options.api.tags = 'gillyhicks_outfit'; 3319 } 3320 3321 if (Utils.getANFObject().params.env.region === 'UK') { 3322 styliticsBrand += '_uk'; 3323 } 3324 3325 if (Utils.getANFObject().params.env.region === 'EU') { 3326 styliticsBrand += '_eu'; 3327 } 3328 3329 return { 3330 styliticsBrand: styliticsBrand, 3331 options: options 3332 }; 3333 } 3334 3335 function onLoadSingleBundle(outfit) { 3336 var hasModelBadge = outfit.bundle['on-model-image']; 3337 3338 setupCtaButton(outfit.element); 3339 3340 if (hasModelBadge) { 3341 var text = Utils.getANFObject().messaging.product.PDP_STYLITICS_WIDGET_BADGE_TEXT; 3342 var $element = $(outfit.element); 3343 3344 cardBadgeSetup($element, text); 3345 modalBadgeSetup($element, text); 3346 } 3347 } 3348 3349 function sendBundlesMfe(outfits) { 3350 var $widgetContainer = $(containerSelector); 3351 3352 if ($widgetContainer.length === 0) { 3353 return; 3354 } 3355 3356 var canPost = typeof window !== 'undefined' && window.postMessage && window.addEventListener; 3357 3358 if (canPost) { 3359 window.digitalData.set('bundles', outfits.bundles); 3360 } 3361 3362 if (outfits.bundles.length > 2) { 3363 $widgetContainer.removeClass('hide'); 3364 } else { 3365 $widgetContainer.addClass('hide'); 3366 } 3367 } 3368 3369 function replaceImages(data) { 3370 var originalUrl = data.item.client_original_image_url; // "https://img.abercrombie.com/is/image/anf/KIC_154-2052-0663-900_prod1" 3371 3372 var $img = $(data.element).find('.stylitics-item-image'); 3373 var imageURL = originalUrl + '?policy=product-small'; 3374 3375 $img.attr('data-src', imageURL); 3376 } 3377 3378 function handleStyliticsItemClick(data) { 3379 var itemClicked = data.item.other_client_item_ids; 3380 var quickviewData = { 3381 productId: itemClicked.product_ids[0], 3382 collectionId: itemClicked.collection_ids[0], 3383 seq: itemClicked.csequence_ids[0], 3384 imageId: data.item.remote_id + '_prod1', 3385 productName: data.item.name, 3386 productBrand: styliticsBrandToThemeBrand[data.item.brand], 3387 returnFocus: getReturnFocus(data.element), 3388 faceout: 'prod', 3389 isStylitics: true 3390 }; 3391 3392 window.dispatchEvent(new CustomEvent('quickview:openModal', {detail: quickviewData})); 3393 } 3394 3395 function getReturnFocus(element) { 3396 var $returnFocus = $(element); 3397 3398 if ($returnFocus.hasClass('stylitics-overlay-container')) { 3399 $returnFocus = $returnFocus.closest('.stylitics-bundle-container').find('.stylitics-cta'); 3400 } else if ($returnFocus.hasClass('stylitics-item-container')) { 3401 $returnFocus = $returnFocus.find('.stylitics-cta-link'); 3402 } 3403 3404 return $returnFocus; 3405 } 3406 3407 function triggerViewLook() { 3408 $(window).trigger('styliticsV3Item:viewLook'); 3409 } 3410 3411 function setupCtaButton(element) { 3412 var $viewItemsButton = $(viewItemsButton); 3413 $viewItemsButton.unbind().on('click', triggerViewLook); 3414 3415 var $ctaEyeIconContainer = $(element).find(widgetCtaEyeIconContainerSelector); 3416 var brand = $('header').data('brand'); 3417 var iconKey = brandMap[brand] === 'abercrombie' ? 'eye-anf' : 'eye-hco'; 3418 var $eyeIcon = $(templateService.renderTemplate('styliticsWidgetIcon', { iconKey: iconKey })); 3419 3420 $ctaEyeIconContainer.find('svg').remove(); 3421 $ctaEyeIconContainer.append($eyeIcon); 3422 } 3423 3424 function modalBadgeSetup($element, text) { 3425 var $badgeContainer = $element.find(styliticsModalBadgeContainerSelector); 3426 var $badgeContent = $('<span></span>'); 3427 3428 $badgeContent.text(text); 3429 $badgeContent.addClass(styliticsModalBadgeContentClass); 3430 $badgeContainer.empty(); 3431 $badgeContainer.append($badgeContent); 3432 } 3433 3434 function cardBadgeSetup($element, text) { 3435 var $badgeContainer = $element.find(styliticsBadgeContainerSelector); 3436 var $badgeContent = $('<span></span>'); 3437 3438 $badgeContent.text(text); 3439 $badgeContent.addClass(styliticsBadgeContentClass); 3440 $badgeContainer.empty(); 3441 $badgeContainer.append($badgeContent); 3442 } 3443 3444 function handleWidgetRefresh(_e, $selectedSwatch, $productSection, productId) {
3445 if (!Utils.isQuickviewOpened) { 3446 var selectedKIC = Utils.getProductData(productId).kicId; 3447 var $widgetTarget = $(widgetTargetIdSelector); 3448 var styliticsWidget = $widgetTarget.data('widget-instance-v3'); 3449 3450 var showStylitics = shouldShowStylitics(selectedKIC); 3451 3452 if (styliticsWidget && showStylitics) { 3453 styliticsWidget.refresh({ api: { item_number: selectedKIC }}); 3454 } 3455 3456 if (!styliticsWidget && showStylitics) { 3457 restartModule(); 3458 } 3459 } 3460 } 3461 3462 function restartModule() { 3463 var $productSection = $(productSectionSelector); 3464 $(widgetTargetIdSelector).empty(); 3465 initStylitics($productSection); 3466 } 3467 3468 var publicMembers = { 3469 'listen': listen 3470 }; 3471 3472 return publicMembers; 3473}); 3474 3475redesign.define('productIntegrations/styliticsV3Analytics',[], 3476 function StyliticsV3Analytics() { 3477 'use strict'; 3478 3479 var widgetTargetIdSelector = '#stylitics-widget'; 3480 3481 function trackItemAddToBag(e, itemData) { 3482 var styliticsWidget = $(widgetTargetIdSelector).data('widget-instance-v3'); 3483 if (styliticsWidget && typeof styliticsWidget.trackAddItemToCart === 'function') { 3484 styliticsWidget.trackAddItemToCart(itemData.kicId); 3485 } 3486 } 3487 3488 var publicMembers = { 3489 'listen': function listen() { 3490 $(ANF).on('styliticsV3Item:addToBag', trackItemAddToBag); 3491 } 3492 }; 3493 3494 return publicMembers; 3495 }); 3496 3497redesign.define('productSections/product/productPageImages',[ 3498 'coreSectionHelpers', 3499 'productModules/product/productThumbnails', 3500 'productModules/product/productMainImage', 3501 'productModules/product/productZoom', 3502 'productModules/product/productModelSelector', 3503 'productModules/product/productImageLoader', 3504 'coreServices/window', 3505 'productModules/helpers/featureSwitchHelper', 3506 'productIntegrations/stylitics', 3507 'productIntegrations/styliticsAnalytics', 3508 'productModules/helpers/productIntersectionObserver', 3509 'productIntegrations/styliticsV3', 3510 'productIntegrations/styliticsV3Analytics' 3511], function ProductPageImages( 3512 coreSectionHelpers, 3513 thumbnails, 3514 mainImage, 3515 zoom, 3516 productModelSelector, 3517 imageLoader, 3518 windowService, 3519 featureSwitchHelper, 3520 stylitics, 3521 styliticsAnalytics, 3522 productIntersectionObserver, 3523 styliticsV3, 3524 styliticsV3Analytics 3525) { 3526 'use strict'; 3527 3528 var messageBus = $(ANF); 3529 3530 var privateMembers = { 3531 'DEFAULT_MODULES': { 3532 '.product-thumbnails': thumbnails, 3533 '.product-main-images': mainImage, 3534 'productZoom': zoom, 3535 '.model-selector': productModelSelector, 3536 'productImages': imageLoader, 3537 'stylitics': stylitics, 3538 'styltitcsAnalytics': styliticsAnalytics(productIntersectionObserver), 3539 'styliticsV3': styliticsV3, 3540 'styliticsV3Analytics': styliticsV3Analytics 3541 }, 3542 'GLOBAL_SCOPE': '', 3543 'parentPage': '', 3544 'MODULES': {} 3545 }; 3546 3547 return function productImages(settings) { 3548 privateMembers.GLOBAL_SCOPE = settings.scope; 3549 privateMembers.parentPage = settings.parentPage; 3550 privateMembers.MODULES = privateMembers.DEFAULT_MODULES; 3551 3552 if (typeof settings.modules === 'object') { 3553 $.extend(true, privateMembers.MODULES, settings.modules); 3554 } 3555 3556 return { 3557 init: function productPageImagesInit() { 3558 coreSectionHelpers.init({ 3559 'modules': privateMembers.MODULES, 3560 'scope': privateMembers.GLOBAL_SCOPE 3561 }); 3562 }, 3563 subscribe: function productPageImagesSubscribe(settingsObject) { 3564 var options = $.extend({}, privateMembers, settingsObject); 3565 messageBus.on('productPageMain:init', options.parentPage.productPageImages.productPageImagesLoad); 3566 }, 3567 productPageImagesLoad: function productPageImagesLoad() { 3568 messageBus.trigger('productPageImages:load', { URL: windowService.location.get(), isQuickview: false }); 3569 featureSwitchHelper.stylitics(); 3570 } 3571 }; 3572 }; 3573}); 3574 3575redesign.define('productSections/product/productPageReviews',[ 3576 'coreSectionHelpers' 3577], function ProductPageReviews(coreSectionHelpers) { 3578 'use strict'; 3579 3580 var privateMembers = { 3581 'GLOBAL_SCOPE': '', 3582 'MODULES': {}, 3583 'parentPage': '' 3584 }; 3585 3586 return function scope(settings) { 3587 privateMembers.GLOBAL_SCOPE = settings.scope; 3588 privateMembers.parentPage = settings.parentPage; 3589 3590 if (typeof settings.modules === 'object') { 3591 $.extend(true, privateMembers.MODULES, settings.modules); 3592 } 3593 3594 var messageBus = $(ANF); 3595 3596 return {
3597 'init': function init() { 3598 coreSectionHelpers.init({ 3599 'scope': privateMembers.GLOBAL_SCOPE 3600 }); 3601 }, 3602 'subscribe': function subscribe() { 3603 messageBus.on('productPageMain:init', privateMembers.parentPage.productPageReviews.productPageReviewsLoad); 3604 messageBus.on('productPageMain:init', privateMembers.parentPage.productPageReviews.productPageReviewsSummaryLoad); 3605 }, 3606 'productPageReviewsLoad': function productPageReviewsLoad() { 3607 messageBus.trigger('productPageReviews:load'); 3608 } 3609 }; 3610 }; 3611}); 3612 3613redesign.define('productModules/product/productSizes',[ 3614 'productModules/helpers/Utils', 3615 'coreServices/window', 3616 'coreServices/url', 3617 'dsPartial/icon-block' 3618], function ProductSizes( 3619 Utils, 3620 windowService, 3621 urlService, 3622 dsPartialIconBlock 3623) { 3624 'use strict'; 3625 3626 var messageBus = $(ANF); 3627 var productSectionSelectorClass = '.product-page-v2-modules'; 3628 var productAttrClass = '.product-attrs__attr'; 3629 var productSizeAttrClass = '.js-product-attributes-inline .product-sizes .product-attrs__attr input'; 3630 var productUrgencyMsgClass = '.product-urgency-message'; 3631 var skus = '.product-page__skus'; 3632 var inventoryBackorderableStatus = 'Backorderable'; 3633 var inventoryAvailableStatus = 'Available'; 3634 var productWrap = '.product-page__swatches-attributes'; 3635 var selectedSkuParameterName = 'sizeValue'; 3636 var errorContainerSelectorClass = '.validator-container'; 3637 var ajaxErrorClass = 'atb-ajax-error'; 3638 var quickviewPrimaryActionContainerClass = '.quick-view__primary-action-wrapper'; 3639 var quickviewProductInfoContainerSelectorClass = '.product-quickview__modal[data-open="true"] .product-page__info-container'; 3640 var partNumberSelector = 'input[name="partNumber"]'; 3641 var productPageFormSelectorClass = '.product-page-add-to-bag__container'; 3642 var quickviewErrorClass = 'qv-error'; 3643 var inputSelector = 'input'; 3644 var fadeSpeed = 100; 3645 var fadeEasing = 'linear'; 3646 var checked = 'checked'; 3647 var pageUrl = windowService.location.getHref(); 3648 var productPagePrimarySizeList = '.product-page__swatches-attributes .product-sizes-primary ul li'; 3649 var productPageATBButtonSelectorClass = '.product-page-add-to-bag__button'; 3650 var showUrgencyMessageClass = 'product-urgency-message__show'; 3651 var productSizesSelectorClass = '.product-sizes'; 3652 var productSizesCurrentSelectedClass = '.product-sizes__name__current-selected'; 3653 var selectedClass = 'selected'; 3654 3655 function sizeOnLoad() { 3656 var selectedSku = urlService.deconstruct(pageUrl) 3657 .search 3658 .reduce(function selectedSkuParameterReducer(acum, queryStringValueInstance) { 3659 return queryStringValueInstance.name === selectedSkuParameterName ? 3660 queryStringValueInstance.value : 3661 acum; 3662 }, ''); 3663 3664 // if there is a selected sku in the url, 3665 if (selectedSku && selectedSku.length > 0) { 3666 var $skus = $(skus); 3667 var $selectedSkuOption = $skus.children('option[value="' + selectedSku + '"]'); 3668 3669 if ($selectedSkuOption.length) { 3670 var $product = $selectedSkuOption.closest(productWrap); 3671 var sizePrimary = $selectedSkuOption.data('size-primary'); 3672 var $sizePrimaryTile = $product.find(productAttrClass + ' [name^="size-primary"][value="' + sizePrimary + '"]'); 3673 var sizeSecondary = $selectedSkuOption.data('size-secondary'); 3674 var $sizeSecondaryTile = $product.find(productAttrClass + ' [name^="size-secondary"][value="' + sizeSecondary + '"]'); 3675 3676 if ($sizePrimaryTile.length) { 3677 $sizePrimaryTile.prop(checked, true).closest(productAttrClass).addClass('selected'); 3678 } 3679 3680 if ($sizeSecondaryTile.length) { 3681 $sizeSecondaryTile.prop(checked, true).closest(productAttrClass).addClass('selected'); 3682 } 3683 3684 messageBus.trigger('productMultipleAttributeFilter.attributeUpdate'); 3685 } 3686 } 3687 3688 // if single size 3689 var $sizeList = Utils.isQuickviewOpened ? $('.product-quickview__modal[data-open="true"]').find(productPagePrimarySizeList) : $(productPagePrimarySizeList); 3690 if ($sizeList.length === 1) { 3691 var $input = $sizeList.find('input'); 3692 $input.attr('checked', ''); 3693 displaySelected($input, true); 3694 } 3695 } 3696 3697 function skuChangeHandler(e, data) { 3698 var sku = data.sku || ''; 3699 var $skuContainer = data.skuContainer; 3700 var $targetSku; 3701 var inventory; 3702 var inventoryStatus; 3703 3704 $targetSku = $skuContainer.find(skus).children('option[value="' + sku + '"]'); 3705 inventory = $targetSku.data('inventory'); 3706 inventoryStatus = $targetSku.data('inventory-status'); 3707 3708 updateUrgencyMessage(inventory, inventoryStatus, $skuContainer, $targetSku); 3709 } 3710 3711 function updateUrgencyMessage(inventory, inventoryStatus, $skuContainer, $targetSku) { 3712 var urgencyMessageCount = ANF.params.storeAttributes.catalogV2.UrgencyMessageCount; 3713 var $productUrgencyMsg = $skuContainer.find(productUrgencyMsgClass); 3714 var $urgencyScreenReaderMsg = $productUrgencyMsg.find('.urgency-message__sku_screen-reader-text'); 3715 var message; 3716 if (inventoryStatus === inventoryBackorderableStatus) { 3717 $productUrgencyMsg.addClass(showUrgencyMessageClass); 3718 message = ANF.messaging.catalog.BACKORDER_URGENCY_MESSAGE; 3719 } else if (inventory < urgencyMessageCount && inventory > 0 && inventoryStatus === inventoryAvailableStatus) { 3720 // If there are less then 5 of a product in stock, display urgent message. 3721 3722 if (!$productUrgencyMsg.hasClass('show')) { 3723 $productUrgencyMsg.addClass('fadein').removeClass('fadeout hide default-hidden'); 3724 setTimeout(function timeoutBump() { 3725 $productUrgencyMsg.removeClass('fadein').addClass('bump'); 3726 3727 setTimeout(function timeoutShow() { 3728 $productUrgencyMsg.removeClass('bump').addClass('show'); 3729 $urgencyScreenReaderMsg.html(message); 3730 }, 500); 3731 }, 500); 3732 } else { 3733 $productUrgencyMsg.removeClass('show hide').addClass('bump'); 3734 3735 if ($skuContainer.closest('.product-page__info-container').hasClass('is_stuck')) { 3736 setTimeout(function timeoutShow() { 3737 $productUrgencyMsg.removeClass('bump').addClass('show'); 3738 }, 650); 3739 } else { 3740 setTimeout(function timeoutShow() { 3741 $productUrgencyMsg.removeClass('bump').addClass('show'); 3742 $urgencyScreenReaderMsg.html(message); 3743 }, 500); 3744 } 3745 } 3746 3747 message = addSizeToUrgencyMessage($skuContainer, $targetSku); 3748 } else { 3749 if (!$productUrgencyMsg.hasClass('hide')) { 3750 $productUrgencyMsg.removeClass('show').addClass('fadeout'); 3751 setTimeout(function timeoutHide() { 3752 $productUrgencyMsg.removeClass('fadeout').addClass('hide'); 3753 $urgencyScreenReaderMsg.html(''); 3754 }, 450); 3755 } 3756 3757 message = $productUrgencyMsg.find('.icon-block-heading').text(); 3758 } 3759 3760 var iconBlockSettings = { 3761 'data-icon': 'clock', 3762 'icon-block-optional-content': { 3763 'icon-block-heading': message 3764 } 3765 }; 3766 3767 var template = dsPartialIconBlock.templates['icon-block'](iconBlockSettings); 3768 $productUrgencyMsg.find('.urgency-message-content').html(template); 3769 } 3770 3771 function addSizeToUrgencyMessage($skuContainer, $targetSku) { 3772 var message = ANF.messaging.catalog.STOCK_URGENCY_MESSAGE; 3773 var skuData = $targetSku.data(); 3774 var size = skuData.urgencyPrimary; 3775 3776 if (skuData.urgencySecondary && $skuContainer.find('.product-sizes-secondary').length > 0) { 3777 size += ' X ' + skuData.urgencySecondary; 3778 } 3779 3780 if ($skuContainer.closest('.product-page-sticky-size-tile__container').length === 0) { 3781 $(window).trigger('analytics.miscProductFeature', {'type': 'low inventory message', 'action': 'SKU level', 'message': size}); 3782 } 3783 3784 return message.replace(/\{(.*)\}/, size); 3785 } 3786 3787 function sizeTileClickHandler(e) { 3788 var $selectedSizeTile = $(e.currentTarget).parents('.product-attrs__attr'); 3789 if (!$selectedSizeTile.hasClass('unavailable')) { 3790 selectProductAttrHandler($selectedSizeTile); 3791 displaySelected($selectedSizeTile); 3792 } 3793 } 3794 3795 function stickySizeTileSelectedHandler(e, data) { 3796 selectProductAttrHandler(data.$selectedTile); 3797 } 3798 3799 function selectProductAttrHandler($selectedSizeTile) { 3800 // get the size tile in the pdp, and the tile in the sticky tray 3801 var sizeValue = $selectedSizeTile.find(inputSelector).data('size'); 3802 var $sizes = $selectedSizeTile 3803 .parents(productSectionSelectorClass) 3804 .find('input[data-size="' + sizeValue + '"]') 3805 .closest(productAttrClass); 3806 3807 // resolve product section and active form 3808 var $productSection = $selectedSizeTile.closest(productSectionSelectorClass); 3809 var $atbForm = $productSection.find(productPageFormSelectorClass).add(quickviewPrimaryActionContainerClass); 3810 3811 handleSizeTileSelection($sizes); 3812 3813 // clear the error message when a size tile is selected. 3814 $atbForm.find(errorContainerSelectorClass).fadeOut(fadeSpeed, fadeEasing); 3815 $atbForm.removeClass(ajaxErrorClass); 3816 $atbForm.find(productPageATBButtonSelectorClass).removeAttr('disabled'); 3817 3818 $(quickviewProductInfoContainerSelectorClass).removeClass(quickviewErrorClass); 3819 3820 triggerAttributeClicked($selectedSizeTile, $productSection); 3821 dataBridgeSkuUpdateHandler($productSection); 3822 } 3823 3824 function handleSizeTileSelection($selectedSizes) { 3825 var sizeTiles = $selectedSizes.closest(productSizesSelectorClass).find(productAttrClass); 3826 3827 // remove all selectedClass and set all prop "checked" to false 3828 sizeTiles.removeClass(selectedClass); 3829 sizeTiles.find('input').prop('checked', false); 3830 3831 // add selectedClass and set prop "checked" to true for selectedSize tile 3832 $selectedSizes.addClass(selectedClass); 3833 $selectedSizes.find('input').prop('checked', true); 3834 } 3835 3836 function displaySelected($el) { 3837 var $input = $el; 3838 3839 if ($el.prop('tagName') !== 'INPUT') $input = $el.find('input'); 3840 3841 var selectedText = $input.parent().find('.tile-text').html(); 3842 var sizeSizeCurrentSelectedSelector = productSizesCurrentSelectedClass; 3843 3844 var $productSizesName = $input.closest(productSizesSelectorClass).prev(); 3845 var $sizeCurrentSelected = $($productSizesName.find(sizeSizeCurrentSelectedSelector)[0]); 3846 3847 $sizeCurrentSelected.text(selectedText); 3848 } 3849 3850 function triggerAttributeClicked($selectedSizeTile, $productSection) { 3851 var attributeData = { 3852 selectedAttribute: $selectedSizeTile, 3853 $productSection: $productSection 3854 }; 3855 messageBus.trigger('productMultipleAttributeFilter:attributeClicked', attributeData); 3856 } 3857 3858 function dataBridgeSkuUpdateHandler($productSection) { 3859 var shortSku = $productSection.find(partNumberSelector).val() || null; 3860 var productId = $productSection.attr('data-productid');
3861 var kic = Utils.getProductData(productId).kicId; 3862 3863 try { 3864 var productData = { 3865 kic: kic, 3866 shortSku: shortSku, 3867 productId: productId 3868 }; 3869 window.digitalData.trigger('productInfo.sizeSelected', productData); 3870 } catch (error) { 3871 // DataBridge Error while doing "productInfo.sizeSelected:" 3872 } 3873 } 3874 3875 var publicMembers = { 3876 listen: function listen() { 3877 messageBus.on('productSize:updateUrgencyMessage', skuChangeHandler); 3878 messageBus.on('productPageInfo:load', sizeOnLoad); 3879 messageBus.on('stickySizeTileSelected', stickySizeTileSelectedHandler); 3880 }, 3881 bindEvents: function bindEvents() { 3882 $(document).on('click', productSizeAttrClass, sizeTileClickHandler); 3883 } 3884 }; 3885 3886 return publicMembers; 3887}); 3888 3889redesign.define('productModules/helpers/ProductMFEAdapter',[ 3890 'productModules/helpers/Utils', 3891 'globalServices/startGuestSession' 3892], 3893function ProductMFEAdapter(Utils, guestSession) { 3894 'use strict'; 3895 3896 function callGuestSession() { 3897 guestSession.deferredInit().done(function guestSessionSuccess() { 3898 var guestSessionSuccessEvent = new CustomEvent('crs:guestSessionSuccess'); 3899 window.dispatchEvent(guestSessionSuccessEvent); 3900 }).fail(function guestSessionFail() { 3901 var guestSessionFailEvent = new CustomEvent('crs:guestSessionFail'); 3902 window.dispatchEvent( guestSessionFailEvent); 3903 }); 3904 } 3905 3906 var publicMembers = { 3907 attributeUpdate: function attributeUpdate(data) { 3908 var attributeUpdateEvent = new CustomEvent('crs:attributeUpdate', { detail: data }); 3909 !Utils.isQuickviewOpened && window.dispatchEvent(attributeUpdateEvent); 3910 }, 3911 swatchUpdate: function swatchUpdate(data) { 3912 var swatchUpdateEvent = new CustomEvent('crs:swatchUpdate', { detail: data }); 3913 !Utils.isQuickviewOpened && window.dispatchEvent(swatchUpdateEvent); 3914 }, 3915 3916 listen: function listen() { 3917 window.addEventListener('mfe:callGuestSession', callGuestSession); 3918 } 3919 }; 3920 3921 return publicMembers; 3922}); 3923 3924redesign.define('productModules/product/productMultipleAttributeFilter',[ 3925 'coreServices/ariaLiveSetterService', 3926 'productModules/helpers/Utils', 3927 'productModules/helpers/ProductMFEAdapter' 3928], function productMultipleAttributeFilter(ariaLiveSetterService, Utils, ProductMFEAdapter) { 3929 'use strict'; 3930 3931 var skuContainer = '.product-page__skus'; 3932 var swatchesSizesContainerSelectorClass = '.product-page__swatches-attributes'; 3933 var tileContainerClass = '.product-attrs__attr'; 3934 var selectedTileInputSelectorClass = 'input:checked'; 3935 var productParentPageContainerSelectorClass = '.product-page-v2-modules'; 3936 var stickySizeTileContainerSelectorClass = '.product-page-sticky-size-tile__container'; 3937 var productFormSectionSelector = '.product-page-add-to-bag__container'; 3938 3939 var messageBus = $(ANF); 3940 3941 // This will hold all combinations of skuDefiningProperties and whether that sku has inventory 3942 var skuAvailabilityByCollection = {}; 3943 3944 // This will hold all combinations of skuDefiningProperties and what shortSku has those properties 3945 var definedSkusByCollection = {}; 3946 3947 // This is used to generate permutations. The algorithm is using it is O(n!), so 3948 // don't let this list get any longer than a few properties 3949 var skuDefiningProperties = ['kic', 'sizePrimary', 'sizeSecondary']; 3950 3951 function setupSkuData(_e, productSection) { 3952 var $skuParentContainer = $(productSection).find(swatchesSizesContainerSelectorClass); 3953 var collection = $(productSection).find(productFormSectionSelector).children('input[name=collection]').val(); 3954 3955 var skus = $skuParentContainer 3956 .find(skuContainer + ' option') 3957 .get() // converts jquery object to plain array 3958 .map(skuElementToData); 3959 3960 skuAvailabilityByCollection[collection] = skus.reduce(createSkuAvailabilityObject, {}); 3961 definedSkusByCollection[collection] = skus.reduce(createDefinedSkuObject, {}); 3962 } 3963 3964 function skuElementToData(sku) { 3965 return $.extend({}, sku.dataset, {'shortSku': sku.value}); 3966 } 3967 3968 function permutationsForDefiningProperties(sku) { 3969 // Remove unused properties, usually secondary size 3970 var skuProperties = skuDefiningProperties 3971 .filter(function removeUndefined(property) { return !!sku[property]; }) 3972 .map(function getValue(property) { return sku[property]; }); 3973 3974 var permuted = []; 3975 permute(skuProperties, skuProperties.length, function permuteAnon(arr) { 3976 permuted.push(arr.slice()); 3977 }); 3978 3979 return permuted; 3980 } 3981 3982 function createSkuAvailabilityObject(acc, sku) { 3983 var permuted = permutationsForDefiningProperties(sku); 3984 3985 permuted.forEach( function permuteForLoop(permutation) { 3986 setNested(permutation, acc, sku.inventory > 0); 3987 }); 3988 3989 return acc; 3990 } 3991 3992 function createDefinedSkuObject(acc, sku) { 3993 var permuted = permutationsForDefiningProperties(sku); 3994 3995 permuted.forEach( function permuteForLoop(permutation) { 3996 setNested(permutation, acc, sku.shortSku); 3997 }); 3998 3999 return acc; 4000 } 4001 4002 // Takes an array and generates all permutations of the items in it 4003 function permute(arr, len, cb) { 4004 if (len === 1) { 4005 cb(arr); 4006 } else { 4007 for (var i = 0; i < len; i++) { 4008 permute(arr, len - 1, cb); 4009 4010 if (len % 2 === 0) { 4011 swap(arr, i, len - 1); 4012 } else { 4013 swap(arr, 0, len - 1); 4014 } 4015 } 4016 } 4017 4018 // http://jira/browse/EPIC-9604 Jira to revisit this to adhere to "no-param-reassign" ESLint rule 4019 function swap(paramArr, a, b) { 4020 var tmp = paramArr[a]; 4021 paramArr[a] = paramArr[b]; 4022 paramArr[b] = tmp; 4023 } 4024 } 4025 4026 // Allows setting a value on an object with an array of property names 4027 // Example: 4028 // setNested(['a', 'b', 'c'], {}, true) 4029 // => { a: { b: { c: true } } } 4030 function setNested(path, object, value) { 4031 var lastIndex = path.length - 1; 4032 var nested = object; 4033 4034 for (var i = 0; i < path.length; i++) { 4035 var key = path[i]; 4036 var newValue = value; 4037 var existing = nested[key]; 4038 4039 if (i !== lastIndex) { 4040 newValue = existing || {}; 4041 } 4042 4043 nested[key] = newValue; 4044 nested = nested[key]; 4045 } 4046 4047 return object; 4048 } 4049 4050 // Gets a value out of an object with an array of property names 4051 function getNested(path, object) { 4052 if (!object || path.length === 0) { 4053 return object; 4054 } else if (path.length === 1) { 4055 return object[path[0]]; 4056 } 4057 return getNested(path.slice(1), object[path[0]]); 4058 } 4059 4060 // Searches recursively through an object tree to find if any key/value pair is true 4061 // Returns as soon as it finds one. 4062 function findNestedTrue(node) { 4063 if (typeof node === 'boolean') { 4064 return node; 4065 } 4066 4067 if (typeof node === 'object') { 4068 var children = Object.keys(node); 4069 4070 for (var i = 0; i < children.length; i++) { 4071 var foundTrue = findNestedTrue(node[children[i]]); 4072 if (foundTrue) { 4073 return true; 4074 } 4075 } 4076 4077 return false;
4078 } 4079 4080 return false; 4081 } 4082 4083 function findIsSkuAvailable(skuAvailability, criteria, singleSearch) { 4084 var path = Object.keys(criteria).map(function getFilters(key) { 4085 return criteria[key]; 4086 }); 4087 4088 var state = getNested(path, skuAvailability); 4089 4090 if (typeof state === 'boolean') { 4091 return state; 4092 } else if (singleSearch || !state) { 4093 return false; 4094 } 4095 return findNestedTrue(state); 4096 } 4097 4098 var publicMembers = { 4099 initFilter: function initFilter() { 4100 // set up skus data for each product on the page 4101 var isQuickview = Utils.isQuickviewOpened; 4102 4103 if (!isQuickview && Utils.hasProductMFEEnabled()) { 4104 return; 4105 } 4106 4107 $(productParentPageContainerSelectorClass).each(setupSkuData); 4108 }, 4109 4110 renderBuyGridTiles: function renderBuyGridTiles($skuParentContainer, skuAvailability, selectedTileData) { 4111 // Focus gets moved around when setting elements' disabled attribute, so we need to reset it 4112 var savedFocusedElement = document.activeElement; 4113 4114 // If a selected combo is actually unavailable, we need to determine availability just by KIC 4115 var verifiedSelection = findIsSkuAvailable(skuAvailability, selectedTileData) ? 4116 selectedTileData : { 'kic': selectedTileData.kic }; 4117 4118 var tiles = $skuParentContainer.find(tileContainerClass); 4119 tiles.each(function renderTile(_idx, tile) { 4120 var input = $(tile).find('input')[0]; 4121 var tileType = input.name.split('_')[0]; // handles tray tiles 4122 var tileOptions = $.extend({}, verifiedSelection); 4123 tileOptions[tileType] = input.value; 4124 4125 var skuHasInventory = findIsSkuAvailable(skuAvailability, tileOptions); 4126 4127 if (!skuHasInventory) { 4128 if (tileType === 'kic') { 4129 tile.setAttribute('data-state', 'disabled'); 4130 } else { 4131 $(tile).removeClass('selected'); 4132 input.checked = false; 4133 input.setAttribute('disabled', 'disabled'); 4134 } 4135 } else if (tileType === 'kic') { 4136 tile.setAttribute('data-state', 'default'); 4137 } else { 4138 input.removeAttribute('disabled'); 4139 } 4140 }); 4141 4142 savedFocusedElement.focus(); 4143 }, 4144 4145 attributeUpdate: function attributeUpdate(_e, data) { 4146 var $selectedAttr = data.selectedAttribute || $(); 4147 var $productSection = data.$productSection; 4148 var $skuParentContainer = $productSection.find(swatchesSizesContainerSelectorClass); 4149 var $skuContainer = $skuParentContainer.find(skuContainer); 4150 var $selectedSkuArray = $skuParentContainer.find(selectedTileInputSelectorClass); 4151 4152 var productId = $selectedAttr.find('input').attr('data-productid') || $productSection.attr('data-productid'); 4153 var collection = $productSection.find(productFormSectionSelector).children('input[name=collection]').val(); 4154 4155 var skuAvailability = skuAvailabilityByCollection[collection]; 4156 var filterOptions = {}; 4157 4158 var $skuOption; 4159 var skuSelected = null; 4160 4161 $.each($selectedSkuArray, function selectedSkuArrayLoop(_idx, input) { 4162 // Exclude the duplicates in sticky tray, if they exist 4163 if (!$(input).closest(stickySizeTileContainerSelectorClass).length) { 4164 var tileType = input.name.split('_')[0]; // handles qv tiles 4165 filterOptions[tileType] = input.value; 4166 } 4167 }); 4168 4169 publicMembers.renderBuyGridTiles($skuParentContainer, skuAvailability, filterOptions); 4170 4171 var selectedIsQualifiedSku = findIsSkuAvailable(skuAvailability, filterOptions, true); 4172 $productSection.attr('data-qualified-sku', selectedIsQualifiedSku); 4173 4174 if (selectedIsQualifiedSku) { 4175 var definedSkuAttributes = Object.keys(filterOptions).map(function getFilters(key) { 4176 return filterOptions[key]; 4177 }); 4178 4179 var sku = getNested(definedSkuAttributes, definedSkusByCollection[collection]); 4180 skuSelected = sku; 4181 $skuOption = $skuContainer.find('option[value="' + skuSelected + '"]'); 4182 4183 publicMembers.handlePartNumberCondition($skuContainer); 4184 4185 messageBus.trigger('productPrice:skuSelected', { 4186 'productId': productId, 4187 'skuId': skuSelected, 4188 'skuContainer': $skuParentContainer, 4189 'productPrices': productPrices, 4190 'productSection': $productSection 4191 }); 4192 4193 // for analytics 4194 setTimeout(function analyticsTimeout() { 4195 $(window).trigger('productSize:skuSelected', { 4196 'sku': skuSelected, 4197 'skuContainer': $skuParentContainer, 4198 'productId': productId, 4199 'selectedAttribute': $selectedAttr 4200 }); 4201 }, 0); 4202 } else {
4203 var currentKIC = Utils.getProductData(productId).kicId; 4204 $skuOption = $skuContainer.find('option[data-kic="' + currentKIC + '"]').first(); 4205 4206 // If we can't find the product via KIC this grabs the first option after the blank option instead. 4207 if (!$skuOption.val()) { 4208 $skuOption = $skuContainer.find('option:eq(2)'); 4209 } 4210 4211 messageBus.trigger('productPrice:swatchSelected', { 4212 'productId': productId, 4213 'kic': currentKIC, 4214 'skuContainer': $skuParentContainer, 4215 'productPrices': productPrices, 4216 'productSection': $productSection 4217 }); 4218 } 4219 $skuContainer.val(skuSelected); 4220 $productSection.attr('data-inventory', $skuOption.attr('data-inventory')); 4221 4222 var $productQuantityDropdown = $productSection.find('.product-quantity-dropdown select'); 4223 var isStickyTileSelection = $selectedAttr.parents(stickySizeTileContainerSelectorClass).length === 1; 4224 4225 messageBus.trigger('productSize:updateUrgencyMessage', { 4226 'sku': skuSelected, 4227 'skuContainer': $skuParentContainer, 4228 'productId': productId, 4229 'selectedAttribute': $selectedAttr, 4230 'isStickyTileSelection': isStickyTileSelection 4231 }); 4232 4233 messageBus.trigger('productSize:skuSelected', { 4234 'sku': skuSelected, 4235 'skuContainer': $skuParentContainer, 4236 'productId': productId, 4237 'selectedAttribute': $selectedAttr, 4238 'isStickyTileSelection': isStickyTileSelection, 4239 'quantity': $productQuantityDropdown ? $productQuantityDropdown.val() : 1 4240 }); 4241 4242 messageBus.trigger('PDP:skuSelected', { 4243 'sku': skuSelected, 4244 '$parentContainer': $productSection 4245 }); 4246 4247 ProductMFEAdapter.attributeUpdate({ 4248 sku: skuSelected, 4249 productId: productId, 4250 kic: filterOptions.kic, 4251 seq: productCatalog[productId].swatchSequence 4252 }); 4253 4254 ariaLiveSetterService.assertive(ANF.messaging.product.productListUpdated); 4255 }, 4256 4257 handlePartNumberCondition: function handlePartNumberCondition($skuContainer) { 4258 var needsPartNumber = ($('body').hasClass('checkout') || $('body').is('#checkout-cart')); 4259 4260 if (needsPartNumber) { 4261 var $addToBagForm = $skuContainer.closest('form'); 4262 publicMembers.addPartNumberToForm($addToBagForm); 4263 } 4264 }, 4265 4266 addPartNumberToForm: function addPartNumberToForm(form) { 4267 var $sku = form.find('[name="sku"]'); 4268 var $partNumber; 4269 var dataSelector = '.product-add-to-bag .data'; 4270 var partNumberName = 'partNumber_2'; 4271 4272 // if new size tiles are used, the sku is on the form as 'sku' input instead of 'partnumber' as expected. 4273 if ($sku.length) { 4274 $partNumber = form.find('[name="' + partNumberName + '"]'); 4275 4276 if ($partNumber.length) { 4277 $partNumber.val($sku.val()); 4278 } else { 4279 $('<input>').attr({ 4280 type: 'hidden', 4281 id: partNumberName, 4282 name: partNumberName, 4283 value: $sku.val() 4284 }).appendTo(dataSelector); 4285 } 4286 } 4287 } 4288 }; 4289 4290 publicMembers.listen = { 4291 'productPageInfo:load': publicMembers.initFilter, 4292 'productMultipleAttributeFilter:attributeUpdate': publicMembers.attributeUpdate, 4293 'productMultipleAttributeFilter:attributeClicked': publicMembers.attributeUpdate 4294 }; 4295 4296 return publicMembers; 4297}); 4298 4299redesign.define('productModules/product/productSwatches',[ 4300 'productModules/helpers/Utils', 4301 'dsPartial/icon-block', 4302 'util/analytics/enum/dataPoint', 4303 'util/analytics/enum/subscriptionName', 4304 'productModules/helpers/ProductMFEAdapter' 4305], function ProductSwatches( 4306 Utils, 4307 dsPartialIconBlock,
4308 analyticsDataPoint, 4309 analyticsSubscriptionName, 4310 ProductMFEAdapter 4311) { 4312 'use strict'; 4313 4314 // Constants 4315 var activeProductClass = 'active-product'; 4316 var activeProductParentContainerSelectorClass = '.product-page-v2-modules.active-product'; 4317 var ajaxErrorClass = 'atb-ajax-error'; 4318 var defaultSwatchClass = '.default-swatch'; 4319 var errorContainerSelectorClass = '.validator-container'; 4320 var fadeEasing = 'linear'; 4321 var fadeSpeed = 100; 4322 var messageBus = $(ANF); 4323 var partNumberSelector = 'input[name="partNumber"]'; 4324 var productPageATBButtonSelectorClass = '.product-page-add-to-bag__button'; 4325 var productSection = '.product-page-v2-modules'; 4326 var productSkuSelectorClass = '.product-page__skus'; 4327 var productSwatchesSelectorClass = '.product-swatches'; 4328 var productWrap = '.product-page__swatches-attributes'; 4329 var quickviewModalIsActiveSelectorClass = '.product-quickview__modal[data-open="true"]'; 4330 var quickviewModalProductSection = '.product-quickview__modal[data-open="true"] .product-quickview__content .product-page-v2-modules'; 4331 var selectedClass = 'selected'; 4332 var swatchLabel = '.selected-swatch__label'; 4333 var swatchLoadingClass = 'loading'; 4334 var swatchTile = '.product-swatch__attr'; 4335 var urgencyMessageSelectorClass = '.product-urgency-size-availability'; 4336 4337 function handleMfeSwatchChange(event) { 4338 var productDetails = event.detail; 4339 var $productSection = Utils.getProductSection(); 4340 messageBus.trigger('mfe:SwatchSelected', [{}, $productSection, productDetails.productId, ANF]); 4341 } 4342 4343 function swatchOnLoad() { 4344 var isQuickview = Utils.isQuickviewOpened; 4345 var $productSection = isQuickview ? $(quickviewModalProductSection) : $(productSection); 4346 var $selectedSwatch = $productSection.find(defaultSwatchClass); 4347 var productId = getProductId($productSection); 4348 4349 if (!isQuickview && Utils.hasProductMFEEnabled()) { 4350 return; 4351 } 4352 4353 4354 if ($selectedSwatch.length > 0) { 4355 $productSection.addClass(activeProductClass); // set active product 4356 swatchSelectedUpdate($selectedSwatch, $productSection, productId); 4357 } else { 4358 updateSwatchUrgencyMessage($productSection, productId); 4359 messageBus.trigger('productMultipleAttributeFilter:attributeUpdate', {$productSection: $productSection}); 4360 } 4361 messageBus.trigger('productPageInfo:swatchOnLoad', [$selectedSwatch, $productSection, productId, ANF]); 4362 4363 if (isQuickview) { 4364 setTimeout(function quickviewSwatchLoadedTrigger() { 4365 messageBus.trigger('productQuickview:swatchesLoaded', $productSection); 4366 }, 0); 4367 } 4368 } 4369 4370 function swatchClickEvent(e) { 4371 var $selectedSwatch = $(e.currentTarget); 4372 var $selectedInput = $selectedSwatch.find('input'); 4373 var newProductId = $selectedInput.data('productid'); 4374 var newKIC = $selectedInput.val(); 4375 var $productSection = $selectedSwatch.closest(productSection); 4376 var $addToBagForm = $productSection.find('form'); 4377 if (newProductId !== parseInt(getProductId($productSection), 10)) { 4378 $(activeProductParentContainerSelectorClass).removeClass(activeProductClass); 4379 $productSection.addClass(activeProductClass); 4380 4381 $addToBagForm 4382 .find(errorContainerSelectorClass) 4383 .fadeOut(fadeSpeed, fadeEasing) 4384 .removeClass(ajaxErrorClass); 4385 4386 // update product ids across page 4387 $addToBagForm.find('input[name=productId]').val(newProductId); 4388 4389 $productSection 4390 .attr('data-productid', newProductId) 4391 .data('productid', newProductId); 4392 4393 $productSection.find('.details__write-review-modal').attr('data-productid', newProductId); 4394 4395 var $currentProductSkus = $productSection.find(productSkuSelectorClass); 4396 var longSku = $currentProductSkus.find('option[data-kic="' + newKIC + '"]').first().attr('data-long-sku'); 4397 $productSection.attr('data-long-sku', $.trim(longSku)); 4398 4399 var $fitPredictorContainer = $productSection.find('.fp-root'); 4400 if ($fitPredictorContainer.length > 0) { 4401 $fitPredictorContainer.attr('data-product-id', longSku); 4402 } 4403 4404 // trigger image load after all the product data is saved on the <section> 4405 messageBus.trigger('productPageImages:reload', { 4406 $selectedSwatch: $selectedSwatch, 4407 $productSection: $productSection 4408 }); 4409 4410 swatchSelectedUpdate($selectedSwatch, $productSection, newProductId); 4411 ProductMFEAdapter.swatchUpdate({ 4412 productId: newProductId 4413 }); 4414 messageBus.trigger('productPageInfo:swatchSelected', [$selectedSwatch, $productSection, newProductId]); 4415 $addToBagForm.find(productPageATBButtonSelectorClass).removeAttr('disabled'); 4416 4417 triggerAnalyticsEvent(newProductId, longSku); 4418 } 4419 4420 // push call to end of stack to avoid race conditions 4421 window.setTimeout(function timeout() { 4422 dataBridgeSwatchSelectHandler($productSection, newProductId); 4423 }, 0); 4424 4425 // This is to prevent the event to alter the pdp when in the quickview of the same product 4426 e.preventDefault(); 4427 } 4428 4429 function swatchSelectedUpdate($selectedSwatch, $productSection, productId) { 4430 var $input = $selectedSwatch.find('input'); 4431 var $productAttributeWrapper = $input.closest(productWrap); 4432 4433 // handle product urgency message display 4434 updateSwatchUrgencyMessage($productSection, productId); 4435 4436 // EPIC-7708: adding this each back so that both the pdp swatch and tray swatch is updated to have selected class on swatch click 4437 // TODO: remove this when we improve productMultipleAttributeFilter.attributeUpdate's function to use only pdp swatch to filter 4438 var swatchSeq = $input.attr('data-seq'); 4439 var swatches = $productSection.find('input[data-seq=' + swatchSeq + ']').closest(swatchTile); 4440 4441 $.each(swatches, function updateSwatches(index, swatch) { 4442 var $swatch = $(swatch); 4443 4444 updateSwatchLabel($input, $productAttributeWrapper); 4445 updateSelectedSeq($input, $productAttributeWrapper); 4446 updateSwatchSelectedProperty($swatch, $input); 4447 4448 if (index === 0) { 4449 setTimeout(function timeout() { 4450 triggerPostSwatchSelectionProcesses($swatch, $productSection); 4451 4452 if ($(quickviewModalIsActiveSelectorClass).length > 0) { 4453 messageBus.trigger('quickview:updateFullProductLink', $swatch.children('input').attr('data-producturl')); 4454 } 4455 }, 0); 4456 } 4457 }); 4458 } 4459 4460 function updateSwatchUrgencyMessage($productSection, productId) { 4461 if ($productSection.find('.product-sizes-primary .tiles').first().children().length > 1) { 4462 var $urgencyMsg = $productSection.find(urgencyMessageSelectorClass); 4463 var $urgencyScreenReaderMsg = $urgencyMsg.find('.urgency-message__kic_screen-reader'); 4464 4465 var finalThreshold = ANF.params.storeAttributes.catalogV2.lowestInventoryThreshold; 4466 var currentProductInventory = Utils.getProductData(productId).lowestInventoryCount; 4467 var isLowInventory = currentProductInventory > 0 && currentProductInventory < finalThreshold; 4468 4469 if (isLowInventory) { 4470 if (!$urgencyMsg.hasClass('show')) { 4471 $urgencyMsg.addClass('fadein').removeClass('hide default-hidden'); 4472 setTimeout(function timeoutBump() { 4473 $urgencyMsg.removeClass('fadein').addClass('bump'); 4474 4475 setTimeout(function timeoutShow() { 4476 $urgencyMsg.removeClass('bump').addClass('show'); 4477 $urgencyScreenReaderMsg.html(ANF.messaging.catalog.NEW_KIC_URGENCY_MESSAGE); 4478 }, 500); 4479 }, 500); 4480 } else { 4481 $urgencyMsg.removeClass('show hide').addClass('bump'); 4482 4483 setTimeout(function timeoutShow() { 4484 $urgencyMsg.removeClass('bump').addClass('show'); 4485 $urgencyScreenReaderMsg.html(ANF.messaging.catalog.NEW_KIC_URGENCY_MESSAGE); 4486 }, 500); 4487 } 4488 4489 $(window).trigger('analytics.miscProductFeature', { 4490 'type': 'low inventory message', 4491 'action': 'KIC level' 4492 }); 4493 } else if (!$urgencyMsg.hasClass('hide')) { 4494 $urgencyMsg.removeClass('show').addClass('fadeout'); 4495 setTimeout(function timeoutHide() { 4496 $urgencyMsg.removeClass('fadeout').addClass('hide'); 4497 $urgencyScreenReaderMsg.html(''); 4498 }, 450); 4499 } 4500 4501 // unfortunately the best way to gaurantee animation running again is by replacing the content of the urgency message. 4502 var iconBlockSettings = { 4503 'data-icon': 'clock', 4504 'icon-block-optional-content': { 4505 'icon-block-heading': ANF.messaging.catalog.NEW_KIC_URGENCY_MESSAGE 4506 } 4507 }; 4508 4509 var template = dsPartialIconBlock.templates['icon-block'](iconBlockSettings); 4510 $urgencyMsg.find('.urgency-message__content').html(template); 4511 } 4512 } 4513 4514 function triggerPostSwatchSelectionProcesses($selectedSwatch, $productSection) { 4515 var $productWrap = $productSection.find(productWrap); 4516 4517 var attributeData = { 4518 selectedAttribute: $selectedSwatch, 4519 $productSection: $productSection 4520 }; 4521 4522 messageBus.trigger('productMultipleAttributeFilter:attributeClicked', attributeData); 4523 4524 if ($productWrap.hasClass(swatchLoadingClass)) { 4525 $productWrap.removeClass(swatchLoadingClass); 4526 } 4527 } 4528 4529 function updateSwatchLabel($input, $productAttributeWrapper) { 4530 // multi-pdp pages can have multiple selected swatch tiles, one for each product. 4531 // get the product wrapper for this swatch so we are only setting the label for the correct product. 4532 var $swatchLabel = $productAttributeWrapper.find(swatchLabel); 4533 var swatchData = $input.attr('data-swatch').toLowerCase(); 4534 4535 if ($input.length > 0) { 4536 $swatchLabel.html(swatchData); 4537 $input.closest(productSection).attr('data-swatch', $input.attr('data-swatch').toLowerCase()); 4538 } 4539 } 4540 4541 function updateSelectedSeq($input, $productAttributeWrapper) { 4542 // multi-pdp pages can have multiple selected swatch tiles, one for each product. 4543 // get the product wrapper for this swatch so we are only setting selected sequence for the correct product. 4544 var $skus = $productAttributeWrapper.find(productSkuSelectorClass); 4545 var newSeq = $input.attr('data-seq'); 4546 4547 $skus.attr('data-selected-seq', newSeq); 4548 window.digitalData.set('product.data.sequence', newSeq); 4549 } 4550 4551 function updateSwatchSelectedProperty($selectedSwatch, $input) { 4552 $selectedSwatch 4553 .parents(productSwatchesSelectorClass) 4554 .find(swatchTile) 4555 .removeClass(selectedClass); 4556 4557 $selectedSwatch.addClass(selectedClass); 4558 $input.prop('checked', true); 4559 } 4560 4561 function getProductId($productSection) { 4562 return $productSection.attr('data-productid'); 4563 } 4564 4565 function dataBridgeSwatchSelectHandler($productSection, productId) { 4566 var shortSku = $productSection.find(partNumberSelector).val() || null;
4567 var kic = Utils.getProductData(productId).kicId; 4568 4569 try { 4570 var productData = { 4571 kic: kic, 4572 shortSku: shortSku, 4573 productId: productId 4574 }; 4575 window.digitalData.trigger('productInfo.swatchSelected', productData); 4576 window.dispatchEvent(new CustomEvent('crs:swatchSelected', { detail: productData })); 4577 } catch (error) { 4578 // do nothing 4579 } 4580 } 4581 4582 function getFromFirstProduct(productId, key) { 4583 var items; 4584 try { 4585 items = productPrices[productId].items; 4586 } catch (error) { 4587 // do nothing 4588 } 4589 var firstItem = items && Object.values(items)[0]; 4590 return firstItem ? firstItem[key] : 'null'; 4591 } 4592 4593 function getBadgeDataFromProduct(productId, key) { 4594 var productData = Utils.getProductData(productId); 4595 if (!productData) { 4596 return null; 4597 } 4598 var keys = key.split('.'); 4599 return keys.reduce(function _reduce(acc, currKey) { 4600 return (acc && acc.hasOwnProperty(currKey)) ? acc[currKey] : null; 4601 }, productData); 4602 } 4603 4604 function getProductBadge(productId) { 4605 var isNewArrival = getBadgeDataFromProduct(productId, 'newArrival'); 4606 var earlyAccessFlag = getBadgeDataFromProduct(productId, 'productAttrs.EarlyAccessIndicator'); 4607 var isEarlyAccess = earlyAccessFlag ? earlyAccessFlag.toString().toLowerCase() === 'true' : false; 4608 var isTopRated = getBadgeDataFromProduct(productId, 'productAttrs.topRated'); 4609 var isBestSeller = getBadgeDataFromProduct(productId, 'productAttrs.bestSeller'); 4610 return [isNewArrival ? 'new' : '', isEarlyAccess ? 'early access' : '', isTopRated ? 'top rated' : '', isBestSeller ? 'top seller' : ''].filter(Boolean).join(', '); 4611 } 4612 4613 function triggerAnalyticsEvent(productId, longSku) { 4614 var clickedSwatches = window.digitalData.get('productSwatches.clicked') ? window.digitalData.get('productSwatches.clicked') : []; 4615 var hadBeenClickedCurrentSwatch = clickedSwatches.includes(productId); 4616 4617 if (hadBeenClickedCurrentSwatch) return; 4618 4619 clickedSwatches.push(productId); 4620 window.digitalData.set('productSwatches.clicked', clickedSwatches); 4621 4622 var productInfo = Utils.getProductData(productId); 4623 var dataPointEmptyValue = 'null'; 4624 4625 var eventData = {}; 4626 eventData[analyticsDataPoint.KEY_ITEM_COLOR] = productInfo.kicId; 4627 eventData[analyticsDataPoint.PRICE_LIST_LOCAL] = getFromFirstProduct(productId, 'listPrice') || dataPointEmptyValue; 4628 eventData[analyticsDataPoint.PRICE_LIST_USD] = getFromFirstProduct(productId, 'listPriceUSD') || dataPointEmptyValue; 4629 eventData[analyticsDataPoint.PRICE_OFFER_LOCAL] = getFromFirstProduct(productId, 'offerPrice') || dataPointEmptyValue; 4630 eventData[analyticsDataPoint.PRICE_OFFER_USD] = getFromFirstProduct(productId, 'offerPriceUSD') || dataPointEmptyValue; 4631 eventData[analyticsDataPoint.PRICE_FLAG] = getFromFirstProduct(productId, 'priceFlag') || dataPointEmptyValue; 4632 eventData[analyticsDataPoint.LONG_SKU] = longSku || dataPointEmptyValue; 4633 eventData[analyticsDataPoint.PRODUCT_ID] = productId || dataPointEmptyValue; 4634 eventData[analyticsDataPoint.PRODUCT_NAME] = productInfo.name || dataPointEmptyValue; 4635 eventData[analyticsDataPoint.COLLECTION_ID] = productInfo.collection || dataPointEmptyValue; 4636 eventData[analyticsDataPoint.BRAND] = productInfo.productAttrs.brand || dataPointEmptyValue; 4637 eventData[analyticsDataPoint.PRODUCT_BADGE] = getProductBadge(productId) || dataPointEmptyValue; 4638 window.digitalData.trigger(analyticsSubscriptionName.ACTION.SWATCH_CLICK, eventData); 4639 } 4640 4641 var publicMembers = { 4642 bindEvents: function bindEvents() { 4643 $(document).on('change', swatchTile, swatchClickEvent); 4644 }, 4645 listen: function listen() { 4646 $(ANF).on('productPageInfo:load', swatchOnLoad); 4647 window.addEventListener('mfe:SwatchSelected', handleMfeSwatchChange); 4648 } 4649 }; 4650 4651 return publicMembers; 4652}); 4653 4654/*! 4655 * currency.js - v1.2.2 4656 * http://scurker.github.io/currency.js 4657 * 4658 * Copyright (c) 2019 Jason Wilson 4659 * Released under MIT license 4660 */ 4661 4662(function (global, factory) { 4663 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 4664 typeof redesign.define === 'function' && redesign.define.amd ? redesign.define('currency',factory) : 4665 (global = global || self, global.currency = factory()); 4666}(this, function () { 'use strict'; 4667 4668 var defaults = { 4669 symbol: '$', 4670 separator: ',',
4671 decimal: '.', 4672 formatWithSymbol: false, 4673 errorOnInvalid: false, 4674 precision: 2, 4675 pattern: '!#', 4676 negativePattern: '-!#' 4677 }; 4678 4679 var round = function round(v) { 4680 return Math.round(v); 4681 }; 4682 4683 var pow = function pow(p) { 4684 return Math.pow(10, p); 4685 }; 4686 4687 var rounding = function rounding(value, increment) { 4688 return round(value / increment) * increment; 4689 }; 4690 4691 var groupRegex = /(\d)(?=(\d{3})+\b)/g; 4692 var vedicRegex = /(\d)(?=(\d\d)+\d\b)/g; 4693 /** 4694 * Create a new instance of currency.js 4695 * @param {number|string|currency} value 4696 * @param {object} [opts] 4697 */ 4698 4699 function currency(value, opts) { 4700 var that = this; 4701 4702 if (!(that instanceof currency)) { 4703 return new currency(value, opts); 4704 } 4705 4706 var settings = Object.assign({}, defaults, opts), 4707 precision = pow(settings.precision), 4708 v = parse(value, settings); 4709 that.intValue = v; 4710 that.value = v / precision; // Set default incremental value 4711 4712 settings.increment = settings.increment || 1 / precision; // Support vedic numbering systems 4713 // see: https://en.wikipedia.org/wiki/Indian_numbering_system 4714 4715 if (settings.useVedic) { 4716 settings.groups = vedicRegex; 4717 } else { 4718 settings.groups = groupRegex; 4719 } // Intended for internal usage only - subject to change 4720 4721 4722 this.s = settings; 4723 this.p = precision; 4724 } 4725 4726 function parse(value, opts) { 4727 var useRounding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; 4728 var v = 0, 4729 decimal = opts.decimal, 4730 errorOnInvalid = opts.errorOnInvalid, 4731 decimals = opts.precision, 4732 precision = pow(decimals), 4733 isNumber = typeof value === 'number'; 4734 4735 if (isNumber || value instanceof currency) { 4736 v = (isNumber ? value : value.value) * precision; 4737 } else if (typeof value === 'string') { 4738 var regex = new RegExp('[^-\\d' + decimal + ']', 'g'), 4739 decimalString = new RegExp('\\' + decimal, 'g'); 4740 v = value.replace(/\((.*)\)/, '-$1') // allow negative e.g. (1.99) 4741 .replace(regex, '') // replace any non numeric values 4742 .replace(decimalString, '.') // convert any decimal values 4743 * precision; // scale number to integer value 4744 4745 v = v || 0; 4746 } else { 4747 if (errorOnInvalid) { 4748 throw Error('Invalid Input'); 4749 } 4750 4751 v = 0; 4752 } // Handle additional decimal for proper rounding. 4753 4754 4755 v = v.toFixed(4); 4756 return useRounding ? round(v) : v; 4757 } 4758 4759 currency.prototype = { 4760 /** 4761 * Adds values together. 4762 * @param {number} number 4763 * @returns {currency} 4764 */ 4765 add: function add(number) { 4766 var intValue = this.intValue, 4767 _settings = this.s, 4768 _precision = this.p; 4769 return currency((intValue += parse(number, _settings)) / _precision, _settings); 4770 }, 4771 4772 /** 4773 * Subtracts value. 4774 * @param {number} number 4775 * @returns {currency} 4776 */ 4777 subtract: function subtract(number) { 4778 var intValue = this.intValue, 4779 _settings = this.s, 4780 _precision = this.p; 4781 return currency((intValue -= parse(number, _settings)) / _precision, _settings); 4782 }, 4783 4784 /** 4785 * Multiplies values. 4786 * @param {number} number 4787 * @returns {currency} 4788 */ 4789 multiply: function multiply(number) { 4790 var intValue = this.intValue, 4791 _settings = this.s; 4792 return currency((intValue *= number) / pow(_settings.precision), _settings); 4793 }, 4794 4795 /** 4796 * Divides value. 4797 * @param {number} number 4798 * @returns {currency} 4799 */ 4800 divide: function divide(number) { 4801 var intValue = this.intValue, 4802 _settings = this.s; 4803 return currency(intValue /= parse(number, _settings, false), _settings); 4804 }, 4805 4806 /** 4807 * Takes the currency amount and distributes the values evenly. Any extra pennies 4808 * left over from the distribution will be stacked onto the first set of entries. 4809 * @param {number} count 4810 * @returns {array} 4811 */ 4812 distribute: function distribute(count) { 4813 var intValue = this.intValue, 4814 _precision = this.p, 4815 _settings = this.s, 4816 distribution = [], 4817 split = Math[intValue >= 0 ? 'floor' : 'ceil'](intValue / count), 4818 pennies = Math.abs(intValue - split * count); 4819 4820 for (; count !== 0; count--) { 4821 var item = currency(split / _precision, _settings); // Add any left over pennies 4822 4823 pennies-- > 0 && (item = intValue >= 0 ? item.add(1 / _precision) : item.subtract(1 / _precision)); 4824 distribution.push(item); 4825 } 4826 4827 return distribution; 4828 }, 4829 4830 /** 4831 * Returns the dollar value. 4832 * @returns {number} 4833 */ 4834 dollars: function dollars() { 4835 return ~~this.value; 4836 }, 4837 4838 /** 4839 * Returns the cent value. 4840 * @returns {number} 4841 */ 4842 cents: function cents() { 4843 var intValue = this.intValue, 4844 _precision = this.p; 4845 return ~~(intValue % _precision); 4846 }, 4847 4848 /** 4849 * Formats the value as a string according to the formatting settings. 4850 * @param {boolean} useSymbol - format with currency symbol 4851 * @returns {string} 4852 */ 4853 format: function format(useSymbol) { 4854 var _this$_settings = this.s, 4855 pattern = _this$_settings.pattern, 4856 negativePattern = _this$_settings.negativePattern, 4857 formatWithSymbol = _this$_settings.formatWithSymbol, 4858 symbol = _this$_settings.symbol, 4859 separator = _this$_settings.separator, 4860 decimal = _this$_settings.decimal, 4861 groups = _this$_settings.groups, 4862 values = (this + '').replace(/^-/, '').split('.'), 4863 dollars = values[0], 4864 cents = values[1]; // set symbol formatting 4865 4866 typeof useSymbol === 'undefined' && (useSymbol = formatWithSymbol); 4867 return (this.value >= 0 ? pattern : negativePattern).replace('!', useSymbol ? symbol : '').replace('#', "".concat(dollars.replace(groups, '$1' + separator)).concat(ce
4867nts ? decimal + cents : '')); 4868 }, 4869 4870 /** 4871 * Formats the value as a string according to the formatting settings. 4872 * @returns {string} 4873 */ 4874 toString: function toString() { 4875 var intValue = this.intValue, 4876 _precision = this.p, 4877 _settings = this.s; 4878 return rounding(intValue / _precision, _settings.increment).toFixed(_settings.precision); 4879 }, 4880 4881 /** 4882 * Value for JSON serialization. 4883 * @returns {float} 4884 */ 4885 toJSON: function toJSON() { 4886 return this.value; 4887 } 4888 }; 4889 4890 return currency; 4891 4892})); 4893 4894redesign.define('coreServices/klarna/messagePlacement',[ 4895 'anfUtil/testFramework', 4896 'currency' 4897], function klarnaMessagePlacement(test, currency) { 4898 'use strict'; 4899 4900 /** 4901 * @desc Reponsible for managing the Klarna Onsite Mesaging Script 4902 * @todo Refactor KlarnaMessaginScript and BaiduScript into a Script class responsible for loading the script 4903 * @private 4904 * @final 4905 */ 4906 4907 var KlarnaMessagePlacementScript = { 4908 /** 4909 * @desc the deferred instance that will get resolved when Klarna Onsite Messaging Script is loaded 4910 * @property {Promise} deferred 4911 * @protected 4912 */ 4913 deferred: null 4914 }; 4915 4916 /** 4917 * @desc load the Klarna Onsite Messaging Script onto the DOM 4918 * @protected 4919 * @static 4920 * @return {Promise} Promise represents when the script has loaded 4921 */ 4922 4923 KlarnaMessagePlacementScript.load = function load() { 4924 if (!this.deferred) { 4925 window.$LAB.script(this.getUrl()).wait(function wait() { 4926 this.getDeferred().resolve(this.find().get(0)); 4927 }.bind(this)); 4928 } 4929 return this.getDeferred().promise(); 4930 }; 4931 4932 /** 4933 * @desc get the URL to the Baidu Script 4934 * @protected 4935 * @static 4936 * @return {string} 4937 */ 4938 4939 KlarnaMessagePlacementScript.getUrl = function getUrl() { 4940 return ANF.params.storeAttributes.klarnaOnsiteMessagingScriptURL; 4941 }; 4942 4943 /** 4944 * @desc attempts to find the klarna script tag on the DOM 4945 * @protected 4946 * @static 4947 * @return {HTMLElement} A jQuery wrapped HTMLElement 4948 */ 4949 4950 KlarnaMessagePlacementScript.find = function find() { 4951 var url = this.getUrl(); 4952 return $('script').filter(function filter() { 4953 return String($(this).attr('src')).indexOf(url) >= 0 ? true : false; 4954 }); 4955 }; 4956 4957 /** 4958 * @desc get the Deferred() instance used to resolve when Klarna Onsite Messaging Script has been loaded 4959 * @protected 4960 * @static 4961 * @return {$.Deferred} 4962 */ 4963 4964 KlarnaMessagePlacementScript.getDeferred = function getDeferred() { 4965 if (!this.deferred) { 4966 this.deferred = new $.Deferred(); 4967 } 4968 return this.deferred; 4969 }; 4970 4971 /** 4972 * @desc Responsible for controlling Klarna's Onsite Messaging 4973 * @public 4974 * @class 4975 * @final 4976 * @param {HTMLElement} [element] - The HTMLElement that reprsents the <klarna-placement /> tag wrapped in jQuery 4977 * @param {Object} [options] - Contains options for this KlarnaMessagePlacement 4978 * @param {Object} [options.threshold] - The treshold for determining if we can display the Klarna Onsite Messaging 4979 * @param {Number} [options.threshold.min] - The minimum threshold in the smallest unit of account 4980 * @param {Number} [options.threshold.max] - The maximum threshold in the smallest unit of account 4981 * @param {Function} [options.isWithinOtherConstraints] - A function that will return a boolean if conditions match other constraints. This method will replace this instance's isWithinOtherConstraints() function. 4982 * @exports KlarnaMessagePlacement 4983 */ 4984 4985 function KlarnaMessagePlacement(options) { 4986 if (test.type.object(options)) { 4987 if (test.type.object(options.element)) { 4988 this.setElement(options.element); 4989 } 4990 4991 if (test.type.object(options.threshold)) { 4992 this.setThreshold(options.threshold); 4993 } 4994 4995 if (test.type.function(options.isWithinOtherConstraints)) { 4996 this.isWithinOtherConstraints = options.isWithinOtherConstraints; 4997 } 4998 } 4999 this.hide(); 5000 } 5001 5002 /** 5003 * @desc The HTML element tag name for Klarna Message Placement 5004 * @public 5005 * @property {String} 5006 */ 5007 5008 KlarnaMessagePlacement.TagName = 'klarna-placement'; 5009 5010 /** 5011 * @desc The HTML element attribute name for setting / geting the purchase amount 5012 * @public 5013 * @property {String} 5014 */ 5015 5016 KlarnaMessagePlacement.DataPurchaseAmountAttributeName = 'data-
5016purchase_amount'; 5017 5018 /** 5019 * @desc The event name for trigger Klarna Message Placement updates 5020 * @public 5021 * @property {String} 5022 */ 5023 5024 KlarnaMessagePlacement.RefreshPlacementsEventName = 'refresh-placements'; 5025 5026 /** 5027 * @desc Parse a threshold that comes from a store attribute field 3 5028 * @public 5029 * @static 5030 * @param {String} value - comma separated string e.g. "35,1000" 5031 * @return {Object} containing a min and max field e.g. { min: 3500, max: 100000 } 5032 */ 5033 5034 KlarnaMessagePlacement.parseThreshold = function parseThreshold(value) { 5035 var threshold = {}; 5036 5037 if (!test.type.string(value)) { 5038 return threshold; 5039 } 5040 5041 var parts = value.split(','); 5042 5043 var min = parseNumber(parts.shift()); 5044 if (test.type.number(min)) { 5045 threshold.min = min * 100; // use smallest unit of account 5046 } 5047 5048 var max = parseNumber(parts.shift()); 5049 if (test.type.number(max)) { 5050 threshold.max = max * 100; // use smallest unit of account 5051 } 5052 5053 return threshold; 5054 }; 5055 5056 /** 5057 * @desc Initializes the Klarna Onsite Messaging Script 5058 * @public 5059 * @instance 5060 * @return {Promise} Promise represents an HTMLElement <script /> 5061 */ 5062 5063 KlarnaMessagePlacement.prototype.getScriptTag = function getScriptTag() { 5064 var script = KlarnaMessagePlacementScript.find(); 5065 if (script && script.length) { 5066 return new $.Deferred().resolve(script.get(0)).promise(); 5067 } 5068 return KlarnaMessagePlacementScript.load(); 5069 }; 5070 5071 /** 5072 * @desc set the element that we are controlling 5073 * @public 5074 * @instance 5075 * @param {jQuery} element - Represents the <klarna-placement /> tag wrapped in jQuery 5076 * @return {KlarnaMessagePlacement} Represents the current instance for chaining 5077 */ 5078 5079 KlarnaMessagePlacement.prototype.setElement = function setElement(element) { 5080 if (element) { 5081 this.element = $(element); 5082 } 5083 return this; 5084 }; 5085 5086 /** 5087 * @desc get the element that we are controlling 5088 * @public 5089 * @instance 5090 * @return {jQuery} Represents the <klarna-placement /> tag wrapped in jQuery 5091 * @throws {Error} When there are no <klarna-placement /> tags on the DOM 5092 */ 5093 5094 KlarnaMessagePlacement.prototype.getElement = function getElement() { 5095 if (!this.element) { 5096 this.setElement($(this.constructor.TagName)); 5097 } 5098 return this.element; 5099 }; 5100 5101 /** 5102 * @desc reset the message placement 5103 * @public 5104 * @instance 5105 * @return {KlarnaMessagePlacement} 5106 */ 5107 5108 KlarnaMessagePlacement.prototype.reset = function reset() { 5109 this.element = null; 5110 return this; 5111 }; 5112 5113 /** 5114 * @desc set the threshold for determining if we can display the Klarna Onsite Messaging 5115 * @public 5116 * @instance 5117 * @param {Object} threshold - The treshold for determining if we can display the Klarna Onsite Messaging 5118 * @param {Number} threshold.min - The minimum threshold in the smallest unit of account 5119 * @param {Number} threshold.max - The maximum threshold in the smallest unit of account 5120 * @return {KlarnaMessagePlacement} Represents the current instance for chaining 5121 */ 5122 5123 KlarnaMessagePlacement.prototype.setThreshold = function setThreshold(threshold) { 5124 this.threshold = threshold; 5125 return this; 5126 }; 5127 5128 /** 5129 * @desc get the threshold for determing if we can display the Klarna Onsite Messaging 5130 * @public 5131 * @instance 5132 * @return {Object} Represents the min and max threshold 5133 */ 5134 5135 KlarnaMessagePlacement.prototype.getThreshold = function getThreshold() { 5136 if (!this.threshold) { 5137 this.setThreshold({}); 5138 } 5139 return this.threshold; 5140 }; 5141 5142 /** 5143 * @desc determine if the value is within the threshold 5144 * @public 5145 * @instance 5146 * @param {Number} value - Represents the value we are checking 5147 * @return {Boolean} 5148 */ 5149 5150 KlarnaMessagePlacement.prototype.isWithinThreshold = function isWithinThreshold(value) { 5151 var threshold = this.getThreshold(); 5152 5153 if (test.type.number(threshold.min)) { 5154 if (value < threshold.min) { 5155 return false; 5156 } 5157 } 5158 5159 if (test.type.number(threshold.max)) { 5160 if (value > threshold.max) { 5161 return false; 5162 } 5163 } 5164 5165 return true; 5166 }; 5167 5168 /** 5169 * @desc Allows for an instance to supply additional constraints by overriding this method 5170 * @public 5171 * @instance 5172 * @override 5173 * @return {Boolean} 5174 */ 5175 5176 KlarnaMessagePlacement.prototype.isWithinOtherConstraints = function isWithinOtherConstraints() { 5177 return true; 5178 }; 5179 5180 /** 5181 * @desc Determine if the Klarna Onsite Messaging should be visible to the user based on the Klarna Card Brands 5182 * @public 5183 * @instance 5184 * @param {Number} value - The value used to determine if we can display or not 5185 * @return {Boolean} 5186 */ 5187 5188 KlarnaMessagePlacement.prototype.canDisplay = function canDisplay(value) { 5189 return this.isWithinThreshold(value) && this.isWithinOtherConstraints(); 5190 }; 5191 5192 /** 5193 * @desc Updates the Klarna Onsite Messaging by setting the value on the element and pushing an event to Klarna 5194 * @public 5195 * @instance 5196 * @param {number} value - The value used to determine if we can display or not 5197 * @return {KlarnaMessagePlacement} Represents the current instance for chaining 5198 */ 5199 5200 KlarnaMessagePlacement.prototype.update = function update(value) { 5201 try { 5202 var purchaseAmount = currency(value).intValue; 5203 this.getElement().attr(this.constructor.DataPurchaseAmountAttributeName, purchaseAmount); 5204 if (this.canDisplay(purchaseAmount)) { 5205 this.show(); 5206 } else { 5207 this.hide(); 5208 } 5209 window.KlarnaOnsiteService = window.KlarnaOnsiteService || []; 5210 window.KlarnaOnsiteService.push({ 5211 eventName: this.constructor.RefreshPlacementsEventName 5212 }); 5213 } finally { 5214 return this; 5215 } 5216 }; 5217 5218 /** 5219 * @desc hide the placement 5220 * @public 5221 * @instance 5222 * @return {KlarnaMessagePlacement} 5223 */ 5224 5225 KlarnaMessagePlacement.prototype.hide = function hide() { 5226 this.getElement().parent().hide(); 5227 return this; 5228 }; 5229 5230 /**
5231 * @desc show the placement 5232 * @public 5233 * @instance 5234 * @return {KlarnaMessagePlacement} 5235 */ 5236 5237 KlarnaMessagePlacement.prototype.show = function show() { 5238 this.getElement().parent().show(); 5239 return this; 5240 }; 5241 5242 return KlarnaMessagePlacement; 5243 5244 /** 5245 * @desc parse a number 5246 * @private 5247 * @param {Number} value 5248 * @return {Number|undefined} 5249 */ 5250 5251 function parseNumber(value) { 5252 var num = parseInt(value, 10); 5253 if (isNaN(num)) { 5254 return void 0; 5255 } 5256 return num; 5257 } 5258}); 5259 5260redesign.define('productPartial/pdp_productPrice',['handlebars','dsPartial/product-price','dsPartial/product-price-text','wcsHelper/i18n'], function(Handlebars) { 5261 5262this["ANF"] = this["ANF"] || {}; 5263this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 5264 5265Handlebars.registerPartial("pdp_productPrice", this["ANF"]["templates"]["pdp_productPrice"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 5266 var stack1; 5267 5268 return ((stack1 = container.invokePartial(partials["product-price"],depth0,{"name":"product-price","hash":{"brand":(depth0 != null ? depth0.productBrand : depth0),"clearance-text":(depth0 != null ? depth0.clearanceText : depth0),"description":(depth0 != null ? depth0.description : depth0),"type":"clearance","class-list":"js-main-price"},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 5269},"2":function(container,depth0,helpers,partials,data) { 5270 var stack1; 5271 5272 return ((stack1 = container.invokePartial(partials["product-price-text"],depth0,{"name":"product-price-text","hash":{"formatted-price":(depth0 != null ? depth0.price : depth0),"type":"original","size":"large"},"data":data,"indent":"\t\t","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 5273 + ((stack1 = container.invokePartial(partials["product-price-text"],depth0,{"name":"product-price-text","hash":{"formatted-price":(depth0 != null ? depth0.discountPrice : depth0),"type":"discount","size":"large"},"data":data,"indent":"\t\t","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 5274},"4":function(container,depth0,helpers,partials,data) { 5275 var stack1; 5276 5277 return ((stack1 = container.invokePartial(partials["product-price"],depth0,{"name":"product-price","hash":{"brand":(depth0 != null ? depth0.productBrand : depth0),"description":(depth0 != null ? depth0.description : depth0),"class-list":"js-main-price"},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 5278},"5":function(container,depth0,helpers,partials,data) { 5279 var stack1; 5280 5281 return ((stack1 = container.invokePartial(partials["product-price-text"],depth0,{"name":"product-price-text","hash":{"formatted-price":(depth0 != null ? depth0.price : depth0),"size":"large"},"data":data,"indent":"\t\t","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 5282},"7":function(container,depth0,helpers,partials,data) { 5283 var stack1; 5284 5285 return ((stack1 = container.invokePartial(partials["product-price"],depth0,{"name":"product-price","hash":{"brand":(depth0 != null ? depth0.productBrand : depth0),"clearance-text":helpers.i18n.call(depth0 != null ? depth0 : (container.nullContext || {}),"loyalty.MEMBER_CLEARANCE",{"name":"i18n","hash":{},"data":data}),"description":(depth0 != null ? depth0.memberPriceDescription : depth0),"type":"member","class-list":"js-member-price"},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 5286},"8":function(container,depth0,helpers,partials,data) { 5287 var stack1; 5288 5289 return ((stack1 = container.invokePartial(partials["product-price-text"],depth0,{"name":"product-price-text","hash":{"formatted-price":(depth0 != null ? depth0.memberPrice : depth0),"type":"member","size":"large"},"data":data,"indent":"\t\t","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 5290},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 5291 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 5292 5293 return "\n" 5294 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isClearance : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.program(4, data, 0),"data":data})) != null ? stack1 : "") 5295 + "\n" 5296 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasMemberPrice : depth0),{"name":"if","hash":{},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 5297},"usePartial":true,"useData":true})); 5298 5299return this["ANF"]; 5300 5301}); 5302redesign.define('productModules/product/productPrice',[ 5303 'anfUtil/testFramework', 5304 'coreServices/klarna/messagePlacement', 5305 'anfUtil/constants', 5306 'productPartial/pdp_productPrice', 5307 'productModules/helpers/Utils' 5308], function productPrice( 5309 test, 5310 KlarnaMessagePlacement, 5311 coreConstants, 5312 productPartialPdpProductPrice, 5313 Utils 5314) { 5315 'use strict'; 5316 5317 // Aliasing this as variable since we can't use const yet 5318 5319 // Constants
5320 var priceContainerSelector = '.js-product-price'; 5321 var priceTextSelector = '.product-price-text'; 5322 var skuSelect = '.product-page__skus'; 5323 5324 var memberPricing = { 5325 contractPrice: 0, 5326 contractPriceFmt: '' 5327 }; 5328 5329 var klarna = { 5330 messagePlacement: null, 5331 threshold: KlarnaMessagePlacement.parseThreshold(ANF.params.storeAttributes.klarnaOnsiteMessagingProductThreshold), 5332 hasKlarnaOnsiteMessaging: function hasKlarnaOnsiteMessaging() { 5333 return ANF.params.storeAttributes.hasKlarnaOnsiteMessagingProduct; 5334 }, 5335 isWithinOtherConstraints: function isWithinOtherConstraints() { 5336 return this.hasKlarnaOnsiteMessaging(); 5337 }, 5338 getMessagePlacement: function getMessagePlacement() { 5339 var deferred = new $.Deferred(); 5340 5341 if (!this.messagePlacement) { 5342 this.messagePlacement = new KlarnaMessagePlacement({ 5343 threshold: this.threshold, 5344 isWithinOtherConstraints: this.isWithinOtherConstraints.bind(this) 5345 }); 5346 } 5347 5348 if (!this.hasKlarnaOnsiteMessaging()) { 5349 return deferred.resolve(this.messagePlacement).promise(); 5350 } 5351 5352 this.messagePlacement.getScriptTag().done(function done() { 5353 deferred.resolve(this.messagePlacement); 5354 }.bind(this)); 5355 5356 return deferred.promise(); 5357 } 5358 }; 5359 5360 function updatePricetag(sku, productContainer) { 5361 var priceModel = { 5362 isClearance: false, 5363 price: '', 5364 hasMemberPrice: false, 5365 productBrand: Utils.getProductData(productContainer.data('productid')).productAttrs.brand 5366 }; 5367 var $priceContainer = productContainer.find(priceContainerSelector); 5368 var contractPrice = memberPricing.contractPrice; 5369 var contractPriceFmt = memberPricing.contractPriceFmt; 5370 5371 /* Handle main price */ 5372 priceModel.price = sku.listPriceFmt; 5373 priceModel.description = sku.listPriceFmt; 5374 if (sku.listPrice !== sku.offerPrice) { // Discount price 5375 priceModel.discountPrice = sku.offerPriceFmt; 5376 priceModel.isClearance = true; 5377 if (sku.priceFlag === '2' || sku.priceFlag === '7') { 5378 priceModel.clearanceText = ANF.messaging.catalogV2.TMNT.cardClearance.value; 5379 } 5380 priceModel.description = ANF.messaging.product.GLB_PRICE_DESC_IND_IND.replace('{0}', sku.listPriceFmt) 5381 .replace('{1}', sku.offerPriceFmt); 5382 } 5383 5384 var klarnaPrice = priceModel.discountPrice || priceModel.price; 5385 5386 if (hasMemberPrice(contractPrice, sku)) { 5387 klarnaPrice = contractPrice; 5388 priceModel.hasMemberPrice = true; 5389 priceModel.memberPriceDescription = contractPriceFmt; 5390 priceModel.memberPrice = contractPriceFmt; 5391 } 5392 5393 $priceContainer.empty().html(productPartialPdpProductPrice.templates.pdp_productPrice(priceModel)); 5394 $priceContainer.find(priceTextSelector).addClass('ds-override'); 5395 if (klarna.hasKlarnaOnsiteMessaging()) { 5396 klarna.getMessagePlacement().done(function done(messagePlacement) { 5397 messagePlacement.reset().update(klarnaPrice); 5398 }); 5399 } 5400 5401 if (ANF.params.storeAttributes.checkout.hasPayOverTimeMessagingProduct) { 5402 window.dispatchEvent(new CustomEvent('payOverTime:refresh', { 5403 detail: { 5404 purchaseAmount: parseFloat(String(klarnaPrice).replace(/[^0-9\.]/, '')), 5405 page: 'product' 5406 } 5407 })); 5408 } 5409 } 5410 5411 function hasMemberPrice(contractPrice, sku) { 5412 return coreConstants.anf('params.storeAttributes.loyalty.memberPricingEnabled') && 5413 (contractPrice && contractPrice > 0) && 5414 (contractPrice < sku.listPrice && contractPrice < sku.offerPrice); 5415 } 5416 5417 function updateSwatchPrice(e, data) { 5418 if (typeof data === 'object' && typeof data.productId !== 'undefined') { 5419 var kicSkus = $(data.skuContainer).find(skuSelect).find('option[data-kic=' + data.kic + ']'); 5420 5421 if (kicSkus.length > 0) { 5422 var sku = data.productPrices[data.productId].items[kicSkus[0].value]; 5423 5424 setContractPricing(data.productPrices[data.productId]); 5425 5426 if (sku) { 5427 updatePricetag(sku, $(data.productSection)); 5428 reRenderGermanVAT(sku); 5429 } 5430 } 5431 } 5432 } 5433 5434 function updateSkuPrice(e, data) { 5435 if ( typeof data === 'object' && typeof data.productId !== 'undefined' && typeof data.skuId !== 'undefined' ) { 5436 var sku = data.productPrices[data.productId].items[data.skuId]; 5437 5438 setContractPricing(data.productPrices[data.productId]); 5439 updatePricetag(sku, $(data.productSection));
5440 reRenderGermanVAT(sku); 5441 } 5442 } 5443 5444 function setContractPricing(product) { 5445 if (product !== null && test.type.object(product)) { 5446 memberPricing.contractPrice = product.lowContractPrice ? product.lowContractPrice : 0; 5447 memberPricing.contractPriceFmt = product.lowContractPriceFmt ? product.lowContractPriceFmt : ''; 5448 } 5449 } 5450 5451 function isPromotionalPricing(sku) { 5452 if (!sku) { 5453 return false; 5454 } 5455 var contractPrice = memberPricing.contractPrice || 0; 5456 var priceFlag = sku.priceFlag; 5457 var hasDiscountPricing = (sku.listPrice !== sku.offerPrice); 5458 var hasMemberPricing = (contractPrice > 0 && contractPrice < sku.listPrice && contractPrice < sku.offerPrice); 5459 var hasPriceFlag = (priceFlag === '2' || priceFlag === '7'); 5460 var hasPromoMessage = (sku.merchPromoMsgType && sku.merchPromoMsg); 5461 var result = (hasMemberPricing || hasDiscountPricing || hasPriceFlag || hasPromoMessage); 5462 return result; 5463 } 5464 5465 function reRenderGermanVAT(sku) { 5466 var $vatContainer = $('.js-german-vat-container'); 5467 if (!$vatContainer.length) return; 5468 5469 var germanVatText = $vatContainer.data('vat-text') || ''; 5470 $vatContainer.empty(); 5471 5472 var className = isPromotionalPricing(sku) 5473 ? 'product-price-german-tax-discounted' 5474 : 'product-price-german-tax'; 5475 5476 $vatContainer.html( 5477 '<div class="' + className + '" data-property="GERMAN_INCLUDE_TAX">' + 5478 germanVatText + 5479 '</div>' 5480 ); 5481 } 5482 5483 var publicMembers = { 5484 listen: function listen() { 5485 $(ANF).on('productPrice:swatchSelected', updateSwatchPrice); 5486 $(ANF).on('productPrice:skuSelected', updateSkuPrice); 5487 } 5488 }; 5489 5490 return publicMembers; 5491} 5492); 5493 5494!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():'function' === typeof redesign.define && redesign.define.amd?redesign.define('anfDataBridge',[],e):"object"==typeof exports?exports.DataBridge=e():t.DataBridge=e()}(self,()=>(()=>{"use strict";var t={d:(e,r)=>{for(var n in r)t.o(r,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:r[n]})}};t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),t.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var e={};function r(t){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},r(t)}t.d(e,{default:()=>St});var n=function(){return"undefined"!=typeof window?window:void 0!==t.g?t.g:Object.create(null)};function o(t){var e,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:new WeakMap;if(null===t||"object"!==r(t))return t;if(i.has(t))return i.get(t);if(t instanceof Date)return e=new Date(t.getTime()),i.set(t,e),e;if(t instanceof RegExp)return e=new RegExp(t.source,t.flags),i.set(t,e),e;if(t instanceof Error)return e=new t.constructor(t.message),i.set(t,e),t.stack&&Object.defineProperty(e,"stack",{value:t.stack,writable:!0,configurable:!0}),void 0!==t.cause&&(e.cause=o(t.cause,i)),Object.getOwnPropertyNames(t).forEach(function(r){if("stack"!==r&&"message"!==r&&"cause"!==r)try{e[r]=o(t[r],i)}catch(t){}}),e;if(Array.isArray(t)){e=new Array(t.length),i.set(t,e);for(var u=0;u<t.length;u++)e[u]=o(t[u],i);return e}if(t instanceof Map)return e=new Map,i.set(t,e),t.forEach(function(t,r){e.set(o(r,i),o(t,i))}),e;if(t instanceof Set)return e=new Set,i.set(t,e),t.forEach(function(t){e.add(o(t,i))}),e;if("undefined"!=typeof ArrayBuffer&&t instanceof ArrayBuffer)return e=t.slice(0),i.set(t,e),e;if("undefined"!=typeof ArrayBuffer&&t&&t.buffer instanceof ArrayBuffer){var a=t.constructor.name,s=n();if(/^(Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|BigInt64Array|BigUint64Array)$/.test(a)){var c=o(t.buffer,i);return"function"==typeof s[a]?(e=new s[a](c,t.byteOffset,t.length),i.set(t,e),e):(e=new Uint8Array(c,t.byteOffset,t.byteLength),i.set(t,e),e)}}if("undefined"!=typeof DataView&&t instanceof DataView){var f=o(t.buffer,i);return e=new DataView(f,t.byteOffset,t.byteLength),i.set(t,e),e}if("undefined"!=typeof CustomEvent&&t instanceof CustomEvent){var l=t.type,p=t.detail,y=t.bubbles,b=void 0!==y&&y,v=t.cancelable,h=void 0!==v&&v,d=t.composed,m={bubbles:b,cancelable:h,composed:void 0!==d&&d,detail:void 0!==p?o(p,i):void 0};try{return e=new CustomEvent(l,m),i.set(t,e),e}catch(e){var g=document.createEvent("CustomEvent");return g.initCustomEvent(l,b,h,m.detail),i.set(t,g),g}}if("undefined"!=typeof Event&&t instanceof Event&&!(t instanceof CustomEvent)){var w=t.type,S=t.bubbles,O=void 0!==S&&S,_=t.cancelable,A=void 0!==_&&_,j=t.composed,E=void 0!==j&&j;try{return e=new Event(w,{bubbles:O,cancelable:A,composed:E}),i.set(t,e),e}catch(e){var x=document.createEvent("Event");return x.initEvent(w,O,A),i.set(t,x),x}}if("undefined"!=typeof Blob&&t instanceof Blob)return e=t.slice(0,t.size,t.type),i.set(t,e),e;if("undefined"!=typeof File&&t instanceof File)try{return e=new File([t],t.name,{type:t.type,lastModified:t.lastModified}),i.set(t,e),e}catch(r){return(e=t.slice(0,t.size,t.type)).name=t.name,e.lastModified=t.lastModified,i.set(t,e),e}if("undefined"!=typeof URL&&t instanceof URL)return e=new URL(t.href),i.set(t,e),e;if("undefined"!=typeof URLSearchParams&&t instanceof URLSearchParams)return e=new URLSearchParams(t.toString()),i.set(t,e),e;if("undefined"!=typeof FormData&&t instanceof FormData)return e=new FormData,i.set(t,e),t.forEach(function(t,r){e.append(r,t)}),e;e=Object.create(Object.getPrototypeOf(t)),i.set(t,e);for(var P=Object.getOwnPropertyDescriptors(t),T=Object.keys(P),k={},C=0;C<T.length;C++){var I=T[C],M=P[I];void 0!==M.value&&(M.value=o(M.value,i)),k[I]=M}if(Object.defineProperties(e,k),"undefined"!=typeof Symbol)for(var U=Object.getOwnPropertySymbols(t),z=0;z<U.length;z++){var F=U[z],D=Object.getOwnPropertyDescriptor(t,F);void 0!==D.value&&(D.value=o(D.value,i)),Object.defineProperty(e,F,D)}return e}var i=n();function u(t){var e=new WeakMap,n=function t(n){if(null===n||"object"!==r(n))return n;if(e.has(n))return e.get(n);if("undefined"!=typeof CustomEvent&&n instanceof CustomEvent){var o={__isCustomEvent:!0,type:n.type,detail:n.detail?t(n.detail):void 0,bubbles:n.bubbles,cancelable:n.cancelable,composed:n.composed};return e.set(n,o),o}if(Array.isArray(n)){var i=[];e.set(n,i);for(var u=0;u<n.length;u++)i[u]=t(n[u]);return i}if(n instanceof Map){var a=new Map;
5494return e.set(n,a),n.forEach(function(e,r){a.set(t(r),t(e))}),a}if(n instanceof Set){var s=new Set;return e.set(n,s),n.forEach(function(e){s.add(t(e))}),s}var c=Array.isArray(n)?[]:Object.create(Object.getPrototypeOf(n));return e.set(n,c),Object.getOwnPropertyNames(n).forEach(function(e){Object.prototype.hasOwnProperty.call(n,e)&&(c[e]=t(n[e]))}),c}(t);return structuredClone(n)}"function"!=typeof i.structuredClone&&(i.structuredClone=o);var a=function(t,e){return a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r])},a(t,e)};function s(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function r(){this.constructor=t}a(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}function c(t){var e="function"==typeof Symbol&&Symbol.iterator,r=e&&t[e],n=0;if(r)return r.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function f(t,e){var r="function"==typeof Symbol&&t[Symbol.iterator];if(!r)return t;var n,o,i=r.call(t),u=[];try{for(;(void 0===e||e-- >0)&&!(n=i.next()).done;)u.push(n.value)}catch(t){o={error:t}}finally{try{n&&!n.done&&(r=i.return)&&r.call(i)}finally{if(o)throw o.error}}return u}function l(t,e,r){if(r||2===arguments.length)for(var n,o=0,i=e.length;o<i;o++)!n&&o in e||(n||(n=Array.prototype.slice.call(e,0,o)),n[o]=e[o]);return t.concat(n||Array.prototype.slice.call(e))}function p(t){return"function"==typeof t}function y(t){var e=t(function(t){Error.call(t),t.stack=(new Error).stack});return e.prototype=Object.create(Error.prototype),e.prototype.constructor=e,e}Object.create,Object.create,"function"==typeof SuppressedError&&SuppressedError;var b=y(function(t){return function(e){t(this),this.message=e?e.length+" errors occurred during unsubscription:\n"+e.map(function(t,e){return e+1+") "+t.toString()}).join("\n "):"",this.name="UnsubscriptionError",this.errors=e}});function v(t,e){if(t){var r=t.indexOf(e);0<=r&&t.splice(r,1)}}var h=function(){function t(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}var e;return t.prototype.unsubscribe=function(){var t,e,r,n,o;if(!this.closed){this.closed=!0;var i=this._parentage;if(i)if(this._parentage=null,Array.isArray(i))try{for(var u=c(i),a=u.next();!a.done;a=u.next())a.value.remove(this)}catch(e){t={error:e}}finally{try{a&&!a.done&&(e=u.return)&&e.call(u)}finally{if(t)throw t.error}}else i.remove(this);var s=this.initialTeardown;if(p(s))try{s()}catch(t){o=t instanceof b?t.errors:[t]}var y=this._finalizers;if(y){this._finalizers=null;try{for(var v=c(y),h=v.next();!h.done;h=v.next()){var d=h.value;try{g(d)}catch(t){o=null!=o?o:[],t instanceof b?o=l(l([],f(o)),f(t.errors)):o.push(t)}}}catch(t){r={error:t}}finally{try{h&&!h.done&&(n=v.return)&&n.call(v)}finally{if(r)throw r.error}}}if(o)throw new b(o)}},t.prototype.add=function(e){var r;if(e&&e!==this)if(this.closed)g(e);else{if(e instanceof t){if(e.closed||e._hasParent(this))return;e._addParent(this)}(this._finalizers=null!==(r=this._finalizers)&&void 0!==r?r:[]).push(e)}},t.prototype._hasParent=function(t){var e=this._parentage;return e===t||Array.isArray(e)&&e.includes(t)},t.prototype._addParent=function(t){var e=this._parentage;this._parentage=Array.isArray(e)?(e.push(t),e):e?[e,t]:t},t.prototype._removeParent=function(t){var e=this._parentage;e===t?this._parentage=null:Array.isArray(e)&&v(e,t)},t.prototype.remove=function(e){var r=this._finalizers;r&&v(r,e),e instanceof t&&e._removeParent(this)},t.EMPTY=((e=new t).closed=!0,e),t}(),d=h.EMPTY;function m(t){return t instanceof h||t&&"closed"in t&&p(t.remove)&&p(t.add)&&p(t.unsubscribe)}function g(t){p(t)?t():t.unsubscribe()}var w=null,S=null,O=void 0,_=!1,A=!1,j={setTimeout:function(t,e){for(var r=[],n=2;n<arguments.length;n++)r[n-2]=arguments[n];var o=j.delegate;return(null==o?void 0:o.setTimeout)?o.setTimeout.apply(o,l([t,e],f(r))):setTimeout.apply(void 0,l([t,e],f(r)))},clearTimeout:function(t){var e=j.delegate;return((null==e?void 0:e.clearTimeout)||clearTimeout)(t)},delegate:void 0};function E(){}var x=P("C",void 0,void 0);function P(t,e,r){return{kind:t,value:e,error:r}}var T=null;function k(t){if(_){var e=!T;if(e&&(T={errorThrown:!1,error:null}),t(),e){var r=T,n=r.errorThrown,o=r.error;if(T=null,n)throw o}}else t()}var C=function(t){function e(e){var r=t.call(this)||this;return r.isStopped=!1,e?(r.destination=e,m(e)&&e.add(r)):r.destination=N,r}return s(e,t),e.create=function(t,e,r){return new z(t,e,r)},e.prototype.next=function(t){this.isStopped?D(function(t){return P("N",t,void 0)}(t),this):this._next(t)},e.prototype.error=function(t){this.isStopped?D(P("E",void 0,t),this):(this.isStopped=!0,this._error(t))},e.prototype.complete=function(){this.isStopped?D(x,this):(this.isStopped=!0,this._complete())},e.prototype.unsubscribe=function(){this.closed||(this.isStopped=!0,t.prototype.unsubscribe.call(this),this.destination=null)},e.prototype._next=function(t){this.destination.next(t)},e.prototype._error=function(t){try{this.destination.error(t)}finally{this.unsubscribe()}},e.prototype._complete=function(){try{this.destination.complete()}finally{this.unsubscribe()}},e}(h),I=Function.prototype.bind;function M(t,e){return I.call(t,e)}var U=function(){function t(t){this.partialObserver=t}return t.prototype.next=function(t){var e=this.partialObserver;if(e.next)try{e.next(t)}catch(t){F(t)}},t.prototype.error=function(t){var e=this.partialObserver;if(e.error)try{e.error(t)}catch(t){F(t)}else F(t)},t.prototype.complete=function(){var t=this.partialObserver;if(t.complete)try{t.complete()}catch(t){F(t)}},t}(),z=function(t){function e(e,r,n){var o,i,u=t.call(this)||this;return p(e)||!e?o={next:null!=e?e:void 0,error:null!=r?r:void 0,complete:null!=n?n:void 0}:u&&A?((i=Object.create(e)).unsubscribe=function(){return u.unsubscribe()},o={next:e.next&&M(e.next,i),error:e.error&&M(e.error,i),complete:e.complete&&M(e.complete,i)}):o=e,u.destination=new U(o),u}return s(e,t),e}(C);function F(t){var e;_?(e=t,_&&T&&(T.errorThrown=!0,T.error=e)):function(t){j.setTimeout(function(){if(!w)throw t;w(t)})}(t)}function D(t,e){var r=S;r&&j.setTimeout(function(){return r(t,e)})}var N={closed:!0,next:E,error:function(t){throw t},complete:E},B="function"==typeof Symbol&&Symbol.observable||"@@observable";function R(t){return t}var W=function(){function t(t){t&&(this._subscribe=t)}return t.prototype.lift=function(e){var r=new t;return r.source=this,r.operator=e,r},t.prototype.subscribe=function(t,e,r){var n,o=this,i=(n=t)&&n instanceof C||function(t){return t&&p(t.next)&&p(t.error)&&p(t.complete)}(n)&&m(n)?t:new z(t,e,r);return k(function(){var t=o,e=t.operator,r=t.source;i.add(e?e.call(i,r):r?o._subscribe(i):o._trySubscribe(i))}),i},t.prototype._trySubscribe=function(t){try{return this._subscribe(t)}catch(e){t.error(e)}},t.prototype.forEach=function(t,e){var r=this;return new(e=L(e))(function(e,n){var o=new z({next:function(e){try{t(e)}catch(t){n(t),o.unsubscribe()}},error:n,complete:e});r.subscribe(o)})},t.prototype._subscribe=function(t){var e;return null===(e=this.source)||void 0===e?void 0:e.subscribe(t)},t.prototype[B]=function(){return this},t.prototype.pipe=function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];return(0===(r=t).length?R:1===r.length?r[0]:function(t){return r.reduce(function(t,e){return e(t)},t)})(this);var r},t.prototype.toPromise=function(t){var e=this;return new(t=L(t))(function(t,r){var n;e.subscribe(function(t){return n=t},function(t){return r(t)},function(){return t(n)})})},t.create=function(e){return new t(e)},t}();function L(t){var e;return null!==(e=null!=t?t:O)&&void 0!==e?e:Promise}var V=y(function(t){return function(){t(this),this.name="ObjectUnsubscribedError",this.message="object unsubscribed"}}),$=function(t){function e(){var e=t.call(this)||this;return e.closed=!1,e.currentObservers=null,e.observers=[],e.isStopped=!1,e.hasError=!1,e.thrownError=null,e}return s(e,t),e.prototype.lift=function(t){var e=new Y(this,this);return e.operator=t,e},e.prototype._throwIfClosed=function(){if(this.closed)throw new V},e.prototype.next=function(t){var e=this;k(function(){var r,n;if(e._throwIfClosed(),!e.isStopped){e.currentObservers||(e.currentObservers=Array.from(e.observers));try{for(var o=c(e.currentObservers),i=o.next();!i.done;i=o.next())i.value.next(t)}catch(t){r={error:t}}finally{try{i&&!i.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}}})},e.prototype.error=function(t){var e=this;k(function(){if(e._throwIfClosed(),!e.isStopped){e.hasError=e.isStopped=!0,e.thrownError=t;for(var r=e.observers;r.length;)r.shift().error(t)}})},e.prototype.complete=function(){var t=this;k(function(){if(t._throwIfClosed(),!t.isStopped){t.isStopped=!0;for(var e=t.observers;e.length;)e.shift().complete()}})},e.prototype.unsubscribe=function(){this.isStopped=this.closed=!0,this.observers=this.currentObservers=null},Object.defineProperty(e.prototype,"observed",{get:function(){var t;return(null===(t=this.observers)||void 0===t?void 0:t.length)>0}
5494,enumerable:!1,configurable:!0}),e.prototype._trySubscribe=function(e){return this._throwIfClosed(),t.prototype._trySubscribe.call(this,e)},e.prototype._subscribe=function(t){return this._throwIfClosed(),this._checkFinalizedStatuses(t),this._innerSubscribe(t)},e.prototype._innerSubscribe=function(t){var e=this,r=this,n=r.hasError,o=r.isStopped,i=r.observers;return n||o?d:(this.currentObservers=null,i.push(t),new h(function(){e.currentObservers=null,v(i,t)}))},e.prototype._checkFinalizedStatuses=function(t){var e=this,r=e.hasError,n=e.thrownError,o=e.isStopped;r?t.error(n):o&&t.complete()},e.prototype.asObservable=function(){var t=new W;return t.source=this,t},e.create=function(t,e){return new Y(t,e)},e}(W),Y=function(t){function e(e,r){var n=t.call(this)||this;return n.destination=e,n.source=r,n}return s(e,t),e.prototype.next=function(t){var e,r;null===(r=null===(e=this.destination)||void 0===e?void 0:e.next)||void 0===r||r.call(e,t)},e.prototype.error=function(t){var e,r;null===(r=null===(e=this.destination)||void 0===e?void 0:e.error)||void 0===r||r.call(e,t)},e.prototype.complete=function(){var t,e;null===(e=null===(t=this.destination)||void 0===t?void 0:t.complete)||void 0===e||e.call(t)},e.prototype._subscribe=function(t){var e,r;return null!==(r=null===(e=this.source)||void 0===e?void 0:e.subscribe(t))&&void 0!==r?r:d},e}($),G=function(t){function e(e){var r=t.call(this)||this;return r._value=e,r}return s(e,t),Object.defineProperty(e.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),e.prototype._subscribe=function(e){var r=t.prototype._subscribe.call(this,e);return!r.closed&&e.next(this._value),r},e.prototype.getValue=function(){var t=this,e=t.hasError,r=t.thrownError,n=t._value;if(e)throw r;return this._throwIfClosed(),n},e.prototype.next=function(e){t.prototype.next.call(this,this._value=e)},e}($),K={now:function(){return(K.delegate||Date).now()},delegate:void 0},X=function(t){function e(e,r,n){void 0===e&&(e=1/0),void 0===r&&(r=1/0),void 0===n&&(n=K);var o=t.call(this)||this;return o._bufferSize=e,o._windowTime=r,o._timestampProvider=n,o._buffer=[],o._infiniteTimeWindow=!0,o._infiniteTimeWindow=r===1/0,o._bufferSize=Math.max(1,e),o._windowTime=Math.max(1,r),o}return s(e,t),e.prototype.next=function(e){var r=this,n=r.isStopped,o=r._buffer,i=r._infiniteTimeWindow,u=r._timestampProvider,a=r._windowTime;n||(o.push(e),!i&&o.push(u.now()+a)),this._trimBuffer(),t.prototype.next.call(this,e)},e.prototype._subscribe=function(t){this._throwIfClosed(),this._trimBuffer();for(var e=this._innerSubscribe(t),r=this._infiniteTimeWindow,n=this._buffer.slice(),o=0;o<n.length&&!t.closed;o+=r?1:2)t.next(n[o]);return this._checkFinalizedStatuses(t),e},e.prototype._trimBuffer=function(){var t=this,e=t._bufferSize,r=t._timestampProvider,n=t._buffer,o=t._infiniteTimeWindow,i=(o?1:2)*e;if(e<1/0&&i<n.length&&n.splice(0,n.length-i),!o){for(var u=r.now(),a=0,s=1;s<n.length&&n[s]<=u;s+=2)a=s;a&&n.splice(0,a+1)}},e}($);function q(t){return function(e){if(function(t){return p(null==t?void 0:t.lift)}(e))return e.lift(function(e){try{return t(e,this)}catch(t){this.error(t)}});throw new TypeError("Unable to lift unknown Observable type")}}function H(t,e,r,n,o){return new J(t,e,r,n,o)}var J=function(t){function e(e,r,n,o,i,u){var a=t.call(this,e)||this;return a.onFinalize=i,a.shouldUnsubscribe=u,a._next=r?function(t){try{r(t)}catch(t){e.error(t)}}:t.prototype._next,a._error=o?function(t){try{o(t)}catch(t){e.error(t)}finally{this.unsubscribe()}}:t.prototype._error,a._complete=n?function(){try{n()}catch(t){e.error(t)}finally{this.unsubscribe()}}:t.prototype._complete,a}return s(e,t),e.prototype.unsubscribe=function(){var e;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){var r=this.closed;t.prototype.unsubscribe.call(this),!r&&(null===(e=this.onFinalize)||void 0===e||e.call(this))}},e}(C);function Q(t,e){return t===e}function Z(t,e,r){if(null==t)return r;if(null==e)return r;if("string"==typeof e&&-1===e.indexOf(".")&&-1===e.indexOf("["))return void 0===t[e]?r:t[e];var n;if(Array.isArray(e))n=e;else try{n=e.toString().replace(/\[(\d+)\]/g,".$1").split(".")}catch(t){return r}for(var o=t,i=0;i<n.length;i++){var u=n[i];if(""!==u){if(null==o)return r;void 0!==(o=o[u])&&""===n[i+1]&&void 0!==o[""]&&(o=o[""],i++)}}return void 0===o?r:o}function tt(t){return tt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},tt(t)}function et(t){if(null===t||"object"!==tt(t))return!1;var e=Object.getPrototypeOf(t);if(null===e)return!0;var r=Object.prototype.hasOwnProperty.call(e,"constructor")&&e.constructor;return"function"==typeof r&&r instanceof r&&Function.prototype.toString.call(r)===Function.prototype.toString.call(Object)}function rt(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);r<e;r++)n[r]=t[r];return n}function nt(t){return nt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},nt(t)}function ot(t,e){if(t===e)return 0!==t||0!==e||1/t==1/e;if(null==t||null==e)return t===e;var r=nt(t),n=nt(e);if("number"===r&&"number"===n&&Number.isNaN(t)&&Number.isNaN(e))return!0;
5494if("object"===r&&"object"!==n||"object"!==r&&"object"===n)return("string"===r||"number"===r||"boolean"===r)&&t==e||("string"===n||"number"===n||"boolean"===n)&&t==e;if("object"!==r||"object"!==n)return!1;if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return!1;for(var o=0;o<t.length;o+=1){var i=o in t;if(i!==o in e)return!1;if(i&&!ot(t[o],e[o]))return!1}return!0}if(Array.isArray(t)||Array.isArray(e))return!1;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(t instanceof RegExp&&e instanceof RegExp)return t.toString()===e.toString();if(t instanceof Map&&e instanceof Map)return t.size===e.size&&Array.from(t.entries()).every(function(t){var r,n,o=(n=2,function(t){if(Array.isArray(t))return t}(r=t)||function(t,e){var r=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=r){var n,o,i,u,a=[],s=!0,c=!1;try{if(i=(r=r.call(t)).next,0===e){if(Object(r)!==r)return;s=!1}else for(;!(s=(n=i.call(r)).done)&&(a.push(n.value),a.length!==e);s=!0);}catch(t){c=!0,o=t}finally{try{if(!s&&null!=r.return&&(u=r.return(),Object(u)!==u))return}finally{if(c)throw o}}return a}}(r,n)||function(t,e){if(t){if("string"==typeof t)return rt(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?rt(t,e):void 0}}(r,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),i=o[0],u=o[1];return e.has(i)&&ot(u,e.get(i))});if(t instanceof Set&&e instanceof Set){if(t.size!==e.size)return!1;var u=Array.from(t),a=Array.from(e);return u.every(function(t){return a.some(function(e){return ot(t,e)})})}if(t instanceof Error&&e instanceof Error)return t.name===e.name&&t.message===e.message;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){var s=t.valueOf(),c=e.valueOf();if(s!==t||c!==e)return ot(s,c)}if(et(t)&&et(e)){var f=Object.keys(t),l=Object.keys(e);return f.length===l.length&&f.every(function(r){return Object.prototype.hasOwnProperty.call(e,r)&&ot(t[r],e[r])})}if(t.constructor!==e.constructor)return!1;var p=Object.keys(t),y=Object.keys(e);return p.length===y.length&&p.every(function(r){return Object.prototype.hasOwnProperty.call(e,r)&&ot(t[r],e[r])})}function it(t){return it="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},it(t)}function ut(t){return function(t){if(Array.isArray(t))return at(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return at(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?at(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function at(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);r<e;r++)n[r]=t[r];return n}function st(t){return[].concat(ut(Object.keys(t)),ut(Object.getOwnPropertySymbols(t).filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})))}function ct(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:new WeakMap;if(null==t)return t;if("object"!==it(t))return t;if(r.has(t))return r.get(t);var n=Array.isArray(t)?[]:{};if(r.set(t,n),Array.isArray(t)){for(var o=0;o<t.length;o+=1)n[o]=ct(t[o],e,r);return n}return st(t).forEach(function(o){if(e){var i=e(void 0,t[o],o,n,t);if(void 0!==i)return void(n[o]=i)}n[o]=ct(t[o],e,r)}),n}function ft(t,e,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:new WeakMap;return n.has(e)?n.get(e):(n.set(e,t),st(e).forEach(function(o){var i=e[o];if(void 0!==i)if(i!==e){if(r){var u=r(t[o],i,o,t,e);if(void 0!==u)return void(t[o]=u)}Array.isArray(i)?t[o]=ct(i,r,n):et(i)?et(t[o])?ft(t[o],i,r,n):t[o]=ct(i,r,n):t[o]=i}else t[o]=t}),t)}function lt(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),n=1;n<e;n++)r[n-1]=arguments[n];var o="function"==typeof r[r.length-1]?r.pop():void 0;return r.length?null==t?ct(r[0],o):(r.forEach(function(e){null!=e&&ft(t,e,o)}),t):t}function pt(t){return pt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},pt(t)}function yt(t,e){Object.keys(t).forEach(function(r){e[r]=t[r]})}function bt(t,e,r){if(null==t)return t;if(null==e)return t;var n=t,o=Array.isArray(e)?e.slice():String(e);if("string"==typeof o&&(o=(o=o.replace(/\[(\w+)\]/g,".$1")).split(".")),1===o.length&&""===o[0])return n[""]=r,n;if(""===o[0]&&o.shift(),0===o.length)return n;if(1===o.length)return n[o[0]]=r,n;for(var i=n,u=o.length-1,a=0;a<u;a+=1){var s=o[a];if(""!==s){var c=o[a+1],f=!Number.isNaN(parseInt(c,10))&&Number.isFinite(Number(c))&&""!==c;if(void 0===i[s])i[s]=f?[]:{};else if(f&&!Array.isArray(i[s])){var l=i[s];i[s]=[],"object"===pt(l)&&null!==l?yt(l,i[s]):i[s][0]=l}else null!==i[s]&&"object"===pt(i[s])||(i[s]=f?[]:{});i=i[s]}else{if(a<u-1&&""===o[a+1])continue;void 0===i[""]&&(i[""]={}),i=i[""]}}return i[o[u]]=r,n}"function"!=typeof structuredClone&&(window.structuredClone=o);var vt=function(t,e,r){return function(n,o){return"function"==typeof e?e(n,o,r):function(e,r){return-1===t.findIndex(function(t){return!ot(Z(e,t),Z(r,t))})}(n,o)}};const ht=function(t,e,r,n,o){if(!(t instanceof W))throw new TypeError("Argument #1 must be an instance of Observable");var i,u,a,s,c=-1!==e.indexOf(" ")?e.split(" "):e;return Array.isArray(c)||(c=[c]),t.pipe((u=vt(structuredClone(c),null,{count:0}),void 0===a&&(a=R),u=null!=u?u:Q,q(function(t,e){var r,n=!0;t.subscribe(H(e,function(t){var o=a(t);!n&&u(r,o)||(n=!1,r=o,e.next(t))}))})),(s=structuredClone(c),i=function(t){return e=t,r={}
5494,s.forEach(function(t){var n=Z(e,t);bt(r,t,n)}),1===s.length?Z(r,s[0]):r;var e,r},q(function(t,e){var r=0;t.subscribe(H(e,function(t){e.next(i.call(undefined,t,r++))}))}))).subscribe(r,n,o)};function dt(t){return dt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},dt(t)}function mt(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,gt(n.key),n)}}function gt(t){var e=function(t){if("object"!=dt(t)||!t)return t;var e=t[Symbol.toPrimitive];if(void 0!==e){var r=e.call(t,"string");if("object"!=dt(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}(t);return"symbol"==dt(e)?e:e+""}var wt=new WeakMap;const St=function(){return t=function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_SAFE_INTEGER,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:6e4;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t);var o={};o.data=u(e),o.properties=new G(o.data),o.events=new X(r,n),wt.set(this,o)},e=[{key:"get",value:function(t,e){return Z(wt.get(this).data,t,e)}},{key:"set",value:function(t,e){var r=wt.get(this),n=u(r.data);return bt(n,t,e),r.data=n,r.properties.next(n),this}},{key:"merge",value:function(t,e){if(!et(e))throw new TypeError("Values must be a plain object. Use set or provide the full context for the property.");
5494if("string"!=typeof t)throw new TypeError("Key must be a string.");var r=wt.get(this),n=u(r.data);return bt(n,t,lt({},Z(n,t),e)),r.data=n,r.properties.next(n),wt.set(this,r),this}},{key:"import",value:function(t){var e=wt.get(this),r=lt({},e.data,t);return e.data=r,e.properties.next(r),wt.set(this,e),this}},{key:"export",value:function(){return wt.get(this).data}},{key:"subscribe",value:function(t,e,r,n){var o,i=wt.get(this),u=!1,a=function(){if(!u){this.unsubscribe=function(){u||(u=!0,o&&"function"==typeof o.unsubscribe&&o.unsubscribe())};for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return e.apply(this,r)}},s=(o=Array.isArray(t)&&-1!==t.findIndex(function(t){return-1!==t.indexOf("event")})||-1!==t.indexOf("event")?function(t,e,r,n,o){if(!(t instanceof W))throw new TypeError("Argument #1 must be an instance of Observable");var i=-1!==e.indexOf(" ")?e.split(" "):e;Array.isArray(i)||(i=[i]);var u,a=i.filter(function(t){return-1!==t.indexOf("event")});if(-1!==a.indexOf("event"))return t.subscribe(r,n,o);if(a.length!==i.length)throw new RangeError("All key must be welformed, one or more malformed keys was provided.");return a=a.map(function(t){return t.split(":").pop()}),t.pipe((u=function(t){return-1!==a.indexOf(t.key)},q(function(t,e){var r=0;t.subscribe(H(e,function(t){return u.call(void 0,t,r++)&&e.next(t)}))}))).subscribe(r,n,o)}(i.events,t,a,r,n):ht(i.properties,t,a,r,n)).unsubscribe;return o.unsubscribe=function(){return u=!0,s.call(this)},o}},{key:"trigger",value:function(t,e,r){var n={key:t,data:e,event:r};return wt.get(this).events.next(n),this}}],e&&mt(t.prototype,e),Object.defineProperty(t,"prototype",{writable:!1}),t;var t,e}();return e.default})()); 5495//# sourceMappingURL=anf-databridge.umd.js.map; 5496redesign.define('coreServices/adapter/bags/items',[ 5497 'api/Classes/Adapter', 5498 'api/Classes/AdapterError', 5499 'anfUtil/testFramework', 5500 'anfDataBridge' 5501], function items(APIAdapter, AdapterError, test, DataBridge) { 5502 'use strict'; 5503 5504 var adapter = new APIAdapter(); 5505 5506 var errorMessages = { 5507 SHORT_SKU_REQUIRED: 'no shortSku was provided', 5508 ORDER_ITEM_ID_REQUIRED: 'no orderItemId was provided' 5509 }; 5510 5511 function pushBingUetqToWindow(aProductItemArr) { 5512 window.digitalData = window.digitalData || new DataBridge(); 5513 window.uetq = window.uetq || []; 5514 var pageType = window.digitalData.get('page.pageKind'); 5515 5516 // TODO: clean up when tealium is incorporated on Picked For You page 5517 if (pageType === 'pickedForYou') { 5518 pageType = 'other'; 5519 } 5520 5521 window.uetq.push({ 5522 'prodid': aProductItemArr[aProductItemArr.length - 1].shortSku, 5523 'pagetype': pageType === 'shopping cart' ? 'cart' : pageType 5524 }); 5525 } 5526 5527 /** 5528 * @property {object} CONSTANTS 5529 */ 5530 adapter.CONSTANTS = { 5531 }; 5532 5533 /** 5534 * @name create 5535 * @description Function to sit in front of the api call so we can add some logic if we are going to make the call or not 5536 * @param {object} [settings] - the settings to be supplied to the function 5537 * @param {string} [settings.shortSku] - REQUIRED, the sku of the item to add to bag 5538 * @param {integer} [settings.quantity] - the quantity of provided sku user wants to add to bag, defaults to 1 5539 * @param {boolean} [settings.checkInventory] - should the API check if there is inventory for this sku, defaults to false 5540 * @return {promise} this is a jquery promise that will be resolved/rejected with an object c
5540ontaining reason code and a combination of ajaxData, ajaxJqXHR, ajaxTextStatus, and ajaxErrorThrown depending on the result of the ajax call 5541 */ 5542 adapter.configCreate({ 5543 endPoint: ANF.params.storeAttributes.apiURLRootBags + '/cart/items', 5544 method: 'POST', 5545 validation: function validation(data, context) { 5546 var hasSku = test.object.containsKey(data, 'shortSku', false); 5547 5548 if (!hasSku || test.string.empty(data.shortSku, true)) { 5549 throw new AdapterError(context.REASON.INVALID_PARAMS, errorMessages.SHORT_SKU_REQUIRED); 5550 } 5551 }, 5552 preAjax: function preAjax(data) { 5553 return JSON.stringify(data); 5554 }, 5555 done: function done(data, textStatus, jqXHR) { 5556 /** 5557 * @this CRUD 5558 */ 5559 var response; 5560 if (data.messages && data.messages.length > 0) { 5561 response = { 5562 data: { 5563 error: [ 5564 { 5565 errorMessage: data.messages[0].message 5566 } 5567 ] 5568 } 5569 }; 5570 5571 this.deferred.reject(response); 5572 } else { 5573 pushBingUetqToWindow(data.items); 5574 response = this.getAjaxOkResponse(data, textStatus, jqXHR); 5575 this.deferred.resolve(response); 5576 } 5577 } 5578 }); 5579 5580 /** 5581 * @name update 5582 * @description Function to sit in front of the api call so we can add some logic if we are going to make the call or not 5583 * @param {object} [settings] - the settings to be supplied to the function 5584 * @param {string} [settings.shortSku] - REQUIRED, the sku of the item to add to bag 5585 * @param {string} [settings.orderItemId] - REQUIRED, the orderItemId that the user wants to update, will be used in the endpoint 5586 * @param {boolean} [settings.checkInventory] - should the API check if there is inventory for this sku 5587 * @return {promise} this is a jquery promise that will be resolved/rejected with an object c
5587ontaining reason code and a combination of ajaxData, ajaxJqXHR, ajaxTextStatus, and ajaxErrorThrown depending on the result of the ajax call 5588 */ 5589 adapter.configUpdate({ 5590 endPoint: ANF.params.storeAttributes.apiURLRootBags + '/cart/items/<orderItemId>', 5591 method: 'PUT', 5592 validation: function validation(data, context) { 5593 var hasSku = test.object.containsKey(data, 'shortSku', false); 5594 var hasOrderItemId = test.object.containsKey(data, 'orderItemId', false); 5595 5596 if (!hasSku || test.string.empty(data.shortSku, true)) { 5597 throw new AdapterError(context.REASON.INVALID_PARAMS, errorMessages.SHORT_SKU_REQUIRED); 5598 } 5599 if (!hasOrderItemId || test.string.empty(data.orderItemId, true)) { 5600 throw new AdapterError(context.REASON.INVALID_PARAMS, errorMessages.ORDER_ITEM_ID_REQUIRED); 5601 } 5602 }, 5603 preAjax: function preAjax(data) { 5604 return JSON.stringify(data); 5605 }, 5606 done: function done(data, textStatus, jqXHR) { 5607 /** 5608 * @this CRUD 5609 */ 5610 var response; 5611 if (data.messages && data.messages.length > 0) { 5612 response = { 5613 data: { 5614 error: [ 5615 { 5616 errorMessage: data.messages[0].message 5617 } 5618 ] 5619 } 5620 }; 5621 5622 this.deferred.reject(response); 5623 } else { 5624 pushBingUetqToWindow(data.items); 5625 response = this.getAjaxOkResponse(data, textStatus, jqXHR); 5626 this.deferred.resolve(response); 5627 } 5628 } 5629 }); 5630 5631 Object.freeze(adapter.CONSTANTS); 5632 5633 return adapter; 5634}); 5635 5636redesign.define('globalModulesHelpers/freeShippingProgress',[], function freeShippingProgress() { 5637 'use strict'; 5638 5639 var publicMembers = { 5640 'freeShippingMessageEnabled': ANF.params.storeAttributes.enableFreeShippingMessaging, 5641 'getFreeShippingMessageRange': function getFreeShippingMessageRange() { 5642 return ANF.params.storeAttributes.freeShippingMsgRange; 5643 }, 5644 'getFreeShippingPreLabel': function getFreeShippingPreLabel(cartModel) { 5645 return cartModel.amountToFreeShipping > 0 ? this.getShippingThresholdFmtValue(cartModel.freeShippingThreshold.minShippingThresholdFmt) : ''; 5646 }, 5647 'getFreeShippingPostLabel': function getFreeShippingPostLabel(cartModel) { 5648 return cartModel.amountToFreeShipping > 0 ? this.getShippingThresholdFmtValue(cartModel.freeShippingThreshold.freeShippingThresholdFmt) : ''; 5649 }, 5650 'getShippingThresholdFmtValue': function getShippingThresholdFmt(value) { 5651 return (value !== null) ? value.slice(0, -3) : value; 5652 }, 5653 'getFreeShippingProgressValue': function getFreeShippingProgressValue(amountToFreeShipping, freeShippingProgressMax) { 5654 return freeShippingProgressMax - amountToFreeShipping; 5655 }, 5656 'shouldDisplayFreeShippingProgress': function shouldDisplayFreeShippingProgress(testGroup, amountToFreeShipping, freeShippingProgressMax) { 5657 var isValidAmountToFreeShipping = !isNaN(Number(amountToFreeShipping)); 5658 if (freeShippingProgressMax !== null) { 5659 return this.freeShippingMessageEnabled && isValidAmountToFreeShipping && ((testGroup === 1 && amountToFreeShipping < publicMembers.getFreeShippingMessageRange()) || testGroup === 2); 5660 } 5661 return false; 5662 }, 5663 'getDsModel': function getDsModel(cartModel, testGroup) { 5664 var freeShippingProgressMax = cartModel.freeShippingThreshold && cartModel.freeShippingThreshold.freeShippingThreshold; 5665 return { 5666 'isVisible': this.shouldDisplayFreeShippingProgress(testGroup, cartModel.amountToFreeShipping, freeShippingProgressMax), 5667 'amountToFreeShipping': cartModel.amountToFreeShipping, 5668 'amountToFreeShippingFmt': cartModel.amountToFreeShippingFmt, 5669 'preLabel': this.getFreeShippingPreLabel(cartModel), 5670 'postLabel': this.getFreeShippingPostLabel(cartModel), 5671 'value': this.getFreeShippingProgressValue(cartModel.amountToFreeShipping, freeShippingProgressMax), 5672 'max': freeShippingProgressMax 5673 }; 5674 } 5675 }; 5676 5677 return publicMembers; 5678}); 5679 5680 5681redesign.define('checkoutServices/common/redirect',[ 5682 'coreServices/window' 5683], function redirect(windowService) { 5684 'use strict'; 5685 5686 var privateMembers = {
5687 'urlParams': { 5688 'storeId': ANF.params.storeAttributes.idList.storeId, 5689 'catalogId': ANF.params.storeAttributes.idList.catalogId, 5690 'langId': ANF.params.storeAttributes.idList.langId 5691 } 5692 }; 5693 5694 return { 5695 'buildRedirectURL': function buildRedirectURL(command, additionalParams) { 5696 var parameters = null; 5697 var queryString = null; 5698 var redirectUrl = null; 5699 var baseParameters = privateMembers.urlParams; 5700 var hasCommand = (typeof command === 'string' && command.length > 0) ? true : false; 5701 var hasAdditionalParameters = (typeof additionalParams === 'object' && additionalParams !== null) ? true : false; 5702 5703 if (hasCommand) { 5704 if (hasAdditionalParameters) { 5705 parameters = $.extend({}, baseParameters, additionalParams); 5706 } else { 5707 parameters = baseParameters; 5708 } 5709 5710 queryString = $.param(parameters); 5711 redirectUrl = command + '?' + queryString; 5712 } else { 5713 redirectUrl = ''; 5714 } 5715 5716 return redirectUrl; 5717 }, 5718 'setRedirectLocation': windowService.location.set 5719 }; 5720}); 5721 5722redesign.define('coreModules/checkout/utils',['checkoutServices/common/redirect'], function utils(redirectService) { 5723 'use strict'; 5724 5725 var publicMembers = { 5726 'getCheckoutURL': function getCheckoutURL() { 5727 var additionalParams = { 5728 'doInventory': 'N', 5729 'InvRepuCheckMode': 'checkout', 5730 'URL': 'OrderCheckoutDisplayView', 5731 'removeSoldOutIems': 'true', 5732 'errpage': 'OrderItemDisplayView', 5733 'checkoutMode': 'webCheckout' 5734 }; 5735 return redirectService.buildRedirectURL('Checkout', additionalParams); 5736 }, 5737 5738 'buildCheckoutButton': function buildCheckoutButton() { 5739 var hasMiniBagCheckoutButton = ANF.params.storeAttributes.hasMiniBagCheckoutButton; 5740 var hasESWFeatureEnabled = ANF.params.storeAttributes.shoppingBagV2.hasESWFeatureEnabled; 5741 var showCheckoutButton = hasMiniBagCheckoutButton && !hasESWFeatureEnabled; 5742 var showCheckoutButtonClass = (showCheckoutButton) ? 'show' : ''; 5743 return { 5744 'props': { 5745 'showCheckoutButton': showCheckoutButton, 5746 'showCheckoutButtonClass': showCheckoutButtonClass, 5747 'href': ANF.params.urls.checkout.checkoutUrl 5748 } 5749 }; 5750 } 5751 }; 5752 5753 return publicMembers; 5754}); 5755 5756redesign.define('anfcore/components/progressBar', ['exports', '../helpers/makeSelector'], function (exports, _makeSelector) { 5757 'use strict'; 5758 5759 Object.defineProperty(exports, "__esModule", { 5760 value: true 5761 }); 5762 5763 var _makeSelector2 = _interopRequireDefault(_makeSelector); 5764 5765 function _interopRequireDefault(obj) { 5766 return obj && obj.__esModule ? obj : { 5767 default: obj 5768 }; 5769 } 5770 5771 function _toConsumableArray(arr) { 5772 if (Array.isArray(arr)) { 5773 for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { 5774 arr2[i] = arr[i]; 5775 } 5776 5777 return arr2; 5778 } else { 5779 return Array.from(arr); 5780 } 5781 } 5782 5783 // This is a wrapper around querySelectorAll 5784 var components = (0, _makeSelector2['default'])('.js-progress-bar'); 5785 5786 exports['default'] = function () { 5787 return { 5788 components: components, 5789 init: function () { 5790 function init() { 5791 [].concat(_toConsumableArray(components())).forEach(function (element) { 5792 var progressElement = element.querySelector('.js-progress-element'); 5793 5794 if (!progressElement) return; 5795 5796 var value = progressElement.dataset.value; 5797 var max = progressElement.max; 5798 5799 var animationInterval = setInterval(function () { 5800 if (progressElement.value < value) { 5801 progressElement.setAttribute('value', Math.ceil(progressElement.value + max / 100)); 5802 } else { 5803 clearInterval(animationInterval); 5804 } 5805 }, 1); 5806 }); 5807 return this; 5808 } 5809 5810 return init; 5811 }() 5812 }; 5813 }; 5814}); 5815redesign.define('productModules/helpers/productAddToBagHelper',[ 5816 'coreServices/adapter/bags/items', 5817 'globalServices/startGuestSession', 5818 'anfcore/components/statefulButton', 5819 'coreServices/dataBridge/cart', 5820 'productServices/product/template', 5821 'productModules/helpers/Utils',
5822 'productServices/product/headerHeightCalculator', 5823 'globalModulesHelpers/freeShippingProgress', 5824 'coreModules/checkout/utils', 5825 'util/analytics/enum/dataPoint', 5826 'util/analytics/enum/subscriptionName', 5827 'anfcore/components/progressBar' 5828], function productAddToBagHelper( 5829 bagsItemsApi, 5830 guestSession, 5831 statefulButtonComponent, 5832 cartDataBridgeService, 5833 templateService, 5834 Utils, 5835 headerHeightCalculator, 5836 freeShippingProgressHelper, 5837 checkoutUtils, 5838 analyticsDataPoints, 5839 analyticsSubscriptionNames, 5840 dsProgressBar 5841) { 5842 'use strict'; 5843 var minATBProcessingTime = 1500; 5844 var atbButtonSelector = '.pdp-atb-button'; 5845 var errorContainerSelector = '.validator-container'; 5846 var sizePrimaryNameSelector = '.product-sizes__name__label_primary'; 5847 var sizeSecondaryNameSelector = '.product-sizes__name__label_secondary'; 5848 var messageBus = $(ANF); 5849 var hideMiniBagTimeout = ANF.params.storeAttributes.hideMiniBagTimeoutCounter; 5850 var testGroup = ANF.params.storeAttributes.checkout.freeShippingProgress; 5851 var sizePrimaryQv = 'size-primary_qv'; 5852 var sizeSecondaryQv = 'size-secondary_qv'; 5853 var sizePrimary = 'size-primary'; 5854 var sizeSecondary = 'size-secondary'; 5855 5856 // have to bind utils here bc utils.getvalue uses 'this' 5857 var getInANF = Utils.getValueFromObject.bind(Utils, ANF); 5858 5859 5860 function addToBagTimeout() { 5861 var minProcessTimePromise = new $.Deferred(); 5862 setTimeout(function waitMinProcessingTime() { 5863 minProcessTimePromise.resolve(); 5864 }, minATBProcessingTime); 5865 return minProcessTimePromise; 5866 } 5867 5868 function triggerAddToBagSuccessEvents(data) { 5869 var formData = data.formData; 5870 var cart = data.cart; 5871 var isEdit = data.isEdit; 5872 5873 // mySaves needs this trigger to update icons on items that may have been saved that were added 5874 messageBus.trigger('productAddToBag:productAddedToBag', formData.productId); 5875 5876 if (!isEdit) { 5877 var orEmptyObject = function orEmptyObjectFn(value) { 5878 return value || {}; 5879 }; 5880 5881 var orEmptyArray = function orEmptyArrayFn(value) { 5882 return value || []; 5883 }; 5884 5885 var productDetail = orEmptyObject( 5886 orEmptyObject( 5887 productCatalog 5888 )[formData.productId] 5889 ); 5890 5891 var kicAdded = formData.kic_qv || formData.kic_tray || formData.kic; 5892 5893 var matchedItem = orEmptyObject( 5894 orEmptyArray(orEmptyObject(cart).items) 5895 .filter(function filterNonMatching(item) { 5896 return ( 5897 item.item 5898 && item.item.shortSku === formData.sku 5899 ); 5900 }).pop() 5901 ); 5902 5903 var eventData = {}; 5904 eventData[analyticsDataPoints.PRODUCT_COLLECTION] = formData.collection; 5905 eventData[analyticsDataPoints.QUANTITY] = formData.quantity; 5906 eventData[analyticsDataPoints.KEY_ITEM_COLOR] = kicAdded; 5907 eventData[analyticsDataPoints.PRICE_LIST_LOCAL] = matchedItem.item.listPrice; 5908 eventData[analyticsDataPoints.PRICE_LIST_USD] = matchedItem.item.listPriceUSD; 5909 eventData[analyticsDataPoints.PRICE_OFFER_LOCAL] = matchedItem.item.offerPrice; 5910 eventData[analyticsDataPoints.PRICE_OFFER_USD] = matchedItem.item.offerPriceUSD; 5911 eventData[analyticsDataPoints.PRODUCT_ID] = matchedItem.productId; 5912 eventData[analyticsDataPoints.SHORT_SKU] = matchedItem.item.shortSku; 5913 eventData[analyticsDataPoints.PRICE_FLAG] = productDetail.priceFlag; 5914 eventData[analyticsDataPoints.LONG_SKU] = matchedItem.item.longSku; 5915 eventData[analyticsDataPoints.PRODUCT_NAME] = matchedItem.productName; 5916 5917 window.digitalData.trigger(analyticsSubscriptionNames.ACTION.ADD_TO_BAG, eventData); 5918 } 5919 5920 try { 5921 cartDataBridgeService.add({
5922 kic: Utils.getProductData(formData.productId).kicId, 5923 shortSku: formData.sku, 5924 quantity: formData.quantity 5925 }); 5926 } catch (err) { 5927 // EPIC-8729: looking at old ATB code, we apparently dont need to handle anything here on error 5928 // err.message 5929 } 5930 } 5931 5932 function hideError($form) { 5933 $form.find(errorContainerSelector).fadeOut(100, 'linear'); 5934 } 5935 5936 function centerError($errorContainer) { 5937 var viewPortMiddle = $(window).height() / 2; 5938 var errorContainerTop = $errorContainer.offset().top; 5939 5940 $('html,body').animate({ 5941 scrollTop: errorContainerTop - viewPortMiddle 5942 }); 5943 } 5944 5945 function unselectedSizeErrorMsg(formData, $form) { 5946 var message = publicMembers.constants.selectSizeError; 5947 5948 var primarySizeUnselected = !formData[sizePrimary] && !formData[sizePrimaryQv]; 5949 var secondarySizeUnselected = !formData[sizeSecondary] && !formData[sizeSecondaryQv]; 5950 5951 var replaceParamWithText = function replaceParamWithText(text) { 5952 return publicMembers.constants.selectError.replace('{0}', text); 5953 }; 5954 if (primarySizeUnselected && !secondarySizeUnselected) { 5955 message = replaceParamWithText($form.find(sizePrimaryNameSelector).text().split(':')[0]); 5956 } else if (!primarySizeUnselected && secondarySizeUnselected) { 5957 message = replaceParamWithText($form.find(sizeSecondaryNameSelector).text().split(':')[0]); 5958 } 5959 return message; 5960 } 5961 5962 var publicMembers = { 5963 constants: { 5964 atbError: getInANF('messaging.catalogV2.PDP_ATB_ERROR', ''), 5965 guestSessionError: getInANF('messaging.catalogV2.GLB_GUEST_SESSION_ERROR', ''), 5966 selectSizeError: getInANF('messaging.catalogV2.selectSizeError', ''), 5967 selectError: getInANF('messaging.catalogV2.SELECT_KEY_SIZE', '') 5968 }, 5969 5970 handleMFEAddToBagSuccess: function handleMFEAddToBagSuccess(event) { 5971 var data = event.detail; 5972 var eventData = {}; 5973 eventData[analyticsDataPoints.BRAND_VIEWING] = data.brandViewing; 5974 eventData[analyticsDataPoints.CART_UNITS_ADDED] = data.cartUnitsAdded; 5975 eventData[analyticsDataPoints.ORDER_SUBTOTAL] = data.orderSubtotal; 5976 eventData[analyticsDataPoints.ORDER_SUBTOTAL_USD] = data.orderSubtotal; 5977 eventData[analyticsDataPoints.COLLECTION_ID] = data.orderSubtotal; 5978 eventData[analyticsDataPoints.KEY_ITEM_COLOR] = data.orderSubtotal; 5979 eventData[analyticsDataPoints.LIST_PRICE] = data.orderSubtotal; 5980 eventData[analyticsDataPoints.LIST_PRICE_USD] = data.orderSubtotal; 5981 eventData[analyticsDataPoints.PAGE_TYPE] = data.orderSubtotal; 5982 eventData[analyticsDataPoints.PRICE_FLAG] = data.orderSubtotal; 5983 eventData[analyticsDataPoints.PRODUCT_LONG_SKU] = data.orderSubtotal; 5984 eventData[analyticsDataPoints.MERCHANDISING_CATEGORY_ID] = data.orderSubtotal; 5985 window.digitalData.trigger(analyticsSubscriptionNames.ACTION.ADD_TO_BAG, eventData); 5986 }, 5987 5988 addProductToBag: function addProductToBag($form, options) { 5989 var myOptions = options || {}; 5990 5991 hideError($form); 5992 var response = new $.Deferred(); 5993 var formData = publicMembers.getSerializedFormData($form); 5994 var requestData = publicMembers.buildRequestData(formData, myOptions.orderItemId); 5995 var isEdit = !!myOptions.orderItemId; 5996 5997 5998 if (requestData.shortSku) { 5999 var $buttons = $form.find(atbButtonSelector); 6000 publicMembers.setATBButtonState($buttons, 'processing'); 6001 guestSession.deferredInit().done(function guestSessionSuccess() { 6002 var minProcessTimePromise = addToBagTimeout(); 6003 6004 var cartItemsCall; 6005 if (!myOptions.orderItemId) { 6006 cartItemsCall = bagsItemsApi.create(requestData); 6007 } else { 6008 cartItemsCall = bagsItemsApi.update(requestData); 6009 } 6010 6011 $.when(cartItemsCall, minProcessTimePromise) 6012 .done(function addToBagSuccess(responseData) { 6013 response.resolve(responseData); 6014 triggerAddToBagSuccessEvents({ 6015 $form: $form, 6016 cart: responseData.data, 6017 formData: formData, 6018 isEdit: isEdit, 6019 src: myOptions.src 6020 }); 6021 }) 6022 .fail(function addToBagFail(error) { 6023 var errorMessage = publicMembers.constants.atbError; 6024 var hasAPIErrorMessage = error && error.data && error.data.error && error.data.error.length; 6025 6026 if (hasAPIErrorMessage) { 6027 errorMessage = error.data.error[0].errorMessage; 6028 } 6029 6030 response.reject(errorMessage); 6031 }); 6032 }).fail(function guestSessionFail() { 6033 response.reject(publicMembers.constants.guestSessionError); 6034 }); 6035 } else { 6036 response.reject(unselectedSizeErrorMsg(formData, $form)); 6037 } 6038 6039 return response.promise(); 6040 }, 6041 6042 getSerializedFormData: function getSerializedFormData($form) { 6043 var formData = $form.serializeArray(); 6044 formData = formData.reduce(function reduceFormDataArray(acc, item) { 6045 acc[item.name] = item.value; 6046 return acc; 6047 }, {}); 6048 6049 return formData; 6050 }, 6051 6052 buildRequestData: function buildRequestData(formData, orderItemId) { 6053 var data = { 6054 shortSku: formData.sku, 6055 checkInventory: false 6056 }; 6057 6058 if (orderItemId) { 6059 data.orderItemId = orderItemId; 6060 } else { 6061 data.quantity = parseInt(formData.quantity, 10); 6062 } 6063 6064 return data; 6065 }, 6066 6067 hasQualifiedSku: function hasQualifiedSku($form) { 6068 return publicMembers.getSerializedFormData($form).sku; 6069 }, 6070 6071 setATBButtonState: function setATBButtonState($buttons, state) { 6072 $buttons.each(function setEachButtonState(index, button) { 6073 publicMembers.statefulButton.changeState({ 6074 button: button, 6075 state: state 6076 }); 6077 }); 6078 }, 6079 6080 addToBagFail: function addToBagFail($form, errorMessage) { 6081 var $buttons = $form.find(atbButtonSelector);
6082 var errorHTML = templateService.renderTemplate('errorMessage', { 6083 'error-message-text': errorMessage 6084 }); 6085 var $errorContainer = $form.find(errorContainerSelector); 6086 $errorContainer 6087 .html(errorHTML) 6088 .fadeIn(100, 'linear'); 6089 6090 if (ANF.breakpoints.screen === 'small_phone' || ANF.breakpoints.screen === 'phone') { 6091 centerError($errorContainer); 6092 } 6093 6094 publicMembers.setATBButtonState($buttons, 'initial'); 6095 6096 $(window).trigger('analytics.errorTracking', { 6097 type: 'add to bag', 6098 message: errorMessage 6099 }); 6100 }, 6101 6102 displayNotification: function displayNotification(cartModel) { 6103 var pasId = ANF.params.storeAttributes.pasId; 6104 var $atbNotification = $(templateService.renderTemplate('addToBagNotification', { 6105 cartUrl: ANF.params.urls.checkout.cartURL, 6106 freeShippingMessage: freeShippingProgressHelper.getDsModel(cartModel, testGroup), 6107 checkoutButton: checkoutUtils.buildCheckoutButton() 6108 })); 6109 var positionY = headerHeightCalculator.getCurrentHeaderHeight(pasId); 6110 6111 $('body').prepend($atbNotification); 6112 $(window).one('dragup scroll', function hideNotification() { 6113 $atbNotification.remove(); 6114 }); 6115 $atbNotification 6116 .addClass('visible') 6117 .css('transform', 'translateY(' + positionY + 'px)'); 6118 dsProgressBar.default().init(); 6119 setTimeout(function successTimeout() { 6120 $atbNotification.remove(); 6121 }, hideMiniBagTimeout); 6122 }, 6123 6124 statefulButton: statefulButtonComponent.default() 6125 }; 6126 return publicMembers; 6127}); 6128 6129redesign.define('productModules/product/addToBag/pdpAddToBag',[ 6130 'productModules/helpers/productAddToBagHelper', 6131 'util/analytics/enum/pageType' 6132], function pdpAddToBag( 6133 addToBagHelper, 6134 analyticsPageType 6135) { 6136 'use strict'; 6137 6138 var messageBus = $(ANF); 6139 var successStateTime = 3000; 6140 var pdpAddToBagButtonSelector = '.js-product-page .product-page__atb-saves-wrapper .pdp-atb-button'; 6141 var addToBagFormSelector = '.product-page-add-to-bag__container'; 6142 var atbButtonSelector = '.pdp-atb-button'; 6143 var selectedSwatchSelector = '.product-swatches :checked'; 6144 6145 function bindEvents() { 6146 $(document).on('click', pdpAddToBagButtonSelector, addToBagHandler); 6147 } 6148 6149 function listen() { 6150 messageBus.on('initiateAddToBag:sticky', stickyAddToBagHandler); 6151 } 6152 6153 function addToBagHandler(e) { 6154 e.preventDefault(); 6155 addToBag($(e.target).closest(addToBagFormSelector), 'analytics.addToBagInline'); 6156 } 6157 6158 function stickyAddToBagHandler(e, data) { 6159 addToBag(data.$form, 'analytics.addToBagSticky'); 6160 } 6161 6162 function addToBag($form, analyticsATBType) { 6163 addToBagHelper.addProductToBag($form, { src: analyticsPageType.PDP }) 6164 .done(function success(data) { 6165 addToBagSuccess(data, $form, analyticsATBType); 6166 }) 6167 .fail(function fail(errorMessage) { 6168 addToBagHelper.addToBagFail($form, errorMessage); 6169 }); 6170 } 6171 6172 function addToBagSuccess(data, $form, analyticsATBType) { 6173 var kicId = $form.find(selectedSwatchSelector).val(); 6174 setButtonSuccess($form); 6175 if (ANF.breakpoints.screen === 'small_phone' || 6176 ANF.breakpoints.screen === 'phone' || 6177 ANF.breakpoints.screen === 'tablet' || 6178 ANF.breakpoints.screen === 'small_desktop' || 6179 ANF.breakpoints.screen === 'desktop') { 6180 addToBagHelper.displayNotification(data.data); 6181 } 6182 // this shows the minibag with item added 6183 messageBus.trigger('miniBag:productAddedToCart', data); 6184 6185 $(ANF).trigger('styliticsV3Item:addToBag', {kicId: kicId}); 6186 $(window).trigger('analytics.addToBagStandard'); // deprecated 6187 $(window).trigger(analyticsATBType); 6188 } 6189 6190 function setButtonSuccess($form) { 6191 var $buttons = $form.find(atbButtonSelector); 6192 addToBagHelper.setATBButtonState($buttons, 'success'); 6193 setTimeout(function successTimeout() { 6194 addToBagHelper.setATBButtonState($buttons, 'initial'); 6195 }, successStateTime); 6196 } 6197 6198 var publicMembers = { 6199 listen: listen, 6200 bindEvents: bindEvents 6201 }; 6202 return publicMembers; 6203}); 6204
6205redesign.define('productModules/helpers/curry',[], function Curry() { 6206 'use strict'; 6207 6208 return function curry(fn, arity) { 6209 var ar = arity || fn.length; 6210 6211 return (function nextCurried(prevArgs) { 6212 return function curried(nextArg) { 6213 var args = prevArgs.concat(nextArg); 6214 6215 if (args.length >= ar) { 6216 return fn.apply(null, args); 6217 } 6218 6219 return nextCurried(args); 6220 }; 6221 })( [] ); 6222 }; 6223}); 6224 6225redesign.define('productModules/product/addToBag/quickviewAddToBag',[ 6226 'productModules/helpers/Utils', 6227 'productModules/helpers/productAddToBagHelper', 6228 'productModules/helpers/curry', 6229 'anfcore/components/statefulButton', 6230 'anfcore/components/modal', 6231 'coreServices/window' 6232], function QuickviewAddToBag( 6233 Utils, 6234 addToBagHelper, 6235 curry, 6236 statefulButtonComponent, 6237 modalComponent, 6238 windowService 6239) { 6240 'use strict'; 6241 6242 var addToBagButtonSelector = '.pdp-atb-button'; 6243 var qvAddToBagButtonSelector = '.js-quickview .pdp-atb-button'; 6244 var addToBagFormSelector = '.product-page-add-to-bag__container'; 6245 var editModalSelector = '.product-quickview__modal.edit-product'; 6246 var checkoutCartSelector = '#checkout-cart'; 6247 var quickviewModalId = 'product-quickview__modal'; 6248 var quickviewProductInfoContainerSelectorClass = '.product-quickview__modal[data-open="true"] .product-page__info-container'; 6249 var quickviewErrorClass = 'qv-error'; 6250 var selectedSwatchSelector = '.product-swatches :checked'; 6251 var styliticsModalClass = '.stylitics-quickview'; 6252 6253 var successStateTime = 1000; 6254 6255 function addToBag(e) { 6256 e.preventDefault(); 6257 var $form = $(e.currentTarget).closest(addToBagFormSelector); 6258 var isStylitics = $form.closest(styliticsModalClass).length; 6259 var orderItemId = $form.closest(editModalSelector).attr('data-orderitemid'); 6260 var isEditItem = Boolean(orderItemId); 6261 var atbSuccess = curriedSuccess($form)(isEditItem)(isStylitics); 6262 var atbFail = curriedFail($form); 6263 addToBagHelper.addProductToBag($form, {orderItemId: orderItemId}) 6264 .done(atbSuccess) 6265 .fail(atbFail); 6266 } 6267 6268 var curriedSuccess = curry(addToBagSuccess); 6269 function addToBagSuccess($form, isEdit, isStylitics, data) { 6270 var button = $form.find(addToBagButtonSelector)[0]; 6271 var isShoppingCart = $('body').is(checkoutCartSelector); 6272 var kicId = $form.find(selectedSwatchSelector).val(); 6273 6274 publicMembers.statefulButton.changeState({ 6275 button: button, 6276 state: isEdit ? 'updateSuccess' : 'success' 6277 }); 6278 6279 setTimeout(function successTimeout() { 6280 publicMembers.modal.closeModal(quickviewModalId); 6281 if (isShoppingCart) { 6282 windowService.location.reload(); 6283 } 6284 if (!isShoppingCart) { 6285 // this shows the minibag with item added 6286 var customEvent = new Event('miniBag:productAddedToCart'); 6287 customEvent.detail = { 6288 data: data 6289 }; 6290 window.dispatchEvent(customEvent); 6291 6292 if (ANF.breakpoints.screen === 'small_phone' || ANF.breakpoints.screen === 'phone' || ANF.breakpoints.screen === 'tablet' || ANF.breakpoints.screen === 'small_desktop' || ANF.breakpoints.screen === 'desktop') { 6293 addToBagHelper.displayNotification(data.data); 6294 } 6295 } 6296 }, successStateTime); 6297 6298 window.dispatchEvent(new CustomEvent('addToBag:perso-instrumentation', { 6299 detail: { 6300 kicId: kicId 6301 } 6302 })); 6303 6304 if (isStylitics) { 6305 if (Utils.hasPDPMFEBelowTheFold()) { 6306 var QvFromStyliticsV3ATBEvent = new CustomEvent('styliticsV3Item:addToBagForMFE', { 6307 detail: { 6308 kicId: kicId 6309 } 6310 }); 6311 window.dispatchEvent(QvFromStyliticsV3ATBEvent); 6312 } else { 6313 $(ANF).trigger('styliticsV3Item:addToBag', {kicId: kicId}); 6314 } 6315 } 6316 $(window).trigger('analytics.addToBagStandard').trigger('analytics.addToBagInline'); 6317 } 6318 6319 var curriedFail = curry(addToBagFail); 6320 function addToBagFail($form, errorMessage) { 6321 $(quickviewProductInfoContainerSelectorClass).addClass(quickviewErrorClass); 6322 addToBagHelper.addToBagFail($form, errorMessage); 6323 } 6324 6325 var publicMembers = { 6326 bindEvents: function bindEvents() { 6327 $(document).on('click', qvAddToBagButtonSelector, addToBag); 6328 }, 6329 statefulButton: statefulButtonComponent.default(), 6330 modal: modalComponent.default() 6331 }; 6332 6333 return publicMembers; 6334}); 6335 6336redesign.define('productModules/helpers/hyperlinkShortDescriptorsSplitTest',[], 6337 function hyperlinkShortDescriptorsSplitTest() { 6338 'use strict'; 6339 6340 var list1 = { 6341 anf: { 6342 'Men\'s': { 6343 list: { 6344 'best dressed guest collection': '/shop/us/mens-best-dressed-guest-weddings-and-events', 6345 'a&f x trevor project': '/shop/us/pride', 6346 'gender inclusive': '/shop/us/lookbook-a-and-f-lookbook', 6347 'trend edit': '/shop/us/search?filtered=true&rows=90&searchTerm=Trend+Edit&start=0&departmentId=12202-ANF', 6348 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=Online+Exclusive&start=0&departmentId=12202-ANF', 6349 'matching set': '/shop/us/mens-coordinated-sets', 6350 'getaway shop': '#', 6351 'vol. 28': '#', 6352 'golf shop': '#', 6353 'for justice collection': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=For+Justice+Collection' 6354 } 6355 }, 6356 'Women\'s': { 6357 list: { 6358 'best dressed guest collection': '/shop/us/womens-best-dressed-guest-weddings-and-events', 6359 'a&f x trevor project': '/shop/us/pride', 6360 'gender inclusive': '/shop/us/lookbook-a-and-f-lookbook', 6361 'trend edit': '/shop/us/search?filtered=true&rows=90&searchTerm=Trend+Edit&start=0&departmentId=12203-ANF', 6362 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=Online+Exclusive&start=0&departmentId=12203-ANF', 6363 'matching set': '/shop/us/womens-matching-sets-dresses-and-rompers', 6364 'getaway shop': '#', 6365 'vol. 28': '#', 6366 'premium footwear collection': '/shop/us/search?departmentCategoryId=12203-ANF&searchTerm=premium+footwear+collection', 6367 'abercrombie x tia booth': '#', 6368 'for justice collection': '/shop/us/search?filtered=true&rows=90&searchTerm=For+Justice+Collection&start=0&departmentId=12203-ANF' 6369 } 6370 } 6371 }, 6372 kids: { 6373 boys: { 6374 list: { 6375 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=179205-KIDS', 6376 'matching set': '/shop/us/kids/boys-matching-sets', 6377 'the cool stuff': '/shop/us/search?filtered=true&rows=90&searchTerm=THE+COOL+STUFF&start=0&departmentId=179205-KIDS', 6378 'vol. 28': '#', 6379 'everybody collection': '/shop/us/kids/kids-everybody-collection' 6380 } 6381 }, 6382 girls: { 6383 list: { 6384 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=179206-KIDS', 6385 'matching set': '/shop/us/kids/girls-matching-sets', 6386 'the cool stuff': '/shop/us/search?filtered=true&rows=90&searchTerm=THE+COOL+STUFF&start=0&departmentId=179206-KIDS', 6387 'vol. 28': '#', 6388 'everybody collection': '/shop/us/kids/kids-everybody-collection' 6389 } 6390 } 6391 }, 6392 hol: { 6393 'Men\'s': { 6394 list: { 6395 'pride collection': '/shop/us/pride-collection', 6396 'gender inclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=gender+inclusive&start=0&departmentId=12551-HOL', 6397 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=12551-HOL', 6398 'must-have collection': '/shop/us/mens-must-have-collection', 6399 'all day gameplay': '/shop/us/mens-all-day-gameplay', 6400 'part of a matching set': '/shop/us/search?departmentCategoryId=12551-HOL&searchTerm=part+of+a+matching+set' 6401 } 6402 }, 6403 'Women\'s': { 6404 list: { 6405 'pride collection': '/shop/us/pride-collection', 6406 'gender inclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=gender+inclusive&start=0&departmentId=12552-HOL', 6407 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=12552-HOL', 6408 'all day gameplay': '/shop/us/mens-all-day-gameplay', 6409 'part of a matching set': '/shop/us/womens-matching-sets' 6410 } 6411 } 6412 }, 6413 gh: { 6414 'Men\'s': { 6415 list: { 6416 'pride collection': '/shop/us/gilly-hicks/pride-collection', 6417 'gender inclusive': '/shop/us/gilly-hicks/unisex', 6418 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=65014360-GH', 6419 'part of a matching set': '/shop/us/search?filtered=true&rows=90&searchTerm=part+of+a+matching+set&start=0&departmentId=65014360-GH' 6420 } 6421 }, 6422 'Women\'s': { 6423 list: { 6424 'pride collection': '/shop/us/gilly-hicks/pride-collection', 6425 'gender inclusive': '/shop/us/gilly-hicks/unisex', 6426 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=65014366-GH', 6427 'part of a matching set': '/shop/us/search?filtered=true&rows=90&searchTerm=part+of+a+matching+set&start=0&departmentId=65014366-GH' 6428 } 6429 } 6430 }, 6431 st: { 6432 'men\'s': { 6433 list: { 6434 'gender inclusive': '/shop/us/social-tourist/shop-all-unisex', 6435 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=65014738-ST' 6436 } 6437 }, 6438 'women\'s': { 6439 list: { 6440 'gender inclusive': '/shop/us/social-tourist/shop-all-unisex', 6441 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=65014738-ST' 6442 } 6443 } 6444 } 6445 }; 6446 6447 var list2 = { 6448 anf: { 6449 'Men\'s': { 6450 list: { 6451 'best dressed guest collection': '/shop/us/mens-best-dressed-guest-weddings-and-events', 6452 'a&f x trevor project': '/shop/us/pride', 6453 'gender inclusive': '/shop/us/lookbook-a-and-f-lookbook', 6454 'trend edit': '/shop/us/search?filtered=true&rows=90&searchTerm=Trend+Edit&start=0&departmentId=12202-ANF', 6455 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=Online+Exclusive&start=0&departmentId=12202-ANF', 6456 'matching set': '/shop/us/mens-coordinated-sets', 6457 'getaway shop': '#', 6458 'vol. 28': '#', 6459 'golf shop': '#', 6460 'for justice collection': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=For+Justice+Collection', 6461 '6 inch | 15 cm': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=6+inch', 6462 'a&f vintage stretch denim': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=A%26F+Vintage+Stretch+Denim', 6463 'a&f signature stretch denim': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=A%26F+Signature+Stretch+Denim', 6464 'a&f authentic rigid denim': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=Authentic+Rigid+Denim', 6465 'a&f basic stretch denim': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=basic+stretch+Denim', 6466 'a&f signature stretch twill': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=Signature+Stretch+Twill', 6467 '7 inch | 18 cm': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=7+inch', 6468 '9 inch | 23 cm': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=9+inch', 6469 '3 inch | 7 cm': '/shop/us/search?departmentCategoryId=12202-ANF&searchTerm=3+inch' 6470 } 6471 }, 6472 'Women\'s': { 6473 list: { 6474 'best dressed guest collection': '/shop/us/womens-best-dressed-guest-weddings-and-events', 6475 'a&f x trevor project': '/shop/us/pride', 6476 'gender inclusive': '/shop/us/lookbook-a-and-f-lookbook', 6477 'trend edit': '/shop/us/search?filtered=true&rows=90&searchTerm=Trend+Edit&start=0&departmentId=12203-ANF', 6478 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=Online+Exclusive&start=0&departmentId=12203-ANF', 6479 'matching set': '/shop/us/womens-matching-sets-dresses-and-rompers', 6480 'getaway shop': '#', 6481 'vol. 28': '#', 6482 'premium footwear collection': '/shop/us/search?departmentCategoryId=12203-ANF&searchTerm=premium+footwear+collection', 6483 'abercrombie x tia booth': '#', 6484 'for justice collection': '/shop/us/search?filtered=true&rows=90&searchTerm=For+Justice+Collection&start=0&departmentId=12203-ANF', 6485 'a&f all day comfort stretch denim': '/shop/us/search?departmentCategoryId=12203-ANF&searchTerm=A%26F+All+Day+Comfort+Stretch+Denim', 6486 'a&f signature stretch denim': '/shop/us/search?departmentCategoryId=12203-ANF&searchTerm=A%26F+Signature+Stretch+Denim', 6487 'a&f vintage stretch denim': '/shop/us/search?departmentCategoryId=12203-ANF&searchTerm=A%26F+Vintage+Stretch+Denim', 6488 'authentic rigid denim': '/shop/us/search?departmentCategoryId=12203-ANF&searchTerm=Authentic+Rigid+Denim', 6489 'hold-you-in stretch denim': '/shop/us/search?departmentCategoryId=12203-ANF&searchTerm=Hold-You-In+Stretch+Denim' 6490 } 6491 } 6492 }, 6493 kids: { 6494 boys: { 6495 list: { 6496 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=179205-KIDS', 6497 'matching set': '/shop/us/kids/boys-matching-sets', 6498 'the cool stuff': '/shop/us/search?filtered=true&rows=90&searchTerm=THE+COOL+STUFF&start=0&departmentId=179205-KIDS', 6499 'vol. 28': '#', 6500 'everybody collection': '/shop/us/kids/kids-everybody-collection' 6501 } 6502 }, 6503 girls: { 6504 list: { 6505 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=179206-KIDS', 6506 'matching set': '/shop/us/kids/girls-matching-sets', 6507 'the cool stuff': '/shop/us/search?filtered=true&rows=90&searchTerm=THE+COOL+STUFF&start=0&departmentId=179206-KIDS', 6508 'vol. 28': '#', 6509 'everybody collection': '/shop/us/kids/kids-everybody-collection' 6510 } 6511 } 6512 }, 6513 hol: { 6514 'Men\'s': { 6515 list: { 6516 'pride collection': '/shop/us/pride-collection', 6517 'gender inclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=gender+inclusive&start=0&departmentId=12551-HOL', 6518 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=12551-HOL', 6519 'must-have collection': '/shop/us/mens-must-have-collection', 6520 'all day gameplay': '/shop/us/mens-all-day-gameplay', 6521 'part of a matching set': '/shop/us/search?departmentCategoryId=12551-HOL&searchTerm=part+of+a+matching+set' 6522 } 6523 }, 6524 'Women\'s': { 6525 list: { 6526 'pride collection': '/shop/us/pride-collection', 6527 'gender inclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=gender+inclusive&start=0&departmentId=12552-HOL', 6528 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=12552-HOL', 6529 'all day gameplay': '/shop/us/mens-all-day-gameplay', 6530 'part of a matching set': '/shop/us/womens-matching-sets', 6531 'built in shorts': '#', 6532 'i have pockets': '#', 6533 'classic stretch': '#', 6534 'advanced stretch': '#', 6535 'soft stretch': '#' 6536 } 6537 } 6538 }, 6539 gh: { 6540 'Men\'s': { 6541 list: { 6542 'pride collection': '/shop/us/gilly-hicks/pride-collection', 6543 'gender inclusive': '/shop/us/gilly-hicks/unisex', 6544 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=65014360-GH', 6545 'part of a matching set': '/shop/us/search?filtered=true&rows=90&
6545searchTerm=part+of+a+matching+set&start=0&departmentId=65014360-GH' 6546 } 6547 }, 6548 'Women\'s': { 6549 list: { 6550 'pride collection': '/shop/us/gilly-hicks/pride-collection', 6551 'gender inclusive': '/shop/us/gilly-hicks/unisex', 6552 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=65014366-GH', 6553 'part of a matching set': '/shop/us/search?filtered=true&rows=90&searchTerm=part+of+a+matching+set&start=0&departmentId=65014366-GH' 6554 } 6555 } 6556 }, 6557 st: { 6558 'men\'s': { 6559 list: { 6560 'gender inclusive': '/shop/us/social-tourist/shop-all-unisex', 6561 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=65014738-ST' 6562 } 6563 }, 6564 'women\'s': { 6565 list: { 6566 'gender inclusive': '/shop/us/social-tourist/shop-all-unisex', 6567 'online exclusive': '/shop/us/search?filtered=true&rows=90&searchTerm=online+exclusive&start=0&departmentId=65014738-ST' 6568 } 6569 } 6570 } 6571 }; 6572 6573 var publicMembers = { 6574 computeWhiteList: function computeWhiteList(testSegment, brand, deptName) { 6575 var whitelist = null; 6576 switch (testSegment) { 6577 case 'B': 6578 whitelist = list1[brand] && list1[brand][deptName] && list1[brand][deptName].list; 6579 break; 6580 case 'C': 6581 whitelist = list2[brand] && list2[brand][deptName] && list2[brand][deptName].list; 6582 break; 6583 default: 6584 break; 6585 } 6586 return whitelist; 6587 } 6588 }; 6589 6590 return publicMembers; 6591 }); 6592 6593 6594 6595redesign.define('productModules/product/productNameAndDescription',[ 6596 'productModules/helpers/Utils', 6597 'productModules/helpers/hyperlinkShortDescriptorsSplitTest' 6598], function ProductNameAndDescription(Utils, whitelistHelper) { 6599 'use strict'; 6600 6601 var productUniqueNameSelector = '.product-page-info__title .product-title-component'; 6602 var productPromoSelector = '.product-price-v2__promo-info'; 6603 var productShortDescSelector = '.short-description'; 6604 6605 var separator = ' | '; 6606 var messageBus = $(ANF); 6607 6608 var publicMembers = { 6609 listen: function listen() { 6610 messageBus.on('productPageInfo:swatchSelected productPageInfo:swatchOnLoad', onSwatchSelected); 6611 messageBus.on('shortDescriptorTest:init', handleShortDescriptorSplitTestInit); 6612 } 6613 }; 6614 6615 function onSwatchSelected(e, $selectedSwatch, $productSection, productId) { 6616 var productData = Utils.getProductData(productId); 6617 var isLoyaltyRegion = ANF.params.storeAttributes.loyalty.enabled; 6618 var isProductBadgeEnabled = ANF.params.storeAttributes.catalogV2.hasProductBadgeEnabled; 6619 if (productData) { 6620 updateProductName($productSection, productData); 6621 updateMerchPromoMsg($productSection, productData); 6622 6623 var isEarlyAccessProduct = Utils.productHasEarlyAccessIndicator(productId); 6624 if (isLoyaltyRegion && isEarlyAccessProduct && !isProductBadgeEnabled) { 6625 updateEarlyAccessDescription($productSection); 6626 } else { 6627 updateShortDescription($productSection, productData, window.digitalData.get('test.pdpHyperlinkDescriptor', 'A')); 6628 } 6629 } 6630 } 6631 6632 function updateProductName($productSection, productData) { 6633 var newProductName = Utils.getValueFromObject(productData, 'name', ''); 6634 var $productName = $productSection.find(productUniqueNameSelector); 6635 $productName.html(newProductName); 6636 } 6637 6638 function updateMerchPromoMsg($productSection, productData) { 6639 var merchPromoMsg = Utils.getValueFromObject(productData, 'merchPromoMsg', ''); 6640 // no need to further manipulate merchPromoMsg if it's blank 6641 var newMerchPromoMsg = merchPromoMsg === '' ? '' : '<span class="promo-badge">' + merchPromoMsg + '</span>'; 6642 var $merchPromo = $productSection.find(productPromoSelector); 6643 // edge case: When there's no $merchPromo when load but there is a new merchPromoMsg on swatch click 6644 // we need to create and add $merchPromo on the DOM as last child of .product-price-v2 6645 if (!$merchPromo.length && !!newMerchPromoMsg) { 6646 $merchPromo = $(document.createElement('div')); 6647 $merchPromo.addClass('product-price-v2__promo-info product-price-v2__promo-info--bogo'); 6648 $productSection.find('.product-price-v2').append($merchPromo); 6649 } 6650 6651 $merchPromo.html(newMerchPromoMsg); 6652 } 6653 6654 function updateEarlyAccessDescription($productSection) { 6655 var earlyAccessTMNT = ANF.messaging.product.PDP_EARLY_ACCESS; 6656 var $productShortDesc = $productSection.find(productShortDescSelector); 6657 6658 $productShortDesc.html(earlyAccessTMNT); 6659 } 6660 6661 function handleShortDescriptorSplitTestInit(e, segment) { 6662 var $productSection = Utils.getProductSection(); 6663 var productData = Utils.getProductData($productSection.data('productid')); 6664 updateShortDescription($productSection, productData, segment); 6665 } 6666 6667 function updateShortDescription($productSection, productData, segment) { 6668 var shortDesc1 = Utils.getValueFromObject(productData, 'shortDe
6668sc1', ''); 6669 var shortDesc2 = Utils.getValueFromObject(productData, 'shortDesc2', ''); 6670 6671 if (segment !== 'A') { 6672 var deptName = ANF.catalog.data.categoryHierarchyObj[1].enName; 6673 var productBrand = productData.productAttrs.brand; 6674 var descriptorWhitelist = whitelistHelper.computeWhiteList(segment, productBrand, deptName); 6675 var shortDesc1Link = descriptorWhitelist[shortDesc1.toLowerCase()]; 6676 var shortDesc2Link = descriptorWhitelist[shortDesc2.toLowerCase()]; 6677 shortDesc1 = shortDesc1Link ? '<a href=' + shortDesc1Link + '>' + shortDesc1 + '</a>' : null; 6678 shortDesc2 = shortDesc2Link ? '<a href=' + shortDesc2Link + '>' + shortDesc2 + '</a>' : null; 6679 } 6680 6681 var shortDesc = $.grep([shortDesc1, shortDesc2], Boolean).join(separator); 6682 var $productShortDesc = $productSection.find(productShortDescSelector); 6683 $productShortDesc.html(shortDesc); 6684 } 6685 6686 return publicMembers; 6687}); 6688 6689redesign.define('productModules/product/addToBag/productStickyAddToBag',[ 6690 'productModules/helpers/productAddToBagHelper', 6691 'productModules/helpers/Utils', 6692 'globalServices/userState' 6693], function ProductStickyAddToBag(addToBagHelper, Utils, userStateService) { 6694 'use strict'; 6695 6696 var fadeSpeed = 100; 6697 var fadeEasing = 'linear'; 6698 var addToBagButtonSelectorClass = '.product .product-page-add-to-bag__button'; 6699 var addToBagFormContainerSelectorClass = '.product-page-add-to-bag__container'; 6700 var stickyAddToBagContainerSelectorClass = '.product-page-sticky-add-to-bag__container'; 6701 var quickviewActiveModalSelectorClass = '.product-quickview__modal[data-open="true"]'; 6702 var stickySizeTileSelectorClass = '.product-page-sticky-size-tile__container'; 6703 var stickySizeTrayOverlaySelectorClass = '.product-page-sticky-size-tile__overlay'; 6704 var stickyOverlay = '<div class="product-page-sticky-size-tile__overlay"></div>'; 6705 var stickyAddToBagButtonSelectorClass = '.product-page-sticky-add-to-bag__container .product-page-add-to-bag__button'; 6706 var stickySizeTileInputsSelector = '.product-page-sticky-size-tile__container .product-sizes .product-attrs__attr input'; 6707 var sizeTileContainerSelectorClass = '.product-page__swatches-attributes'; 6708 var attributeTrayWrapperSelectorClass = '.attribute-tray-wrapper'; 6709 var attributeTrayCollapsedState = 'collapsed-state'; 6710 var attributeTraySizeTileState = 'size-tile-state'; 6711 var attributeTrayState = 'collapsed-state'; 6712 var productSection = '.product-page-v2-modules'; 6713 var memberAccessSelector = '.product .member-early-access-logged-out'; 6714 var stickyMemberAccessSelectorClass = '.product-page-sticky-add-to-bag__container .member-early-access-logged-out'; 6715 6716 var messageBus = $(ANF); 6717 6718 return function StickyAddToBagObserver(productIntersectionObserver) { 6719 function wireIntersectionObserver(productId) { 6720 var $addToBagButton = memberAccessFlagCheck(productId); 6721 var addToBagButtonObserver = new productIntersectionObserver.IntersectionObserver( 6722 function productIntersectionObserverCallback(entries) { 6723 for (var i = 0, len = entries.length; i < len; i++) { 6724 var atbButton = entries[i]; 6725 // when boundingClientRect y is less than 0, it means button was scrolled off the top of the page, 6726 // only worry about intersect to hide/show sticky when the button is scrolled into/out of view at the bottom! 6727 if (ANF.breakpoints.screen === 'small_phone' || ANF.breakpoints.screen === 'phone') { 6728 handleIntersection(atbButton.isIntersecting); 6729 } 6730 } 6731 }, 6732 { 6733 threshold: 1 6734 } 6735 ); 6736 addToBagButtonObserver.observe($addToBagButton[0]); 6737 } 6738 6739 function handleIntersection(isIntersecting) { 6740 var isCollapsedState = attributeTrayState === attributeTrayCollapsedState; 6741 if (isIntersecting) { // scrolling down, disable everything 6742 $(stickyAddToBagContainerSelectorClass).slideUp(200); 6743 publicMembers.toggleStickySizeTiles(false); 6744 } else if (!isIntersecting && !isCollapsedState) { // scrolling up and size tiles were active, show sticky tiles! 6745 publicMembers.toggleStickySizeTiles(true); 6746 } else { // scrolling up and size tiles weren't active, show sticky button 6747 $(stickyAddToBagContainerSelectorClass).slideDown(200); 6748 } 6749 } 6750 6751 function memberAccessFlagCheck(productId) { 6752 var $addToBagButton = $(addToBagButtonSelectorClass); 6753 var earlyAccessFlag = Utils.productHasEarlyAccessIndicator(productId); 6754 if (earlyAccessFlag && !userStateService.isLoggedIn()) { 6755 $addToBagButton = $(memberAccessSelector); 6756 } 6757 return $addToBagButton; 6758 } 6759 6760 var publicMembers = { 6761 'bindEvents': function bindEvents() { 6762 var $document = $(document); 6763 $document.on('click', stickyAddToBagButtonSelectorClass, publicMembers.handleStickyATBClick); 6764 $document.on('click', stickySizeTileInputsSelector, publicMembers.handleStickySizeTileSelection); 6765 $document.on('click', stickySizeTrayOverlaySelectorClass, publicMembers.closeAttributeTray); 6766 }, 6767 listen: function listen() { 6768 messageBus.on('productPageInfo:load productPageInfo:swatchSelected', publicMembers.addToBagLoad); 6769 }, 6770 'setupStickyAddToBagButton': function setupStickyAddToBagButton(productId) { 6771 var $addToBagButton = memberAccessFlagCheck(productId); 6772 var $stickyAddToBagButton = $addToBagButton.clone(true); // Copy the add to bag button and it's event listeners 6773 var $stickyAddToBagContainer = $(stickyAddToBagContainerSelectorClass); // Container that has padding 6774 6775 if ($(stickyAddToBagButtonSelectorClass).length < 1 && $(stickyMemberAccessSelectorClass).length < 1 ) {
6776 // Inject our copied button into our container that has padding 6777 $stickyAddToBagContainer.append($stickyAddToBagButton); 6778 wireIntersectionObserver(productId); 6779 } 6780 }, 6781 'toggleStickySizeTiles': function toggleStickySizeTiles(showStickySizeTiles) { 6782 var $stickySizeTileContainer = $(stickySizeTileSelectorClass); 6783 var $overlay = $(stickySizeTrayOverlaySelectorClass); 6784 6785 if (showStickySizeTiles) { 6786 $stickySizeTileContainer.slideDown(200); 6787 $overlay.fadeIn(fadeSpeed, fadeEasing); 6788 publicMembers.setAttributeTrayState(attributeTrayState); 6789 } else { 6790 $stickySizeTileContainer.slideUp(200); 6791 $overlay.fadeOut(fadeSpeed, fadeEasing); 6792 } 6793 }, 6794 'showSizeTileTray': function showSizeTileTray() { 6795 publicMembers.setAttributeTrayState(attributeTraySizeTileState); 6796 publicMembers.toggleStickySizeTiles(true); 6797 }, 6798 'closeStickySizeTray': function closeStickySizeTray() { 6799 publicMembers.toggleStickySizeTiles(false); 6800 publicMembers.setAttributeTrayState(attributeTrayCollapsedState); 6801 }, 6802 'addToBagLoad': function addToBagLoad() { 6803 // only setup sticky add to bag for new pdp phone view. 6804 if (!$(quickviewActiveModalSelectorClass).length && ANF.breakpoints && (ANF.breakpoints.screen === 'small_phone' || ANF.breakpoints.screen === 'phone') && !Utils.hasProductMFEEnabled()) { 6805 var $productSection = $(productSection); 6806 var productId = $productSection.data('productid'); 6807 publicMembers.setupStickyAddToBagButton(productId); 6808 publicMembers.setupStickyAttributeTray(); 6809 } 6810 }, 6811 'setupStickyAttributeTray': function setupStickyAttributeTray() { 6812 // copy the html from the size group 6813 var $addToBagFormContainer = $(addToBagFormContainerSelectorClass); 6814 var $stickyAttributeTiles = $(sizeTileContainerSelectorClass).clone(true); 6815 var $attributeTrayWrapper = $(stickySizeTileSelectorClass).find(attributeTrayWrapperSelectorClass); 6816 6817 /* 6818 * For a11y, input names & id's need to be completely unique. 6819 * After cloning the size & swatch tiles, we append `_tray` to 6820 * make the cloned inputs unique 6821 */ 6822 function identifyTraySizes(_idx, attr) { 6823 return attr + '_tray'; 6824 } 6825 6826 $stickyAttributeTiles.find('input') 6827 .attr('id', identifyTraySizes) 6828 .attr('name', identifyTraySizes); 6829 6830 $stickyAttributeTiles.find('label').attr('for', identifyTraySizes); 6831 6832 if ($(stickySizeTileInputsSelector).length < 1) { 6833 $attributeTrayWrapper.append($stickyAttributeTiles); 6834 if (Utils.hasFinalSaleEnabled()) { 6835 var $finalSaleContainer = $('.final-sale-container').clone(true); 6836 $attributeTrayWrapper.find('.product-urgency-size-availability').after($finalSaleContainer); 6837 } 6838 } 6839 $addToBagFormContainer.append($(stickyOverlay)); 6840 }, 6841 'closeAttributeTray': function closeAttributeTray(e) { 6842 e.preventDefault(); 6843 6844 var $attributeTray = $(stickySizeTileSelectorClass);
6845 var $overlay = $(stickySizeTrayOverlaySelectorClass); 6846 6847 $attributeTray.slideUp(200); 6848 publicMembers.setAttributeTrayState(attributeTrayCollapsedState); 6849 $(attributeTrayWrapperSelectorClass).animate({ scrollTop: 0 }, 'slow'); 6850 6851 $overlay.fadeOut(fadeSpeed, fadeEasing); 6852 }, 6853 6854 'setAttributeTrayState': function setAttributeTrayState(state) { 6855 var $attributeTray = $(stickySizeTileSelectorClass); 6856 6857 $attributeTray 6858 .removeClass(attributeTrayCollapsedState) 6859 .removeClass(attributeTraySizeTileState) 6860 .addClass(state); 6861 6862 attributeTrayState = state; 6863 }, 6864 6865 handleStickyATBClick: function handleStickyATBClick(e) { 6866 e.preventDefault(); 6867 var $form = $(e.currentTarget).closest(addToBagFormContainerSelectorClass); 6868 if (addToBagHelper.hasQualifiedSku($form)) { 6869 messageBus.trigger('initiateAddToBag:sticky', {$form: $form}); 6870 } else { 6871 publicMembers.showSizeTileTray(); 6872 } 6873 }, 6874 6875 handleStickySizeTileSelection: function handleStickySizeTileSelection(e) { 6876 var $selectedTile = $(e.currentTarget).closest('.product-attrs__attr'); 6877 if (!$selectedTile.hasClass('unavailable')) { 6878 var $form = $(e.currentTarget).closest(addToBagFormContainerSelectorClass); 6879 6880 messageBus.trigger('stickySizeTileSelected', {$selectedTile: $selectedTile}); 6881 6882 if (addToBagHelper.hasQualifiedSku($form)) { 6883 messageBus.trigger('initiateAddToBag:sticky', {$form: $form}); 6884 publicMembers.closeStickySizeTray(); 6885 } 6886 } 6887 } 6888 }; 6889 6890 return publicMembers; 6891 }; 6892}); 6893 6894redesign.define('productPartial/pdp_detailBadging',['handlebars','dsPartial/icon-block'], function(Handlebars) { 6895 6896this["ANF"] = this["ANF"] || {}; 6897this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 6898 6899Handlebars.registerPartial("pdp_detailBadging", this["ANF"]["templates"]["pdp_detailBadging"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 6900 var stack1; 6901 6902 return " <li>\n" 6903 + ((stack1 = container.invokePartial(partials["icon-block"],depth0,{"name":"icon-block","hash":{"data-center-vertically":true,"data-icon-size":(depth0 != null ? depth0.size : depth0),"data-icon":(depth0 != null ? depth0.filename : depth0)},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 6904 + " </li>\n"; 6905},"2":function(container,depth0,helpers,partials,data) { 6906 return " <span class=\"h3\">" 6907 + container.escapeExpression(container.lambda((depth0 != null ? depth0.text : depth0), depth0)) 6908 + "</span>\n"; 6909},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 6910 var stack1; 6911 6912 return "<ul class=\"detail-badging-list split-list\">\n" 6913 + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.badges : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 6914 + "</ul>\n"; 6915},"usePartial":true,"useData":true})); 6916 6917return this["ANF"]; 6918 6919}); 6920redesign.define('productModules/product/productDetailBadging',[ 6921 'coreClass/ANFImage', 6922 'productModules/helpers/Utils', 6923 'productModules/helpers/imageLoadHelper', 6924 'productPartial/pdp_detailBadging', 6925 'anfUtil/testFramework' 6926], function ProductDetailBadging( 6927 ANFImage, 6928 Utils, 6929 imageLoadHelper, 6930 productPartialPdpDetailBadging, 6931 testFramework 6932) { 6933 'use strict'; 6934 6935 var messageBus = $(ANF); 6936 var BRAND = ''; 6937 var BADGE_MAP = {}; 6938
6939 function addDetailBadgingBackground(productDataObject, $badgingContainer) { 6940 var imageUrl = ANFImage.instance() 6941 .setImageName(imageLoadHelper.getProd1ImageId(productDataObject.imageSets)) 6942 .setWidth(800) 6943 .url; 6944 6945 $badgingContainer.css('background-image', 'url(' + imageUrl + ')'); 6946 } 6947 6948 // TODO: Clean up paramter list to remove unused variables.EPIC-8683 6949 // using this funciton to condense the paramter list, getting us close to the EPIC-8683 Tech Debt 6950 function detailBadgingLoad(e, $selectedSwatch, $productSection, productId) { 6951 var productData = Utils.getProductData(productId); 6952 if (!Utils.isQuickviewOpened) { 6953 var badgingData = productData.productAttrsComplex.ProductBadgingCode; 6954 var tempDOM = ''; 6955 var bigDetailObj = {badges: []}; 6956 var $badgingContainer = $('.product-badging__container'); 6957 6958 // Current badging data for a single badge items is just a value and name without an identifier, so we must check for the values array first 6959 // This will be addressed in greater detail on the backend in EPIC-8685 6960 if (badgingData && badgingData.values) { 6961 badgingData.values.forEach(function createBadgeTemplateObject(badge) { 6962 var iconFileName = BADGE_MAP[badge.identifier]; 6963 bigDetailObj.badges.push({ 6964 'filename': iconFileName, 6965 'size': 50, 6966 'text': badge.value 6967 }); 6968 }); 6969
6970 addDetailBadgingBackground(productData, $badgingContainer); 6971 tempDOM = productPartialPdpDetailBadging.templates.pdp_detailBadging(bigDetailObj); 6972 $productSection.find('.details-badge-list-wrapper').html(tempDOM); 6973 $badgingContainer.show(); 6974 } else { 6975 $badgingContainer.hide(); 6976 $badgingContainer.css('background-image', ''); 6977 } 6978 } 6979 } 6980 6981 var publicMembers = { 6982 listen: function listen() { 6983 messageBus.on('productPageInfo:swatchOnLoad productPageInfo:swatchSelected mfe:SwatchSelected', detailBadgingLoad); 6984 }, 6985 init: function init() { 6986 BRAND = testFramework.store.hol() ? 'hco' : 'anf'; 6987 6988 if ($('.product-page-v2-modules').hasClass('is-kids-product')) { 6989 BRAND = 'kids'; 6990 } 6991 6992 BADGE_MAP = { 6993 '100%RecycledFill': 'recycled-fill-hco', 6994 '2-WayZipper': 'two-way-zipper-anf', 6995 'TwoWayStretch': 'two-way-stretch-anf', 6996 '4-WayStretch': 'four-way-stretch-' + BRAND, 6997 'BuiltforOptimalWarmth': 'warm-hco', 6998 'Breathable': 'breathable-' + BRAND, 6999 'CozyLining': 'cozy-lining-' + BRAND, 7000 'CozyLinedPockets': 'cozy-lined-pockets-kids', 7001 'ConcealedPocket': 'concealed-pocket-anf', 7002 'ConcealedPhonePocket': 'concealed-phone-pocket-anf', 7003 'DownBlend': 'down-blend-hco', 7004 'DownFill': 'down-blend-hco', 7005 'DownInsulated': 'down-insulated-anf', 7006 'FleeceLinedPockets': 'fleece-lined-pockets-hco', 7007 'Insulated': 'insulated-anf', 7008 'InteriorPocket': 'interior-pocket-hco', 7009 'LightweightFabric': 'lightweight-fabric-anf', 7010 'LowImpactSupport': 'low-impact-support-gh', 7011 'MachineWashable': 'machine-washable-' + BRAND, 7012 'MediumImpactSupport': 'medium-impact-support-gh', 7013 'MemoryFoam': 'memory-foam-gh', 7014 'MoistureWicking': 'moisture-wicking-' + BRAND, 7015 'Packable': 'packable-anf', 7016 'PackableHood': 'packable-hood-hco', 7017 'PrimaloftInsulation': 'primaloft-insulation-anf', 7018 'RecycledFill': 'recycled-fill-hco', 7019 'RemovableFaux-FurTrim': 'removable-faux-fur-trim-' + BRAND, 7020 'RemovableHood': 'removable-hood-' + BRAND, 7021 'RemovableLiner': 'removable-liner-anf', 7022 'ResponsibleDownStandardCertified': 'responsible-down-standard-certified-hco', 7023 'Reversible': 'reversible-anf', 7024 'SeamSealed': 'seam-sealed-anf', 7025 'SoftTouchFabric': 'soft-touch-fabric-gh', 7026 'StretchFabric': 'stretch-fabric-' + BRAND, 7027 'SuperSoft': 'super-soft-anf', 7028 'VeganLeather': 'vegan-leather-anf', 7029 'WarmDownFill': 'warm-down-fill-hco', 7030 'WarmLikeDown': 'warm-hco', 7031 'WaterproofFabric': 'waterproof-fabric-anf', 7032 'WaterRepellent': 'water-repellent-anf', 7033 'WaterResistant': 'water-resistance-fabric-' + BRAND, 7034 'WaterResistantFabric': 'water-resistance-fabric-' + BRAND, 7035 'Wicking': 'wicking-anf', 7036 'WindproofFabric': 'windproof-fabric-anf', 7037 'WindResistant': 'wind-resistant-' + BRAND, 7038 'WindResistantFabric': 'wind-resistant-' + BRAND, 7039 'Wireless': 'wireless-gh', 7040 'WrinkleRelease': 'wrinkle-release-anf' 7041 }; 7042 } 7043 }; 7044 7045 return publicMembers; 7046}); 7047 7048redesign.define('productModules/product/buyGridAccordions/productDetailsAccordion',['productModules/helpers/Utils', 7049 'productServices/product/template', 7050 'coreServices/geo/helper' 7051], function ProductDetailsAccordion(Utils, templateService, GeoHelper) { 7052 'use strict'; 7053 7054 var messageBus = $(ANF); 7055 var hasNewLongDesc = ANF.params.storeAttributes.catalogV2.hasProductLongDescImportedCleanUP; 7056 7057 function loadDetailsAccordion(e, $selectedSwatch, $productSection) { 7058 var productId = $productSection.data('productid'); 7059 var $swatch = $selectedSwatch; 7060 var productData = Utils.getProductData(productId); 7061 var longDescriptionDetails = productData.longDesc.trim(); 7062 7063 if ($swatch.length === 0) { 7064 $swatch = $productSection.find('.product-swatches .selected'); 7065 } 7066 7067 var kicId = $swatch.find('input').val();
7068 if (/\bimported[.,!?]?$/.test(longDescriptionDetails.toLowerCase())) { 7069 longDescriptionDetails = longDescriptionDetails.substring(0, longDescriptionDetails.lastIndexOf(' ')); // Imported text always last word 7070 } 7071 var detailsData = { 7072 productName: $.parseHTML(productData.name)[0].wholeText, 7073 longDescription: hasNewLongDesc ? productData.longDesc : longDescriptionDetails, 7074 storeItemNumber: getStoreItemNumber($swatch, kicId), 7075 webItemNumber: getWebItemNumber(productData, kicId), 7076 isChinaSite: GeoHelper.isChinaSite(), 7077 imported: productData.imported 7078 }; 7079 7080 var accordionPanelContent = templateService.renderTemplate('detailsAccordion', detailsData); 7081 $productSection.find('.product-details__accordion .details-accordion__container').html(accordionPanelContent); 7082 } 7083 7084 function getStoreItemNumber($selectedSwatch, kicId) { 7085 var $buyGrid = $selectedSwatch.parents('.product-page__info'); 7086 var $firstMatchingOption = $buyGrid.find('.product-page__skus option[data-kic="' + kicId + '"]').first(); 7087 var newLongSku = $firstMatchingOption.attr('data-long-sku'); 7088 7089 return newLongSku; 7090 } 7091 7092 function getWebItemNumber(productData, kicId) { 7093 var newWebCollection = productData.collection; 7094 var newWebKIC = kicId; 7095 var newWebItem = newWebCollection; 7096 if (typeof newWebKIC !== 'undefined' && newWebKIC.indexOf('KIC_') >= 0) { 7097 newWebItem += '/' + newWebKIC.split('KIC_')[1]; 7098 } 7099 7100 return newWebItem; 7101 } 7102 7103 var publicMembers = { 7104 listen: function listen() { 7105 messageBus.on('productPageInfo:swatchOnLoad productPageInfo:swatchSelected', loadDetailsAccordion); 7106 } 7107 }; 7108 7109 return publicMembers; 7110}); 7111 7112redesign.define('productServices/product/productReviewService',[ 7113], function ProductReviewService() { 7114 'use strict'; 7115 7116 var reviewsContent = ANF.messaging.catalogV2.REVIEWS; 7117 7118 var publicMembers = {
7119 buildReviewSummaryModel: function buildReviewSummaryModel(data) { 7120 var bvProductId; // This is the BV product ID which correlates to our sku 7121 var fitPopularRecommendation = null; 7122 var fitTotalCount = 0; 7123 7124 if (typeof data.Includes !== 'undefined' && typeof data.Includes.Products !== 'undefined') { 7125 for (var key in data.Includes.Products) { // Quickest way to identify the first property in the object is to start to traverse them and then jump out 7126 if (data.Includes.Products.hasOwnProperty(key)) { 7127 bvProductId = key; // This is the BV product ID which correlates to our sku 7128 break; 7129 } 7130 } 7131 } 7132 7133 if (data.TotalResults > 0 && typeof data.Includes.Products !== 'undefined' && typeof data.Includes.Products[bvProductId] !== 'undefined' && typeof data.Includes.Products[bvProductId].ReviewStatistics !== 'undefined') { 7134 var secondaryRatingsAverages = data.Includes.Products[bvProductId].ReviewStatistics.SecondaryRatingsAverages; 7135 if (typeof secondaryRatingsAverages !== 'undefined' && (secondaryRatingsAverages.Fit_22 && typeof secondaryRatingsAverages.Fit_22.AverageRating !== 'undefined')) { 7136 fitPopularRecommendation = Math.round(secondaryRatingsAverages.Fit_22.AverageRating); 7137 fitTotalCount = data.Includes.Products[bvProductId].ReviewStatistics.TotalReviewCount; 7138 data.showFitRecommendation = true; 7139 } 7140 7141 if (fitTotalCount < 4) { 7142 data.showFitRecommendation = false; 7143 } 7144 7145 switch (fitPopularRecommendation) { 7146 case 1: 7147 data.fitRecommendation = reviewsContent.GLB_FIT_SMALL_TITLE_DESCRIPTION; 7148 break; 7149 case 2: 7150 data.fitRecommendation = reviewsContent.GLB_FIT_SOMEWHAT_SMALL_TITLE_DESCRIPTION; 7151 break; 7152 case 3: 7153 data.fitRecommendation = reviewsContent.GLB_FIT_TRUE_TITLE_DESCRIPTION; 7154 break; 7155 case 4: 7156 data.fitRecommendation = reviewsContent.GLB_FIT_SOMEWHAT_LARGE_TITLE_DESCRIPTION; 7157 break; 7158 case 5: 7159 data.fitRecommendation = reviewsContent.GLB_FIT_LARGE_TIT
7159LE_DESCRIPTION; 7160 break; 7161 default: 7162 break; 7163 } 7164 } 7165 7166 return data; 7167 } 7168 }; 7169 7170 return publicMembers; 7171}); 7172 7173redesign.define('productModules/helpers/garmentMeasurements',['productModules/helpers/Utils'], function GarmentMeasurements(Utils) { 7174 'use strict'; 7175 7176 var savedMeasurementObj = {}; 7177 7178 function buildMeasurements(keys, tmnts, isEyeware, attrs, valueKey, labelKey) { 7179 return keys.reduce(function buildMeasurementReducer(arr, key) { 7180 if (attrs.hasOwnProperty(key)) { 7181 var tooltip = isEyeware ? tmnts.MEASUREMENTS_ACCORDION.MEASUREMENTS_TOOLTIPS : tmnts.GARMENT_MEASUREMENT_LABELS.GARMENT_TOOLTIPS; 7182 var label = isEyeware ? tmnts.MEASUREMENTS_ACCORDION.MEASUREMENTS_LABELS : tmnts.GARMENT_MEASUREMENT_LABELS.GARMENT_LABELS; 7183 7184 var obj = { 7185 tooltip: false, 7186 tooltipId: key, 7187 tooltipHeader: tooltip[key].HEADER, 7188 tooltipBody: tooltip[key].BODY 7189 }; 7190 obj[labelKey] = label[key]; 7191 obj[valueKey] = attrs[key]; 7192 arr.push(obj); 7193 } 7194 return arr; 7195 }, []); 7196 } 7197 7198 var publicMembers = { 7199 getGarmentMeasurementData: function getGarmentMeasurementData(productId, anfObject, $productSection) { 7200 var productAttrsData = Utils.getProductData(productId).productAttrs; 7201 7202 if (Utils.isKidsProduct($productSection)) { 7203 return { baseSize: productAttrsData.BaseMeasurementSize, measurements: [] }; 7204 } 7205 7206 if (!savedMeasurementObj[productId]) { 7207 var keys = [ 7208 'BaseSleeveLengthMeasurement', 7209 'BaseShoulderToHemMeasurement', 7210 'BaseInseamX-ShortMeasurement', 7211 'BaseInseamShortMeasurement', 7212 'BaseInseamRegularMeasurement', 7213 'BaseInseamLongMeasurement', 7214 'BaseFrontRiseMeasurement', 7215 'BaseLegOpeningCircumferenceMeasurement', 7216 'BaseSkirtLengthMeasurement' 7217 ]; 7218 7219 savedMeasurementObj[productId] = { 7220 baseSize: productAttrsData.BaseMeasurementSize, 7221 measurements: buildMeasurements( 7222 keys, 7223 anfObject.messaging.product, 7224 false, 7225 productAttrsData, 7226 'garmentValue', 7227 'garmentLabel' 7228 ) 7229 }; 7230 } 7231 return savedMeasurementObj[productId]; 7232 }, 7233 7234 getEyewearMeasurementData: function getEyewearMeasurementData(productId, anfObject) { 7235 var productAttrsMeasurementData = Utils.getProductData(productId).productAttrs; 7236 7237 if (!savedMeasurementObj[productId]) { 7238 var keys = [ 7239 'EyewearDimensionLensWidth', 7240 'EyewearDimensionBridgeWidth', 7241 'EyewearDimensionArmLength' 7242 ]; 7243 savedMeasurementObj[productId] = { 7244 isEyeware: true, 7245 measurements: buildMeasurements( 7246 keys, 7247 anfObject.messaging.product, 7248 true, 7249 productAttrsMeasurementData, 7250 'lensMeasurementsValue', 7251 'lensMeasurementsLabel' 7252 ) 7253 }; 7254 } 7255 return savedMeasurementObj[productId]; 7256 } 7257 }; 7258 7259 return publicMembers; 7260}); 7261 7262redesign.define('productPartial/pdp_modelSpecs',['handlebars','wcsHelper/i18n'], function(Handlebars) { 7263 7264this["ANF"] = this["ANF"] || {}; 7265this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 7266 7267Handlebars.registerPartial("pdp_modelSpecs", this["ANF"]["templates"]["pdp_modelSpecs"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 7268 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.escapeExpression, alias3=container.lambda; 7269 7270 return "<p class=\"size-guide-fit-model-specs\">\n " 7271 + alias2(helpers.i18n.call(alias1,"product.PDP_TEXT_MODEL_SPECS_LABEL",{"name":"i18n","hash":{},"data":data})) 7272 + ": <span class=\"model-specs__height-measurement\">" 7273 + ((stack1 = alias3((depth0 != null ? depth0.modelHeight : depth0), depth0)) != null ? stack1 : "") 7274 + "</span> <span class='lowercase'>" 7275 + alias2(helpers.i18n.call(alias1,"product.GLB_WEARINGSIZE",{"name":"i18n","hash":{},"data":data})) 7276 + "</span> <span class=\"model-specs__size-measurement\">" 7277 + alias2(alias3((depth0 != null ? depth0.modelSize : depth0), depth0)) 7278 + "</span>\n</p>\n"; 7279},"useData":true})); 7280 7281return this["ANF"]; 7282 7283}); 7284redesign.define('productPartial/pdp_sizeFitAccordionContent',['handlebars','wcsHelper/i18n','productPartial/pdp_modelSpecs','productPartial/pdp_garmentMeasurements'], function(Handlebars) { 7285 7286this["ANF"] = this["ANF"] || {}; 7287this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 7288 7289Handlebars.registerPartial("pdp_sizeFitAccordionContent", this["ANF"]["templates"]["pdp_sizeFitAccordionContent"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 7290 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 7291 7292 return "\n <div class=\"hr above-fit-information\"></div>\n\n <h4 class=\"h4\">" 7293 + container.escapeExpression(helpers.i18n.call(alias1,"product.FIT_INFORMATION",{"name":"i18n","hash":{},"data":data})) 7294 + "</h4>\n <div class=\"fit-information\">\n <ul>\n" 7295 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasFitRecommendation : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7296 + "\n" 7297 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.fitType : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7298 + "\n" 7299 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.style : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7300 + "\n" 7301 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.bottomRiseCode : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7302 + "\n" 7303 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.bottomLengthCode : depth0),{"name":"if","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7304 + "\n" 7305 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasModelSpecs : depth0),{"name":"if","hash":{},"fn":container.program(12, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7306 + " </ul>\n </div>\n\n" 7307 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.compareFitLinkEspot : depth0),{"name":"if","hash":{},"fn":container.program(16, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 7308},"2":function(container,depth0,helpers,partials,data) { 7309 var stack1; 7310 7311 return " <li>\n <p class=\"size-fit-reviewer-recommendation\">\n " 7312 + ((stack1 = container.lambda((depth0 != null ? depth0.contextualFitFullTMNT : depth0), depth0)) != null ? stack1 : "") 7313 + "\n </p>\n </li>\n"; 7314},"4":function(container,depth0,helpers,partials,data) { 7315 var alias1=container.lambda, alias2=container.escapeExpression; 7316 7317 return " <li>" 7318 + alias2(alias1((depth0 != null ? depth0.fitHeaderTMNT : depth0), depth0)) 7319 + ": " 7320 + alias2(alias1((depth0 != null ? depth0.fitDescription : depth0), depth0)) 7321 + "</li>\n"; 7322},"6":function(container,depth0,helpers,partials,data) { 7323 var alias1=container.lambda, alias2=container.escapeExpression; 7324 7325 return " <li>" 7326 + alias2(alias1((depth0 != null ? depth0.style : depth0), depth0)) 7327 + ": " 7328 + alias2(alias1((depth0 != null ? depth0.styleDescription : depth0), depth0)) 7329 + "</li>\n"; 7330},"8":function(container,depth0,helpers,partials,data) { 7331 var alias1=container.lambda, alias2=container.escapeExpression; 7332 7333 return " <li>" 7334 + alias2(alias1((depth0 != null ? depth0.riseTMNT : depth0), depth0)) 7335 + ": " 7336 + alias2(alias1((depth0 != null ? depth0.bottomRiseCode : depth0), depth0)) 7337 + "</li>\n"; 7338},"10":function(container,depth0,helpers,partials,data) { 7339 var alias1=container.lambda, alias2=container.escapeExpression; 7340 7341 return " <li>" 7342 + alias2(alias1((depth0 != null ? depth0.lengthTMNT : depth0), depth0)) 7343 + ": " 7344 + alias2(alias1((depth0 != null ? depth0.bottomLengthCode : depth0), depth0)) 7345 + "</li>\n"; 7346},"12":function(container,depth0,helpers,partials,data) { 7347 var stack1; 7348 7349 return " <li>\n" 7350 + ((stack1 = helpers["with"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.modelSpecs : depth0),{"name":"with","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7351 + " </li>\n"; 7352},"13":function(container,depth0,helpers,partials,data) { 7353 var stack1; 7354 7355 return " <div class=\"model-specs-container\">\n " 7356 + ((stack1 = container.invokePartial(partials.pdp_modelSpecs,depth0,{"name":"pdp_modelSpecs","fn":container.program(14, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 7357 + "\n </div>\n"; 7358},"14":function(container,depth0,helpers,partials,data) { 7359 return ""; 7360},"16":function(container,depth0,helpers,partials,data) { 7361 var stack1; 7362 7363 return " " 7364 + ((stack1 = container.lambda((depth0 != null ? depth0.compareFitLinkEspot : depth0), depth0)) != null ? stack1 : "") 7365 + "\n"; 7366},"18":function(container,depth0,helpers,partials,data) { 7367 return " <div class=\"hr below-fit-information\"></div>\n"; 7368},"20":function(container,depth0,helpers,partials,data) { 7369 var stack1; 7370 7371 return " <div class=\"garment-measurements\">\n" 7372 + ((stack1 = helpers["with"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.garmentData : depth0),{"name":"with","hash":{},"fn":container.program(21, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7373 + " </div>\n"; 7374},"21":function(container,depth0,helpers,partials,data) { 7375 var stack1; 7376 7377 return " " 7378 + ((stack1 = container.invokePartial(partials.pdp_garmentMeasurements,depth0,{"name":"pdp_garmentMeasurements","fn":container.program(14, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 7379 + "\n"; 7380},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 7381 var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 7382 7383 return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasFitInfo : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7384 + "\n" 7385 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.displayHR : depth0),{"name":"if","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7386 + "\n" 7387 + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasGarmentMeasurements : depth0),{"name":"if","hash":{},"fn":container.program(20, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 7388},"usePartial":true,"useData":true})); 7389 7390return this["ANF"]; 7391 7392}); 7393redesign.define('productPartial/pdp_fitRecommendation',['handlebars'], function(Handlebars) { 7394 7395this["ANF"] = this["ANF"] || {}; 7396this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 7397 7398Handlebars.registerPartial("pdp_fitRecommendation", this["ANF"]["templates"]["pdp_fitRecommendation"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 7399 var stack1; 7400 7401 return "<p class=\"size-fit-reviewer-recommendation\">\n " 7402 + ((stack1 = container.lambda((depth0 != null ? depth0.contextualFitFullTMNT : depth0), depth0)) != null ? stack1 : "") 7403 + "\n</p>\n"; 7404},"useData":true})); 7405 7406return this["ANF"]; 7407 7408}); 7409redesign.define('globalServices/marketing/eSpotContentProvider',[],function () { 7410 'use strict'; 7411 7412 /**
7413 * @description ESpot content API proxy service. 7414 * @exports globalServices/marketing/eSpotContentProvider 7415 * @see {@link https://ecom106.abercrombie.com/apidocs/#!/Marketing/getESpot} 7416 */ 7417 var publicMembers = { 7418 /** 7419 * @description A name/value object used by jQuery to represent a query string parameter. 7420 * @typedef {Object} NameValue 7421 * @property {string} name - The parameter name. 7422 * @property {*} value - The parameter value. 7423 */ 7424 7425 /** 7426 * @description The normalized Espot content settings object used throughout this service. 7427 * @typedef {Object} NormalizedSettings 7428 * @property {NameValue[]} parameters - An array of name/value objects that will be passed to the API. 7429 */ 7430 7431 /** 7432 * @description The URL for the `getESpot` API endpoint. Note: The ending forward slash ("/") is required. 7433 * @type {string} 7434 */ 7435 'apiUrl': ANF.params.storeAttributes.apiURLRoot + '/content/marketing/', 7436 7437 /** 7438 * @description Get eSpot content. 7439 * @param {string[]} eSpotNames - An array of eSpot names to retrieve. 7440 * @param {object[]} settings - An array of objects containing additional parameters 7441 * @throws {Error} If passed an invalid argument. 7442 * @return {Promise} A jQuery Promise implementation. 7443 */ 7444 'fetch': function (eSpotNames, settings) { 7445 if (Array.isArray(eSpotNames) && eSpotNames.length === 0) { 7446 return this.getEmptyArrayPromise(); 7447 } 7448 7449 var normalizedSettings = this.normalizeSettings(eSpotNames, settings); 7450 7451 if (!this.validateSettings(normalizedSettings)) { 7452 throw new Error('An invalid argument was supplied'); 7453 } 7454 7455 var queryString = normalizedSettings.parameters.map(function (parameter) { 7456 return parameter.name + '=' + encodeURIComponent(parameter.value); 7457 }).join('&'); 7458 7459 // The `name[]` parameter is not being URL decoded by the server so 7460 // we are using a manually generating a query string instead of 7461 // using the built in jQuery functionality. 7462 return $.getJSON(this.apiUrl + '?' + queryString).promise(); 7463 }, 7464 7465 /** 7466 * @description Get a Promise which resolves with an empty array for use in implementing the null object pattern. 7467 * @return {Promise} A jQuery Promise implementation. 7468 */ 7469 'getEmptyArrayPromise': function () { 7470 return $.Deferred().resolve([]).promise(); 7471 }, 7472 7473 /** 7474 * @description A callback function to be used with `Array.prototype.reduce` to validate an array of settings parameters. 7475 * @param {*} previousValue - This argument is currently unused. 7476 * @param {*} currentValue - The item to be verified as a name/value object. 7477 * @return {boolean} A Boolean indicating whether the current reduction is valid. 7478 */ 7479 'isValidParametersReduction': function (previousValue, currentValue) { 7480 return $.isPlainObject(currentValue) && 7481 currentValue.hasOwnProperty('name') && 7482 typeof currentValue.name === 'string' && 7483 currentValue.hasOwnProperty('value'); 7484 }, 7485 7486 /** 7487 * @description Normalize the settings passed into the service. 7488 * @param {string[]} eSpotNames - An array of Espot names to normalize. 7489 * @param {object[]} settings - An array of objects containing additional parameters 7490 * @return {NormalizedSettings} The normalized settings object. 7491 */ 7492 'normalizeSettings': function (eSpotNames, settings) { 7493 var normalizedSettings = {'parameters': []}; 7494 7495 if (!Array.isArray(eSpotNames)) { 7496 return normalizedSettings; 7497 } 7498 7499 eSpotNames.forEach(function (name) { 7500 normalizedSettings.parameters.push({ 7501 'name': 'name', 7502 'value': name 7503 }); 7504 }); 7505 7506 if (Array.isArray(settings)) { 7507 settings.forEach(function (addlParam) { 7508 normalizedSettings.parameters.push({ 7509 'name': addlParam.key, 7510 'value': addlParam.value 7511 }); 7512 }); 7513 } 7514 7515 return normalizedSettings; 7516 }, 7517 7518 /** 7519 * @description Validate a normalized settings object. 7520 * @param {NormalizedSettings} settings - A normalized settings object. 7521 * @return {boolean} A Boolean indicating whether the provided settings are valid or not. 7522 */ 7523 'validateSettings': function (settings) { 7524 if (!$.isPlainObject(settings)) { 7525 return false;
7526 } 7527 7528 if (!Array.isArray(settings.parameters) || settings.parameters.length === 0) { 7529 return false; 7530 } 7531 7532 if (!settings.parameters.reduce(this.isValidParametersReduction, false)) { 7533 return false; 7534 } 7535 7536 return true; 7537 } 7538 }; 7539 7540 return publicMembers; 7541}); 7542 7543redesign.define('api/adobeExperienceManager/dataSource',[ 7544 'api/Classes/Adapter', 7545 'api/Classes/AdapterError', 7546 'anfUtil/testFramework', 7547 'anfUtil/constants' 7548], function aemContentAPIAdapter(APIAdapter, AdapterError, test, coreConstants) { 7549 'use strict'; 7550 7551 var adapter = new APIAdapter(); 7552 7553 /** 7554 * @property {object} CONSTANTS 7555 * @property {object} CONSTANTS.BRAND 7556 * @property {string} CONSTANTS.BRAND.ANF anf 7557 * @property {string} CONSTANTS.BRAND.HOL hol 7558 * @property {string} CONSTANTS.BRAND.KIDS kids 7559 * @property {object} CONSTANTS.MATCH_CRITERIA 7560 * @property {string} CONSTANTS.MATCH_CRITERIA.DEFAULT default_home_page 7561 */ 7562 adapter.CONSTANTS = { 7563 SWITCH_MODES: { 7564 OFF: 'OFF', 7565 AEM: 'AEM', 7566 AEM_JSP: 'AEM_JSP' 7567 }, 7568 ESPOT_SWITCH: { 7569 OFF: '0', 7570 ON: '1' 7571 }, 7572 ESPOT_CONFIG: { 7573 AEM: '2', 7574 AEM_JSP: '3' 7575 } 7576 }; 7577 7578 var errorMessages = { 7579 INVALID_TIMEOUT: 'The timeout attribute needs to be a number', 7580 INVALID_URL_ROOT: 'The urlRoot is required', 7581 INVALID_LOCALE: 'The locale is required', 7582 INVALID_ESPOT: 'The espot ID(s) is required', 7583 INVALID_STYLES_IN_RESPONSE: 'The stylesInResponse is required' 7584 }; 7585 7586 /** 7587 * @property {object} REASON 7588 */ 7589 7590 /** 7591 * @name read 7592 * @description Function to sit in front of the api call so we can add some logic if we are going to make the call or not 7593 * @param {object} [settings] - the settings to be supplied to the function 7594 * @param {Number} [settings.timeout=0] - the max time to wait for the ajax call to come back 7595 * @return {promise} this is a jquery promise that will be resolved/rejected with an object c
7595ontaining reason code and a combination of ajaxData, ajaxJqXHR, ajaxTextStatus, and ajaxErrorThrown depending on the result of the ajax call 7596 */ 7597 adapter.configRead({ 7598 endPoint: '/' + coreConstants.anf('params.storeAttributes.api.aem.aemApiURLContext') + '/v3', 7599 method: 'GET', 7600 getDataType: function getDataType(data) { 7601 return data.dataType || 'json'; 7602 }, 7603 getTimeout: function getTimeout(data) { 7604 return data.timeout || 0; 7605 }, 7606 validation: function validation(data, context) { 7607 // validate data goes here 7608 if (test.type.defined(data.timeout, false) && test.type.not.number(data.timeout, false)) { 7609 throw new AdapterError(context.REASON.INVALID_PARAMS, errorMessages.INVALID_TIMEOUT); 7610 } 7611 7612 if (test.string.empty(data.locale, true)) { 7613 throw new AdapterError(context.REASON.INVALID_PARAMS, errorMessages.INVALID_LOCALE); 7614 } 7615 7616 if ((!test.object.containsKey(data, 'espotId', false) || test.string.empty(data.espotId, true)) && 7617 (!test.object.containsKey(data, 'pageId', false) || test.string.empty(data.pageId, true))) { 7618 throw new AdapterError(context.REASON.INVALID_PARAMS, errorMessages.INVALID_ESPOT); 7619 } 7620 7621 if (!test.type.boolean(data.stylesInResponse, false)) { 7622 throw new AdapterError(context.REASON.INVALID_PARAMS, errorMessages.INVALID_STYLES_IN_RESPONSE); 7623 } 7624 }, 7625 preAjax: function preAjax(data) { 7626 var myData = $.extend(true, {}, data); 7627 delete myData.timeout; 7628 delete myData.dataType; 7629 delete myData.adapterSettings; 7630 return myData; 7631 }, 7632 done: function done(data, textStatus, jqXHR) { 7633 /** 7634 * @this CRUD 7635 */ 7636 var response; 7637 var responseData = data; 7638 if (typeof responseData === 'string') { 7639 responseData = JSON.parse(data); 7640 } 7641 if (responseData && test.array.not.empty(responseData.response, false)) { 7642 response = this.getAjaxOkResponse(responseData.response, textStatus, jqXHR); 7643 } else { 7644 response = responseData; 7645 } 7646 this.deferred.resolve(response); 7647 } 7648 }); 7649 7650 Object.freeze(adapter.CONSTANTS.SWITCH_MODES); 7651 7652 return adapter; 7653}); 7654 7655redesign.define('coreServices/personalizedContent',[ 7656 'api/adobeExperienceManager/dataSource', 7657 'anfUtil/testFramework' 7658], function personalizedContentService( 7659 aemContentAPI, 7660 test 7661) { 7662 'use strict'; 7663 7664 var CONSTANTS = Object.create(Object.prototype, { 7665 REASONS: { 7666 value: Object.create(Object.prototype, { 7667 AJAX_OK: { 7668 value: 1 7669 }, 7670 AJAX_FAIL: { 7671 value: 2 7672 }, 7673 INVALID_PARAMS: { 7674 value: 3 7675 }, 7676 NO_CONTENT: { 7677 value: 4 7678 } 7679 }) 7680 }, 7681 SWITCH_MODES: { 7682 value: aemContentAPI.CONSTANTS.SWITCH_MODES 7683 }, 7684 ESPOT_SWITCH: { 7685 value: aemContentAPI.CONSTANTS.ESPOT_SWITCH 7686 }, 7687 ESPOT_CONFIG: { 7688 value: aemContentAPI.CONSTANTS.ESPOT_CONFIG 7689 } 7690 7691 }); 7692 7693 var standardResponseObject = { 7694 node: null, 7695 reason: null 7696 }; 7697 7698 var adapterSettings; 7699 var response; 7700 7701 function handleFailedContentCalls(contentUri) { 7702 // Create container for failed content and fill it later 7703 var failedFragmentElement = document.querySelector('div[data-url="' + contentUri + '"]'); 7704 if (failedFragmentElement === null) { 7705 failedFragmentElement = document.createElement('div'); 7706 failedFragmentElement.classList.add('failed-fragment'); 7707 failedFragmentElement.setAttribute('data-url', contentUri); 7708 } 7709 return failedFragmentElement; 7710 } 7711 7712 /** 7713 * @private 7714 * @description function to just make the call to get the aem content promise 7715 * @param {object} settings - the setting object 7716 * @param {string} settings.segment - the url of the content 7717 * @param {number} settings.timeout - the time wait for content to come back 7718 * @return {Deferred.promise} - the promise returned from the aemConentAPI.read call 7719 */ 7720 function getAEMContentReadPromise(settings) { 7721 var myDeferred = new $.Deferred(); 7722 if (!settings.segment) { 7723 return myDeferred.resolve(handleFailedContentCalls(settings.segment)).promise(); 7724 } 7725 $.ajax({ 7726 url: settings.segment, 7727 timeout: settings.timeout 7728 }) 7729 .done(function contentDoneHandler(data) { 7730 var html = document.createElement('html'); 7731 var element = document.createElement('div'); 7732 var body; 7733 7734 html.innerHTML = data; 7735 body = html.querySelector('body'); 7736 element.innerHTML = body.innerHTML; 7737 7738 myDeferred.resolve(element); 7739 }) 7740 .fail(function contentFailHandler() { 7741 var failedFragmentElement = handleFailedContentCalls(settings.segment); 7742 myDeferred.resolve(failedFragmentElement); 7743 }); 7744 7745 return myDeferred.promise(); 7746 } 7747 7748 /* 7749 * @private 7750 * @description Creates a function to be used to stitch together one or more responses when we fetch the AEM Content 7751 * @param {$.Deferred} myDeferred - the Deferred object to be enacted upon 7752 * @return {function} A function to handle the success of making the request to get the aem content 7753 */ 7754 function getAemContentReadSuccessHandler(myDeferred) { 7755 return function aemContentReadSuccessResponseHandler() { 7756 var content = Array.prototype.slice.call(arguments);
7757 var domNode = document.createElement('div'); 7758 var responseObj = Object.create(standardResponseObject); 7759 7760 domNode = content.reduce(function contentReductionHandler(node, currentNode) { 7761 node.appendChild(currentNode); 7762 return node; 7763 }, domNode); 7764 7765 if (domNode.childNodes.length > 0) { 7766 responseObj.reason = CONSTANTS.REASONS.AJAX_OK; 7767 responseObj.node = domNode; 7768 myDeferred.resolve(responseObj); 7769 } else { 7770 // Fetch the standard function to fail with no content and execute it right away 7771 getFailWithNoContent(myDeferred)(); 7772 } 7773 }; 7774 } 7775 7776 /* 7777 * @private 7778 * @description Creates a function to be used to handle the results of the call to get the list of content from AEM 7779 * @param {object} settings - the setting object 7780 * @param {$.Deferred} settings.myDeferred - the Deferred object to be enacted upon 7781 * @param {number} settings.timeout - The time to wait for the HTML content to come back 7782 * @return {function} A function to handle the success of making the request to get the AEM content 7783 */ 7784 function getAemSegmentReadSuccessHandler(settings) { 7785 return function aemSegmentReadSuccessHandler(results) { 7786 response = results; 7787 if ( 7788 results && 7789 results.reason === aemContentAPI.REASON.AJAX_OK && 7790 test.array.not.empty(results.data, false) 7791 ) { 7792 if (adapterSettings.isSingleResponse) { 7793 settings.myDeferred.resolve(results); 7794 } 7795 // Need to use apply so that the map is treated as separate 7796 // arguments instead on one to take advantage of the $.when 7797 // marshaling of promises/Deferreds 7798 var fragments = results.data[0].pages.map(function mapHandler(fragment) { 7799 return fragment.path; 7800 }, this); 7801 $.when.apply($, fragments.map(function getPromiseListItem(segment) { 7802 return getAEMContentReadPromise({ 7803 segment: segment, 7804 timeout: settings.timeout 7805 }); 7806 })) 7807 .done(getAemContentReadSuccessHandler(settings.myDeferred)) 7808 .fail(getFailWithNoContent(settings.myDeferred)); 7809 } else { 7810 getFailWithNoContent(settings.myDeferred)(); 7811 } 7812 }; 7813 } 7814 7815 /* 7816 * @private 7817 * @description Creates a method that will reject the given Deferred with the reason of NO_CONTENT 7818 * @param {$.Deferred} myDeferred - the Deferred object to be rejected 7819 * @return {function} a method to reject the supplied deferred with the NO_CONTENT reason 7820 */ 7821 function getFailWithNoContent(myDeferred) { 7822 return function failWithNoContent() { 7823 var responseObj = Object.create(standardResponseObject); 7824 responseObj.reason = CONSTANTS.REASONS.NO_CONTENT; 7825 responseObj.node = document.createElement('div'); 7826 myDeferred.reject(responseObj); 7827 }; 7828 } 7829 7830 7831 return { 7832 reasons: CONSTANTS.REASONS, 7833 CONSTANTS: { 7834 SWITCH_MODES: CONSTANTS.SWITCH_MODES, 7835 ESPOT_SWITCH: CONSTANTS.ESPOT_SWITCH, 7836 ESPOT_CONFIG: CONSTANTS.ESPOT_CONFIG 7837 }, 7838 /*
7839 * @description This acts a single point of entry to get the products that are currently saved from the server. It will update local storage on successful call to the mysaves api 7840 * @param {string} brand - The brand that we are fetching content for 7841 * @param {string} locale - The local that should be used 7842 * @param {string} matchCriteria - The matching criteria 7843 * @param {number} aemTimeout - How long we should wait for the api call before timing out 7844 * @return {promise} this is a jQuery promise that will be resolved/rejected with an object containing reason code and a combination of ajaxData, ajaxJqXHR, ajaxTextStatus, and ajaxErrorThrown depending on the result of the ajax call 7845 */ 7846 get: function get(data) { 7847 var myDeferred = new $.Deferred(); 7848 var options = $.extend(true, {}, data); 7849 adapterSettings = data.adapterSettings; 7850 options.headers = { 7851 'x-source': 'CRS_WEB', 7852 Accept: 'application/json' 7853 }; 7854 aemContentAPI.read(options) 7855 .done(getAemSegmentReadSuccessHandler({ 7856 myDeferred: myDeferred, 7857 timeout: data.adapterSettings.aemHTMLTimeout 7858 }) 7859 ).fail(getFailWithNoContent(myDeferred)); 7860 7861 return myDeferred.promise(); 7862 }, 7863 7864 getFailedContent: function getFailedContent(settings) { 7865 return getAEMContentReadPromise(settings); 7866 }, 7867 7868 getAEMResponse: function getAEMResponse() { 7869 return (response && response.data) ? response.data : []; 7870 } 7871 }; 7872}); 7873 7874redesign.define('globalPartial/productSliderTemplate',['handlebars','dsPartial/faux-button','dsPartial/product-template','dsPartial/product-name','dsPartial/product-price','dsPartial/product-price-text'], function(Handlebars) { 7875 7876this["ANF"] = this["ANF"] || {}; 7877this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 7878 7879Handlebars.registerPartial("productSliderTemplate", this["ANF"]["templates"]["productSliderTemplate"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data,blockParams,depths) { 7880 var stack1; 7881 7882 return ((stack1 = container.invokePartial(partials["faux-button"],depth0,{"name":"faux-button","hash":{"data-prop":(depth0 != null ? depth0.productTemplateProps : depth0),"class-list":"js-product-card-button product-quickview__button"},"fn":container.program(2, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 7883},"2":function(container,depth0,helpers,partials,data,blockParams,depths) { 7884 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 7885 7886 return " <div \n class=\"js-product-card-wrapper product-card-wrapper product-card--" 7887 + alias2(alias1(((stack1 = (depth0 != null ? depth0.productAttrs : depth0)) != null ? stack1.brand : stack1), depth0)) 7888 + "\"\n data-collection=\"" 7889 + alias2(alias1((depth0 != null ? depth0.collection : depth0), depth0)) 7890 + "\"\n data-images=\"" 7891 + alias2(alias1(((stack1 = (depth0 != null ? depth0.productAttrs : depth0)) != null ? stack1.primaryFaceOutImage : stack1), depth0)) 7892 + "," 7893 + alias2(alias1(((stack1 = (depth0 != null ? depth0.productAttrs : depth0)) != null ? stack1.primaryHoverImage : stack1), depth0)) 7894 + "," 7895 + alias2(alias1(((stack1 = (depth0 != null ? depth0.productAttrs : depth0)) != null ? stack1.testFaceOutImage : stack1), depth0)) 7896 + "," 7897 + alias2(alias1(((stack1 = (depth0 != null ? depth0.productAttrs : depth0)) != null ? stack1.testHoverImage : stack1), depth0)) 7898 + "\"\n data-kic=\"" 7899 + alias2(alias1((depth0 != null ? depth0.kicId : depth0), depth0)) 7900 + "\"\n data-productId=\"" 7901 + alias2(alias1((depth0 != null ? depth0.productId : depth0), depth0)) 7902 + "\"\n >\n" 7903 + ((stack1 = container.invokePartial(partials["product-template"],depth0,{"name":"product-template","hash":{"class-list":"ds-standard-vertical-example"},"fn":container.program(3, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 7904 + " </div>\n"; 7905},"3":function(container,depth0,helpers,partials,data,blockParams,depths) { 7906 return " \n"; 7907},"4":function(container,depth0,helpers,partials,data) { 7908 var alias1=container.lambda, alias2=container.escapeExpression; 7909 7910 return " <img\n class=\"product-card__image\"\n src=\"" 7911 + alias2(alias1((depth0 != null ? depth0.imagePath : depth0), depth0)) 7912 + "\"\n data-fallback=\"" 7913 + alias2(alias1((depth0 != null ? depth0.imageFallbackPath : depth0), depth0)) 7914 + "\"\n alt=\"" 7915 + alias2(alias1((depth0 != null ? depth0.name : depth0), depth0)) 7916 + "\"\n data-productId=\"" 7917 + alias2(alias1((depth0 != null ? depth0.productId : depth0), depth0)) 7918 + "\"\n >\n"; 7919},"6":function(container,depth0,helpers,partials,data) { 7920 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 7921 7922 return ((stack1 = container.invokePartial(partials["product-name"],depth0,{"name":"product-name","hash":{"class-list":"product-card__name-wrapper","brand":(depth0 != null ? depth0.brand : depth0)},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 7923 + "\n <meta itemprop=\"name\" content=\"" 7924 + alias2(alias1((depth0 != null ? depth0.name : depth0), depth0)) 7925 + "\" />\n <meta itemprop=\"image\" content=\"" 7926 + alias2(alias1((depth0 != null ? depth0.imagePath : depth0), depth0)) 7927 + "\" />\n\n <div class=\"product-card__price\">\n <div class=\"product-price-v2--inner\" data-priceFlag=\"" 7928 + alias2(alias1((depth0 != null ? depth0.priceFlag : depth0), depth0)) 7929 + "\">\n" 7930 + ((stack1 = container.invokePartial(partials["product-price"],depth0,{"name":"product-price","hash":{"clearance-text":((stack1 = (depth0 != null ? depth0.priceObj : depth0)) != null ? stack1.clearanceText : stack1),"type":((stack1 = (depth0 != null ? depth0.priceObj : depth0)) != null ? stack1.priceType : stack1),"description":((stack1 = (depth0 != null ? depth0.priceObj : depth0)) != null ? stack1.description : stack1)},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 7931 + "\n" 7932 + ((stack1 = helpers["with"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.memberPriceObj : depth0),{"name":"with","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 7933 + " </div>\n </div>\n"; 7934},"7":function(container,depth0,helpers,partials,data) { 7935 var stack1; 7936 7937 return " " 7938 + ((stack1 = container.lambda((depth0 != null ? depth0.name : depth0), depth0)) != null ? stack1 : "") 7939 + "\n"; 7940},"9":function(container,depth0,helpers,partials,data) { 7941 var stack1; 7942 7943 return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.priceObj : depth0)) != null ? stack1.priceText : stack1),{"name":"each","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 7944},"10":function(container,depth0,helpers,partials,data) { 7945 var stack1; 7946 7947 return ((stack1 = container.invokePartial(partials["product-price-text"],depth0,{"name":"product-price-text","hash":{"formatted-
7947price":(depth0 != null ? depth0.formattedPriceText : depth0),"type":(depth0 != null ? depth0.priceTextType : depth0)},"fn":container.program(11, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 7948},"11":function(container,depth0,helpers,partials,data) { 7949 return ""; 7950},"13":function(container,depth0,helpers,partials,data) { 7951 var stack1; 7952 7953 return ((stack1 = container.invokePartial(partials["product-price"],depth0,{"name":"product-price","hash":{"clearance-text":(depth0 != null ? depth0.clearanceText : depth0),"type":(depth0 != null ? depth0.priceType : depth0),"description":(depth0 != null ? depth0.description : depth0),"class-list":"member-price"},"fn":container.program(14, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 7954},"14":function(container,depth0,helpers,partials,data) { 7955 var stack1; 7956 7957 return ((stack1 = helpers["with"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.priceText : depth0),{"name":"with","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 7958},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) { 7959 var stack1; 7960 7961 return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),depth0,{"name":"each","hash":{},"fn":container.program(1, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 7962},"3_d": function(fn, props, container, depth0, data, blockParams, depths) { 7963 7964 var decorators = container.decorators; 7965 7966 fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(4, data, 0, blockParams, depths),"inverse":container.noop,"args":["product-image-section"],"data":data}) || fn; 7967 fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(6, data, 0, blockParams, depths),"inverse":container.noop,"args":["product-content"],"data":data}) || fn; 7968 return fn; 7969 } 7970 7971,"useDecorators":true,"usePartial":true,"useData":true,"useDepths":true})); 7972 7973return this["ANF"]; 7974 7975}); 7976redesign.define('globalPartial/productSliderTemplateV3',['handlebars','dsPartial/faux-button','dsPartial/product-template','dsPartial/product-name','dsPartial/product-price','dsPartial/product-price-text'], function(Handlebars) { 7977 7978this["ANF"] = this["ANF"] || {}; 7979this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 7980 7981Handlebars.registerPartial("productSliderTemplateV3", this["ANF"]["templates"]["productSliderTemplateV3"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data,blockParams,depths) { 7982 var stack1; 7983 7984 return ((stack1 = container.invokePartial(partials["faux-button"],depth0,{"name":"faux-button","hash":{"data-prop":(depth0 != null ? depth0.productTemplateProps : depth0),"class-list":"js-product-card-button product-quickview__button"},"fn":container.program(2, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 7985},"2":function(container,depth0,helpers,partials,data,blockParams,depths) { 7986 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 7987 7988 return " <div \n class=\"js-product-card-wrapper product-card-wrapper product-card--" 7989 + alias2(alias1((depth0 != null ? depth0.brand : depth0), depth0)) 7990 + "\"\n data-collection=\"" 7991 + alias2(alias1((depth0 != null ? depth0.collectionId : depth0), depth0)) 7992 + "\"\n data-images=\"" 7993 + alias2(alias1((depth0 != null ? depth0.primaryFaceOut : depth0), depth0)) 7994 + "," 7995 + alias2(alias1(((stack1 = ((stack1 = ((stack1 = ((stack1 = (depth0 != null ? depth0.attributes : depth0)) != null ? stack1.primaryHoverImage : stack1)) != null ? stack1.value : stack1)) != null ? stack1["0"] : stack1)) != null ? stack1.value : stack1), depth0)) 7996 + "," 7997 + alias2(alias1(((stack1 = ((stack1 = ((stack1 = ((stack1 = (depth0 != null ? depth0.attributes : depth0)) != null ? stack1.testFaceOutImage : stack1)) != null ? stack1.value : stack1)) != null ? stack1["0"] : stack1)) != null ? stack1.value : stack1), depth0)) 7998 + "," 7999 + alias2(alias1(((stack1 = ((stack1 = ((stack1 = ((stack1 = (depth0 != null ? depth0.attributes : depth0)) != null ? stack1.testHoverImage : stack1)) != null ? stack1.value : stack1)) != null ? stack1["0"] : stack1)) != null ? stack1.value : stack1), depth0)) 8000 + "\"\n data-kic=\"" 8001 + alias2(alias1((depth0 != null ? depth0.kicId : depth0), depth0)) 8002 + "\"\n data-productId=\"" 8003 + alias2(alias1((depth0 != null ? depth0.productId : depth0), depth0)) 8004 + "\"\n >\n" 8005 + ((stack1 = container.invokePartial(partials["product-template"],depth0,{"name":"product-template","hash":{"class-list":"ds-standard-vertical-example"},"fn":container.program(3, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 8006 + " </div>\n"; 8007},"3":function(container,depth0,helpers,partials,data,blockParams,depths) { 8008 return " \n"; 8009},"4":function(container,depth0,helpers,partials,data) { 8010 var alias1=container.lambda, alias2=container.escapeExpression; 8011 8012 return " <img\n class=\"product-card__image\"\n src=\"" 8013 + alias2(alias1((depth0 != null ? depth0.imagePath : depth0), depth0)) 8014 + "\" \n data-fallback=\"" 8015 + alias2(alias1((depth0 != null ? depth0.imageFallbackPath : depth0), depth0)) 8016 + "\"\n alt=\"" 8017 + alias2(alias1((depth0 != null ? depth0.name : depth0), depth0)) 8018 + "\"\n data-productId=\"" 8019 + alias2(alias1((depth0 != null ? depth0.productId : depth0), depth0)) 8020 + "\"\n >\n"; 8021},"6":function(container,depth0,helpers,partials,data) { 8022 var stack1, alias1=container.lambda, alias2=container.escapeExpression; 8023 8024 return ((stack1 = container.invokePartial(partials["product-name"],depth0,{"name":"product-name","hash":{"class-list":"product-card__name-wrapper","brand":(depth0 != null ? depth0.brand : depth0)},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 8025 + "\n <meta itemprop=\"name\" content=\"" 8026 + alias2(alias1((depth0 != null ? depth0.name : depth0), depth0)) 8027 + "\" />\n <meta itemprop=\"image\" content=\"" 8028 + alias2(alias1((depth0 != null ? depth0.imagePath : depth0), depth0)) 8029 + "\" />\n\n <div class=\"product-card__price\">\n <div class=\"product-price-v2--inner\" data-priceFlag=\"" 8030 + alias2(alias1((depth0 != null ? depth0.priceFlag : depth0), depth0)) 8031 + "\">\n" 8032 + ((stack1 = container.invokePartial(partials["product-price"],depth0,{"name":"product-price","hash":{"clearance-text":((stack1 = (depth0 != null ? depth0.priceObj : depth0)) != null ? stack1.clearanceText : stack1),"type":((stack1 = (depth0 != null ? depth0.priceObj : depth0)) != null ? stack1.priceType : stack1),"description":((stack1 = (depth0 != null ? depth0.priceObj : depth0)) != null ? stack1.description : stack1)},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 8033 + "\n" 8034 + ((stack1 = helpers["with"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.memberPriceObj : depth0),{"name":"with","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 8035 + " </div>\n </div>\n"; 8036},"7":function(container,depth0,helpers,partials,data) { 8037 var stack1; 8038 8039 return " " 8040 + ((stack1 = container.lambda((depth0 != null ? depth0.name : depth0), depth0)) != null ? stack1 : "") 8041 + "\n"; 8042},"9":function(container,depth0,helpers,partials,data) { 8043 var stack1; 8044 8045 return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.priceObj : depth0)) != null ? stack1.priceText : stack1),{"name":"each","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 8046},"10":function(container,depth0,helpers,partials,data) { 8047 var stack1; 8048 8049 return ((stack1 = container.invokePartial(partials["product-price-text"],depth0,{"name":"product-price-text","hash":{"formatted-
8049price":(depth0 != null ? depth0.formattedPriceText : depth0),"type":(depth0 != null ? depth0.priceTextType : depth0)},"fn":container.program(11, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 8050},"11":function(container,depth0,helpers,partials,data) { 8051 return ""; 8052},"13":function(container,depth0,helpers,partials,data) { 8053 var stack1; 8054 8055 return ((stack1 = container.invokePartial(partials["product-price"],depth0,{"name":"product-price","hash":{"clearance-text":(depth0 != null ? depth0.clearanceText : depth0),"type":(depth0 != null ? depth0.priceType : depth0),"description":(depth0 != null ? depth0.description : depth0),"class-list":"member-price"},"fn":container.program(14, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 8056},"14":function(container,depth0,helpers,partials,data) { 8057 var stack1; 8058 8059 return ((stack1 = helpers["with"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.priceText : depth0),{"name":"with","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 8060},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) { 8061 var stack1; 8062 8063 return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),depth0,{"name":"each","hash":{},"fn":container.program(1, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); 8064},"3_d": function(fn, props, container, depth0, data, blockParams, depths) { 8065 8066 var decorators = container.decorators; 8067 8068 fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(4, data, 0, blockParams, depths),"inverse":container.noop,"args":["product-image-section"],"data":data}) || fn; 8069 fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(6, data, 0, blockParams, depths),"inverse":container.noop,"args":["product-content"],"data":data}) || fn; 8070 return fn; 8071 } 8072 8073,"useDecorators":true,"usePartial":true,"useData":true,"useDepths":true})); 8074 8075return this["ANF"]; 8076 8077}); 8078redesign.define('globalModulesHelpers/substituteTMNTParams',[], function () { 8079 'use strict'; 8080 8081 var publicMembers = { 8082 substitute: function (str, params) { 8083 var pattern = '[{]__INDEX__[}]'; 8084 var result = str; 8085 var expr; 8086 for (var i = 0; i < params.length; i++) { 8087 expr = new RegExp(pattern.replace('__INDEX__', i), 'g'); 8088 result = result.replace(expr, params[i]); 8089 } 8090 8091 return result; 8092 } 8093 }; 8094 8095 return publicMembers; 8096}); 8097 8098function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } 8099 8100function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 8101 8102function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 8103 8104redesign.define('es6CoreServices/recommendationSliderHelpers',['exports', 'anfUtil/constants', 'globalModulesHelpers/substituteTMNTParams'], function (exports, constants, substituteTMNTParams) { 8105 'use strict'; 8106 /** 8107 * @name isProductListV3Enabled 8108 * @public 8109 * @description Function that returns whether product list v3 flag is turned on or not 8110 * 8111 * @return {Boolean} - Returns a boolean. 8112 */ 8113 8114 var isProductListV3Enabled = function isProductListV3Enabled() { 8115 var _window$digitalData, _window$digitalData$e; 8116 8117 var storeAttributeFlags = (_window$digitalData = window.digitalData) === null || _window$digitalData === void 0 ? void 0 : (_window$digitalData$e = _window$digitalData.export()) === null || _window$digitalData$e === void 0 ? void 0 : _window$digitalData$e.flag; 8118 return storeAttributeFlags ? storeAttributeFlags['has-product-list-v3-enabled'] : false;
8119 }; 8120 /** 8121 * @name generateProductPriceObject 8122 * @public 8123 * @description This function will add a 'priceObj' object to the each product object in the product list array 8124 * @param {String} productsList - An array of objects. Each object contains product details 8125 * 8126 * @return {String} - Returns productsList array, with each object containing 'priceObj' 8127 */ 8128 8129 8130 var generateProductPriceObject = function generateProductPriceObject(productsList) { 8131 return productsList.map(function (product) { 8132 var clearanceText = product.priceFlag === 'clearance' ? constants.anf('messaging.catalogV2.TMNT.cardClearance.value') : null; 8133 var priceType = product.priceFlag === 'clearance' ? product.priceFlag : ''; 8134 var listPrice = product.prices.list.price; 8135 var listPriceFormatted = product.prices.list.formatted; 8136 var offerPrice = product.prices.offer.price; 8137 var offerPriceFormatted = product.prices.offer.formatted; 8138 var hasDiscountPricing = listPrice > offerPrice; // Ignoring range pricing & member pricing 8139 8140 var priceObjConfiguration = hasDiscountPricing ? { 8141 description: substituteTMNTParams.substitute(constants.anf('messaging.product.GLB_PRICE_DESC_IND_IND'), [listPriceFormatted, offerPriceFormatted]), 8142 priceText: [{ 8143 formattedPriceText: listPriceFormatted, 8144 priceTextType: 'original' 8145 }, { 8146 formattedPriceText: offerPriceFormatted, 8147 priceTextType: 'discount' 8148 }] 8149 } : { 8150 description: listPriceFormatted, 8151 priceText: [{ 8152 priceTextType: '', 8153 formattedPriceText: listPriceFormatted 8154 }] 8155 }; 8156 return _objectSpread(_objectSpread({}, product), {}, { 8157 priceObj: _objectSpread({ 8158 priceType: priceType, 8159 clearanceText: clearanceText 8160 }, priceObjConfiguration) 8161 }); 8162 }); 8163 }; 8164 8165 exports.isProductListV3Enabled = isProductListV3Enabled; 8166 exports.generateProductPriceObject = generateProductPriceObject; 8167 return exports; 8168}); 8169 8170redesign.define('globalModules/productSlider',[ 8171 'anfUtil/testFramework', 8172 'anfcore/components/slider', 8173 'coreClass/ANFImage', 8174 'globalPartial/productSliderTemplate', 8175 'globalPartial/productSliderTemplateV3', 8176 'es6CoreServices/recommendationSliderHelpers' 8177], 8178function productSlider( 8179 test, 8180 dsSlider, 8181 ANFImage, 8182 globalPartialProductSliderTemplate, 8183 globalPartialProductSliderTemplateV3, 8184 recommendationSliderHelpers 8185) { 8186 'use strict'; 8187 8188 var NO_TARGET = 'No target node provided.'; 8189 var INVALID_ARRAY = 'No array of products provided.'; 8190 var sliderDefaults = { 8191 clickable: true, 8192 infinite: true, 8193 slidesToShow: 2, 8194 slidesToScroll: 2, 8195 dots: false, 8196 arrows: true, 8197 centerMode: true, 8198 mobileFirst: true, 8199 respondTo: 'window', 8200 responsive: [ 8201 { 8202 breakpoint: 0, 8203 settings: { 8204 arrows: true, 8205 centerMode: false, 8206 slidesToShow: 2, 8207 slidesToScroll: 2 8208 } 8209 }, 8210 { 8211 breakpoint: 540, 8212 settings: { 8213 arrows: true, 8214 centerMode: false, 8215 slidesToShow: 3, 8216 slidesToScroll: 3 8217 } 8218 }, 8219 { 8220 breakpoint: 825, 8221 settings: { 8222 arrows: true, 8223 centerMode: false, 8224 slidesToShow: 4, 8225 slidesToScroll: 4 8226 } 8227 }, 8228 { 8229 breakpoint: 1200, 8230 settings: { 8231 arrows: true, 8232 centerMode: false, 8233 slidesToShow: 5, 8234 slidesToScroll: 5 8235 } 8236 } 8237 ] 8238 }; 8239 8240 /** 8241 * @description Function to build the slider html and instantiate slick slider 8242 * @param {array} productArray - an array of product objects 8243 * @param {object} targetNode - an object the slider will be injected in to 8244 * @param {object} settings - an object used to override the default slider settings 8245 * @param {function} customDataProperties - function that accepts a product and returns an array of key/value pairs to construct custom data properties 8246 * @return {undefined} 8247 */ 8248 function buildProductSlidesHtml(productArray, targetNode, settings, customDataProperties) { 8249 var dsSliderSettings = $.extend({}, sliderDefaults, settings); 8250 var productHtml; 8251 var localProductArray; 8252 8253 var isProductListV3Enabled = recommendationSliderHelpers.isProductListV3Enabled(); 8254 var normalizeFunction = isProductListV3Enabled ? normalizeProductDataV3 : normalizeProductData; 8255 var templateFunction = isProductListV3Enabled ? globalPartialProductSliderTemplateV3.templates.productSliderTemplateV3 : globalPartialProductSliderTemplate.templates.productSliderTemplate; 8256 8257 localProductArray = normalizeFunction(productArray, customDataProperties); 8258 productHtml = templateFunction(localProductArray); 8259 8260 // Disabling for this line that needs to manipulate the DOM 8261 // eslint-disable-next-line no-param-reassign 8262 targetNode.innerHTML = productHtml; 8263 8264 dsSlider.default().initializeSlider(targetNode, dsSliderSettings); 8265 8266 bindEvents(targetNode); 8267 8268 // add the automation testing attribute to the slick slider wrapper 8269 targetNode.setAttribute('data-aui', 'product-slider'); 8270 } 8271 8272 /** 8273 * @description Function that binds a custom function to the error trigger 8274 * @param {object} targetNode - an object the slider will be injected in to 8275 * @return {undefined} 8276 */ 8277 function bindEvents(targetNode) { 8278 var sliderImages = targetNode.querySelectorAll('.product-card__image'); 8279 for (var i = 0, len = sliderImages.length; i < len; i++) { 8280 setImageErrorHandler(sliderImages[i]); 8281 } 8282 } 8283 8284 function setImageErrorHandler(imgNode) { 8285 $(imgNode).one('error', function imgError() { 8286 imageErrorHandler(this); 8287 }); 8288 } 8289 8290 /** 8291 * @description Function to replace all slider images src with data src 8292 * @param {object} imgNode - node of the image that failed to load 8293 * @return {undefined} 8294 */ 8295 function imageErrorHandler(imgNode) { 8296 var failedProducts = $(imgNode).parents('.js-product-slider').find('.product-card__image[data-productId="' + imgNode.dataset.productid + '"]'); 8297 8298 for (var i = 0, len = failedProducts.length; i < len; i++) { 8299 failedProducts[i].src = failedProducts[i].dataset.fallback; 8300 } 8301 } 8302 8303 /** 8304 * @description Function to generate a string of custom data properties 8305 * @param {array} properties - Array of key/value pairs for custom properties 8306 * @return {string} - Empty string or a string of concatenated data properties 8307 */ 8308 function generateCustomDataProperties(properties) { 8309 var result = []; 8310 8311 if (test.type.array(properties) && !test.array.empty(properties)) { 8312 result = properties.map(function propertiesMap(prop) {
8313 if (prop.key) { 8314 return 'data-' + 8315 prop.key + 8316 (typeof prop.value !== 'undefined' && prop.value !== '' ? "='" + prop.value + "'" : ''); 8317 } 8318 8319 return ''; 8320 }); 8321 8322 return result.join(' '); 8323 } 8324 8325 return ''; 8326 } 8327 8328 function capitalizeFirstLetter(word) { 8329 if (typeof word === 'string' || word instanceof String) { 8330 return word.charAt(0).toUpperCase() + word.slice(1); 8331 } 8332 return ''; 8333 } 8334 8335 /** 8336 * @description Function that performs required string concatenation to properly build a slider 8337 * Same as normalizeProductData, adpated for product list v3 response 8338 * @param {array} productArray - array of product objects 8339 * @param {function} customProperties - sets custom properties on individual products 8340 * @return {array} - array of modified product objects 8341 */ 8342 function normalizeProductDataV3(productArray, customProperties) { 8343 var normalizedDataArray = productArray; 8344 var imageWidth = 347; 8345 var imageName; 8346 8347 for (var i = 0, len = normalizedDataArray.length; i < len; i++) { 8348 var product = normalizedDataArray[i]; 8349 var propsArray = test.type.function(customProperties) ? customProperties(product) : []; 8350 var faceout = product.primaryFaceOut || 'prod'; 8351 var faceoutType = capitalizeFirstLetter(faceout.slice(0, faceout.search(/\d/))); 8352 var customDataProperties = generateCustomDataProperties(propsArray); 8353 var imageSet = 'imageSet' + faceoutType; 8354 8355 product.productTemplateProps = "data-collectionid='" + product.collectionId + 8356 "' data-productid='" + product.productId + 8357 "' data-imageid='" + product.kicId + // image id is just kicId 8358 "' data-faceout='" + faceout + "' " + 8359 "' data-product-brand='" + product.brand + "' " + 8360 customDataProperties; 8361 8362 imageName = product[imageSet] ? product[imageSet][0] : product.imageSetProd[0]; 8363 8364 product.imagePath = ANFImage.instance() 8365 .setImageName(imageName) 8366 .setWidth(imageWidth) 8367 .url; 8368 8369 product.imageFallbackPath = ANFImage.instance() 8370 .setImageName(product.kicId + '_prod1') 8371 .setWidth(imageWidth) 8372 .url; 8373 } 8374 return normalizedDataArray; 8375 } 8376 8377 /** 8378 * @description Function that performs required string concatenation to properly build a slider 8379 * @param {array} productArray - array of product objects 8380 * @param {function} customProperties - sets custom properties on individual products 8381 * @return {array} - array of modified product objects 8382 */ 8383 function normalizeProductData(productArray, customProperties) { 8384 var normalizedDataArray = productArray.filter(function removeEmptyImageSet(product) { 8385 return !test.object.empty(product.imageSet); 8386 }); 8387 var imageWidth = 347; 8388 var imageName; 8389 8390 for (var i = 0, len = normalizedDataArray.length; i < len; i++) { 8391 var product = normalizedDataArray[i]; 8392 var faceout = extractFaceout(product.productAttrs.PrimaryFaceOutImage); 8393 var propsArray = test.type.function(customProperties) ? customProperties(product) : []; 8394 var faceoutType = faceout.slice(0, faceout.search(/\d/)); 8395 var customDataProperties = generateCustomDataProperties(propsArray); 8396 8397 product.productTemplateProps = "data-collectionid='" + product.collection + 8398 "' data-productid='" + product.productId + 8399 "' data-imageid='" + product.imageId + 8400 "' data-faceout='" + faceout + "' " + 8401 "' data-product-brand='" + product.productAttrs.brand + "' " + 8402 customDataProperties; 8403 8404 imageName = product.imageSet[faceoutType] ? product.imageSet[faceoutType][0].id : product.imageSet.prod[0].id; 8405 8406 product.imagePath = ANFImage.instance() 8407 .setImageName(imageName) 8408 .setWidth(imageWidth) 8409 .url; 8410 8411 product.imageFallbackPath = ANFImage.instance() 8412 .setImageName(product.imageId + '_prod1') 8413 .setWidth(imageWidth) 8414 .url; 8415 } 8416 return normalizedDataArray; 8417 } 8418 8419 /** 8420 * @description Function that extracts the faceout value from the image ID 8421 * @param {string} PrimaryFaceOutImage - image ID 8422 * @return {string} - image faceout value 8423 */ 8424 function extractFaceout(PrimaryFaceOutImage) { 8425 if (test.type.string(PrimaryFaceOutImage)) { 8426 var faceout = PrimaryFaceOutImage.split('_'); 8427 return faceout[faceout.length - 1]; 8428 } 8429 8430 return 'prod1'; 8431 } 8432 8433 /** 8434 * @description Function that checks if the parameter is an Array 8435 * @param {array} productArray - an array of product objects 8436 * @return {undefined} 8437 */ 8438 function validateProductArray(productArray) { 8439 if ( 8440 test.type.not.array(productArray, false) || 8441 (!productArray.length) 8442 ) { 8443 throw new Error(INVALID_ARRAY); 8444 } 8445 } 8446 8447 /** 8448 * @description Function that checks id the parameter is an Object 8449 * @param {object} targetNode - an object the slider will be injected in to 8450 * @return {undefined} 8451 */ 8452 function validateTargetNode(targetNode) { 8453 if (test.object.null(targetNode, false)) { 8454 throw new Error(NO_TARGET); 8455 } 8456 } 8457 8458 function resolveSliderOpts(overrides) { 8459 return $.extend({}, sliderDefaults, overrides); 8460 } 8461 8462 var publicMembers = { 8463 /**
8464 * @description Function that instantiates the product slider 8465 * @param {array} productArray - an array of product objects 8466 * @param {object} targetNode - an object the slider will be injected in to 8467 * @param {object} settings - an object used to override the default slider settings 8468 * @return {undefined} 8469 */ 8470 'init': function init(productArray, targetNode, settings) { 8471 var localSettings = $.extend(true, {}, settings); 8472 var sliderSettings = resolveSliderOpts(localSettings.slider); 8473 8474 validateTargetNode(targetNode); 8475 8476 validateProductArray(productArray); 8477 8478 buildProductSlidesHtml(productArray, targetNode, sliderSettings, localSettings.dataProps); 8479 } 8480 }; 8481 8482 return publicMembers; 8483}); 8484 8485redesign.define('coreServices/generateProductPriceObject',['globalModulesHelpers/substituteTMNTParams'], function generateProductPriceObject(substituteTMNTParams) { 8486 'use strict'; 8487 8488 var showDiscount = function showDiscountPercentage(product) { 8489 return product.discountPercentage && parseInt(product.discountPercentage, 10) > 0 && ANF.catalog.data.selectedSort.sortOption === 'discount'; 8490 }; 8491 8492 var getMemberPriceConfig = function getMemberPriceConfig(product) { 8493 var hasRangeMemberPricing = product.highContractPrice > product.lowContractPrice; 8494 var formattedLowMemberPrice = product.lowContractPriceFmt; 8495 var formattedHighMemberPrice = product.highContractPriceFmt; 8496 var memberPriceObj = { 8497 clearanceText: ANF.messaging.loyalty.MEMBER_CLEARANCE, 8498 priceType: 'member', 8499 priceText: { 8500 priceTextType: 'member' 8501 }, 8502 'data-prop': { 8503 aui: 'member-price' 8504 } 8505 }; 8506 8507 if (hasRangeMemberPricing) { 8508 memberPriceObj.description = substituteTMNTParams.substitute(ANF.messaging.loyalty.MEMBER_PRICE_DESC_RANGE, [ 8509 formattedLowMemberPrice, 8510 formattedHighMemberPrice 8511 ]); 8512 memberPriceObj.priceText.formattedPriceText = formattedLowMemberPrice + ' - ' + formattedHighMemberPrice; 8513 } else { 8514 memberPriceObj.description = substituteTMNTParams.substitute(ANF.messaging.loyalty.MEMBER_PRICE_DESC_REG, [formattedLowMemberPrice]); 8515 memberPriceObj.priceText.formattedPriceText = formattedLowMemberPrice; 8516 } 8517 8518 return memberPriceObj; 8519 }; 8520 8521 return function getProductArray(products) { 8522 var productArray = products; 8523 8524 for (var i = 0, len = productArray.length; i < len; i++) { 8525 var product = products[i]; 8526 8527 var clearanceText = product.showClearanceBadge ? ANF.messaging.catalogV2.TMNT.cardClearance.value : null; 8528 8529 var formattedLowListPrice = product.lowListPriceFmt; 8530 var formattedHighListPrice = product.highListPriceFmt; 8531 var formattedLowPrice = product.lowPriceFmt; 8532 var formattedHighPrice = product.highPriceFmt; 8533 8534 var hasRangeListPricing = product.highListPrice > product.lowListPrice; 8535 var hasRangeOfferPricing = product.highPrice > product.lowPrice; 8536 var hasRangePricing = hasRangeListPricing || hasRangeOfferPricing; 8537 var hasDiscountHighPricing = product.highListPrice > product.highPrice; 8538 var hasDiscountLowPricing = product.lowListPrice > product.lowPrice; 8539 var hasDiscountPricing = hasDiscountHighPricing || hasDiscountLowPricing; 8540 8541 var priceType = product.showClearanceBadge ? 'clearance' : ''; 8542 var priceObj = { 8543 priceType: priceType, 8544 clearanceText: clearanceText 8545 }; 8546 8547 var priceObjConfiguration = {}; 8548 8549 if (product.discountPercentage) { 8550 priceObj.discountPercentage = product.discountPercentage.toString(); 8551 } 8552 8553 priceObj.showDiscountPercentage = showDiscount(product); 8554 8555 // Wonky logic to specifically deal with the outlet-pricing case where the product OFFER (low) price can 8556 // actually be higher than the product list (HIGH) price. TODO: Remove this once the underlying issue is resolved. 8557 if (product.lowPrice > product.lowListPrice) { 8558 priceObjConfiguration = { 8559 description: formattedLowPrice, 8560 priceText: [ 8561 { 8562 priceTextType: '', 8563 formattedPriceText: formattedLowPrice 8564 } 8565 ] 8566 }; 8567 } else if (hasRangePricing && !hasDiscountPricing) { 8568 // Range list price 8569 priceObjConfiguration = { 8570 description: 'Between ' + formattedLowListPrice + ' and ' + formattedHighListPrice, 8571 priceText: [ 8572 { 8573 priceTextType: '', 8574 formattedPriceText: formattedLowListPrice + ' - ' + formattedHighListPrice 8575 } 8576 ] 8577 }; 8578 } else if (hasRangePricing && hasDiscountPricing) { 8579 if (!hasRangeListPricing && hasRangeOfferPricing) { 8580 priceObjConfiguration = { 8581 description: 'Was ' + formattedLowListPrice + ', now between ' + formattedLowPrice + ' and ' + formattedHighPrice, 8582 priceText: [ 8583 { 8584 formattedPriceText: formattedLowListPrice, 8585 priceTextType: 'original' 8586 }, 8587 { 8588 formattedPriceText: formattedLowPrice + ' - ' + formattedHighPrice, 8589 priceTextType: 'discount' 8590 } 8591 ] 8592 }; 8593 } else if (hasRangeListPricing && !hasRangeOfferPricing) { 8594 priceObjConfiguration = { 8595 description: 'Was between ' + formattedLowListPrice + ' and ' + formattedHighListPrice + ', now ' + formattedLowPrice, 8596 priceText: [ 8597 { 8598 formattedPriceText: formattedLowListPrice + ' - ' + formattedHighListPrice, 8599 priceTextType: 'original' 8600 }, 8601 { 8602 formattedPriceText: formattedLowPrice, 8603 priceTextType: 'discount' 8604 } 8605 ] 8606 }; 8607 } else { 8608 priceObjConfiguration = { 8609 description: 8610 'Was between ' + 8611 formattedLowListPrice + 8612 ' and ' + 8613 formattedHighListPrice + 8614 ', now between ' + 8615 formattedLowPrice + 8616 ' and ' + 8617 formattedHighPrice, 8618 priceText: [ 8619 { 8620 formattedPriceText: formattedLowListPrice + ' - ' + formattedHighListPrice, 8621 priceTextType: 'original' 8622 }, 8623 { 8624 formattedPriceText: formattedLowPrice + ' - ' + formattedHighPrice, 8625 priceTextType: 'discount' 8626 } 8627 ] 8628 }; 8629 } 8630 } else if (!hasRangePricing && hasDiscountPricing) { 8631 priceObjConfiguration = { 8632 description: 'Was ' + formattedLowListPrice + ', now ' + formattedLowPrice, 8633 priceText: [ 8634 { 8635 formattedPriceText: formattedLowListPrice, 8636 priceTextType: 'original' 8637 }, 8638 { 8639 formattedPriceText: formattedLowPrice, 8640 priceTextType: 'discount' 8641 } 8642 ] 8643 }; 8644 } else { 8645 priceObjConfiguration = { 8646 description: formattedLowListPrice, // this should be lowListPrice !!!!! 8647 priceText: [ 8648 { 8649 formattedPriceText: formattedLowListPrice, 8650 priceTextType: '' 8651 } 8652 ] 8653 }; 8654 } 8655 8656 product.priceObj = $.extend(priceObj, priceObjConfiguration); 8657 8658 /* 8659 display member price only if 8660 1. its gt 0 8661 2. lowContractPrice < lowPrice 8662 3. lowContractPrice < lowListPrice 8663 */ 8664 if ( 8665 ANF.params.storeAttributes.loyalty.memberPricingEnabled && 8666 product.lowContractPrice > 0 && 8667 product.lowContractPrice < product.lowPrice && 8668 product.lowContractPrice < product.lowListPrice 8669 ) { 8670 product.memberPriceObj = $.extend(true, {}, getMemberPriceConfig(product)); 8671 } 8672 8673 if (product && product.productAttrs && product.productAttrs.imageAutoFlip === 'true') { 8674 product.dataImageAutoFlip = 'data-image-auto-flip'; 8675 } 8676 } 8677 8678 return productArray; 8679 }; 8680}); 8681 8682redesign.define('es6Adapter/categoryView',['exports', 'anfUtil/constants'], function categoryViewAdapter(exports, coreConstants) { 8683 'use strict'; 8684 8685 var CONSTANTS = Object.create(Object.prototype, { 8686 REASONS: { 8687 value: Object.create(Object.prototype, { 8688 AJAX_OK: { 8689 value: 1, 8690 message: 'ok' 8691 }, 8692 AJAX_FAIL: { 8693 value: 2, 8694 message: 'fail' 8695 }, 8696 INVALID_PARAMS: { 8697 value: 3,
8698 message: 'invalid params' 8699 } 8700 }) 8701 } 8702 }); 8703 8704 var getApiUrl = function getApiUrl(settings) { 8705 var categoryViewApiUrl = coreConstants.anf('params.storeAttributes.categoryViewApiUrl'); 8706 var catalogId = coreConstants.anf('params.storeAttributes.idList.catalogId'); 8707 var catIdentifierPrefix = coreConstants.anf('params.storeAttributes.catIdentifierPrefix'); 8708 return "".concat(categoryViewApiUrl + (catIdentifierPrefix + settings.commonCatId).trim(), "?catalogId=").concat(catalogId, "&responseFormat=json"); 8709 }; 8710 8711 var performApiRequest = function performApiRequest(settings) { 8712 var deferredObject = new $.Deferred(); 8713 var ajaxSettings; 8714 8715 try { 8716 ajaxSettings = { 8717 contentType: 'application/json', 8718 dataType: 'json', 8719 type: 'GET', 8720 url: getApiUrl(settings) 8721 }; 8722 $.ajax(ajaxSettings).done(function (ajaxData, ajaxTextStatus, ajaxJqXHR) { 8723 deferredObject.resolve({ 8724 ajaxData: ajaxData, 8725 ajaxJqXHR: ajaxJqXHR, 8726 ajaxTextStatus: ajaxTextStatus, 8727 reason: CONSTANTS.REASONS.AJAX_OK, 8728 message: CONSTANTS.REASONS.AJAX_OK.message 8729 }); 8730 }).fail(function (ajaxJqXHR, ajaxTextStatus, ajaxErrorThrown) { 8731 deferredObject.reject({ 8732 ajaxErrorThrown: ajaxErrorThrown, 8733 ajaxJqXHR: ajaxJqXHR, 8734 ajaxTextStatus: ajaxTextStatus, 8735 reason: CONSTANTS.REASONS.AJAX_FAIL, 8736 message: CONSTANTS.REASONS.AJAX_FAIL.message 8737 }); 8738 }); 8739 } catch (_ref) { 8740 var _eMsg = _ref.message; 8741 deferredObject.reject({ 8742 _eMsg: _eMsg, 8743 reason: CONSTANTS.REASONS.INVALID_PARAMS 8744 }); 8745 } 8746 8747 return deferredObject.promise(); 8748 }; 8749 8750 exports.get = function (settings) { 8751 return performApiRequest(settings); 8752 }; 8753 8754 return exports; 8755}); 8756 8757function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } 8758 8759function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 8760 8761function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 8762 8763function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } 8764 8765function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } 8766 8767function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 8768 8769redesign.define('es6HomeModules/marketingProductSlider',['exports', 'globalModules/productSlider', 'coreServices/generateProductPriceObject', 'coreServices/adapter/category', 'es6Adapter/categoryView'], function marketingProductSlider(exports, productSlider, generateProductPriceObject, categoryAPIAdapter, categoryViewAPIAdapter) { 8770 'use strict'; 8771 8772 var NO_CAT_ID = 'No category id found on the target node.'; 8773 var CATEGORY_API_CALL_FAIL = 'Category API call failed.'; 8774 var CATEGORY_VIEW_CALL_FAIL = 'Category View API call failed.'; 8775 var NO_PRODUCTS = 'No product data returned from API.'; 8776 8777 function getProductSliders(parent) { 8778 var allCatDrivenProductSliders = [].concat(_toConsumableArray(parent.querySelectorAll('.js-product-slider[data-categoryid]:not(.slick-initialized)')), _toConsumableArray(parent.querySelectorAll('.js-product-slider[data-cat-identifier]:not(.slick-initialized)'))).reduce(function (acum, element) { 8779 if (element.dataset.categoryid !== '' || element.dataset.catIdentifier !== '') acum.valid.add(element); 8780 if (element.dataset.categoryid === '' && element.dataset.catIdentifier === '') acum.invali
8780d.add(element); 8781 return acum; 8782 }, { 8783 valid: new Set(), 8784 invalid: new Set() 8785 }); 8786 8787 if (allCatDrivenProductSliders.invalid.length) { 8788 // eslint-disable-next-line no-console 8789 allCatDrivenProductSliders.invalid.forEach(function (element) { 8790 return console.error(NO_CAT_ID, element); 8791 }); 8792 } 8793 8794 return _toConsumableArray(allCatDrivenProductSliders.valid); 8795 } 8796 8797 function setSliderWaypoints(sliderTarget) { 8798 return new Waypoint({ 8799 element: sliderTarget, 8800 handler: function handler() { 8801 if (typeof this.destroy === 'function') this.destroy(); 8802 productSliderHandler(this.element).fail(function failure(e) { 8803 // eslint-disable-next-line no-console 8804 console.error(e, sliderTarget); 8805 }); 8806 }, 8807 offset: '180%' 8808 }); 8809 } 8810 8811 function getCategoryId(targetNode) { 8812 var deferredObject = new $.Deferred(); 8813 var commonCatId = targetNode.getAttribute('data-cat-identifier'); 8814 var categoryId = ''; 8815 8816 if (commonCatId) { 8817 categoryViewAPIAdapter.get({ 8818 commonCatId: commonCatId 8819 }).done(function success(data) { 8820 var _data$ajaxData$Catalo, _data$ajaxData, _data$ajaxData$Catalo2, _data$ajaxData$Catalo3, _data$ajaxData$Catalo4, _categoryId; 8821 8822 categoryId = (_data$ajaxData$Catalo = data === null || data === void 0 ? void 0 : (_data$ajaxData = data.ajaxData) === null || _data$ajaxData === void 0 ? void 0 : (_data$ajaxData$Catalo2 = _data$ajaxData.CatalogGroupView) === null || _data$ajaxData$Catalo2 === void 0 ? void 0 : (_data$ajaxData$Catalo3 = _data$ajaxData$Catalo2[0]) === null || _data$ajaxData$Catalo3 === void 0 ? void 0 : (_data$ajaxData$Catalo4 = _data$ajaxData$Catalo3.uniqueID) === null || _data$ajaxData$Catalo4 === void 0 ? void 0 : _data$ajaxData$Catalo4.trim()) !== null && _data$ajaxData$Catalo !== void 0 ? _data$ajaxData$Catalo : ''; 8823 8824 if ((_categoryId = categoryId) !== null && _categoryId !== void 0 && _categoryId.length) { 8825 deferredObject.resolve(categoryId); 8826 } else { 8827 deferredObject.reject(new Error(NO_CAT_ID)); 8828 } 8829 }).fail(function failure() { 8830 deferredObject.reject(new Error(CATEGORY_VIEW_CALL_FAIL)); 8831 }); 8832 } else { 8833 var _categoryId2; 8834 8835 categoryId = targetNode.getAttribute('data-categoryId'); 8836 8837 if ((_categoryId2 = categoryId) !== null && _categoryId2 !== void 0 && _categoryId2.length) { 8838 deferredObject.resolve(categoryId); 8839 } else { 8840 deferredObject.reject(new Error(NO_CAT_ID)); 8841 } 8842 } 8843 8844 return deferredObject.promise(); 8845 } 8846 8847 function productSliderHandler(targetNode) { 8848 var deferredObject = new $.Deferred(); 8849 getCategoryId(targetNode).done(function successCatId(catId) { 8850 categoryAPIAdapter.get({ 8851 categoryId: catId.trim(), 8852 rows: 50 8853 }).done(function success(data) { 8854 if (!(targetNode.classList.contains('.slick-initialized') && !targetNode.querySelector('.slick-initialized'))) { 8855 var _data$ajaxData$produc, _data$ajaxData2; 8856 8857 var productArray = (_data$ajaxData$produc = data === null || data === void 0 ? void 0 : (_data$ajaxData2 = data.ajaxData) === null || _data$ajaxData2 === void 0 ? void 0 : _data$ajaxData2.products) !== null && _data$ajaxData$produc !== void 0 ? _data$ajaxData$produc : []; 8858 8859 if (productArray.length === 0) { 8860 deferredObject.reject(new Error(NO_PRODUCTS)); 8861 return; 8862 } 8863 8864 productArray = generateProductPriceObject(productArray); 8865 8866 try { 8867 productSlider.init(productArray, targetNode, { 8868 image: 'model1' 8869 }); 8870 } catch (ignore) {// do nothing, the throws will handle any errors 8871 } finally { 8872 deferredObject.resolve(); 8873 } 8874 } 8875 }).fail(function fail() { 8876 deferredObject.reject(new Error(CATEGORY_API_CALL_FAIL)); 8877 }); 8878 }).fail(function fail(e) { 8879 deferredObject.reject(e); 8880 }); 8881 return deferredObject.promise(); 8882 } 8883 8884 exports.init = function () { 8885 var parent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document; 8886 getProductSliders(parent).forEach(setSliderWaypoints); 8887 }; 8888 8889 return exports; 8890}); 8891 8892redesign.define('productPartial/pdp_fitTypeAndDescription',['handlebars'], function(Handlebars) { 8893 8894this["ANF"] = this["ANF"] || {}; 8895this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 8896 8897Handlebars.registerPartial("pdp_fitTypeAndDescription", this["ANF"]["templates"]["pdp_fitTypeAndDescription"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 8898 var alias1=container.lambda, alias2=container.escapeExpression; 8899 8900 return "<div>\n <h3 class=\"product-attrs__shown-in\">\n <strong>\n " 8901 + alias2(alias1((depth0 != null ? depth0.fitTMNT : depth0), depth0)) 8902 + ":\n </strong>
8902\n <span class=\"selected-swatch__label\">\n " 8903 + alias2(alias1((depth0 != null ? depth0.fitType : depth0), depth0)) 8904 + "\n </span>\n </h3>\n <p> " 8905 + alias2(alias1((depth0 != null ? depth0.fitDescription : depth0), depth0)) 8906 + " </p>\n</div>\n"; 8907},"useData":true})); 8908 8909return this["ANF"]; 8910 8911}); 8912redesign.define('productModules/product/buyGridAccordions/productSizeFitAccordion',[ 8913 'productModules/helpers/Utils', 8914 'productServices/product/productReviewService', 8915 'anfcore/components/tooltip', 8916 'productModules/helpers/garmentMeasurements', 8917 'productPartial/pdp_sizeFitAccordionContent', 8918 'productPartial/pdp_fitRecommendation', 8919 'productPartial/pdp_modelSpecs', 8920 'globalServices/marketing/eSpotContentProvider', 8921 'anfcore/components/modal', 8922 'coreServices/personalizedContent', 8923 'anfUtil/constants', 8924 'anfUtil/testFramework', 8925 'coreServices/userPreference', 8926 'es6HomeModules/marketingProductSlider', 8927 'productPartial/pdp_fitTypeAndDescription', 8928 'globalModulesHelpers/debounce', 8929 'productPartial/pdp_measurements' 8930], 8931function ProductSizeFitAccordion( 8932 Utils, 8933 reviewService, 8934 dsTooltip, 8935 garmentMeasurements, 8936 productPartialPdpSizeFitAccordionContent, 8937 productPartialPdpFitRecommendation, 8938 productPartialPdpModelSpecs, 8939 eSpotContentProvider, 8940 modal, 8941 personalizedContentAPI, 8942 coreConstants, 8943 test, 8944 userPreferenceHelper, 8945 marketingProductSlider, 8946 productPartialPdpFitTypeAndDescription, 8947 debounceService, 8948 productPartialLensMeasurementsAccordionContent 8949) { 8950 'use strict'; 8951 8952 var messageBus = $(ANF); 8953 8954 function reformSizeFitAccordion($productSection) { 8955 var anfObject = Utils.getANFObject(); 8956 var productId = $productSection.data('productid'); 8957 var productData = Utils.getProductData(productId); 8958 var collection = productData.collection; 8959 var sizeFitInformation = $productSection.find('.size-fit-information'); 8960 8961 var accordionData = {}; 8962 var measurementData = Utils.isEyewearProduct(productData.productAttrs) ? 8963 garmentMeasurements.getEyewearMeasurementData(productId, anfObject) : 8964 garmentMeasurements.getGarmentMeasurementData(productId, anfObject, $productSection); 8965 8966 if (measurementData && measurementData.isEyeware) { 8967 renderMeasurementsAccordion(measurementData, $productSection); 8968 } else { 8969 accordionData.garmentData = measurementData; 8970 accordionData.hasGarmentMeasurements = accordionData.garmentData.measurements.length > 0 && !Utils.isJeansProduct(productId); 8971 8972 accordionData.isQuickviewOpened = Utils.isQuickviewOpened; 8973 accordionData.fitRecommendation = getFitRecommendation(anfObject, collection); 8974 accordionData.hasFitRecommendation = !!accordionData.fitRecommendation; 8975 accordionData.hasModelSpecs = hasModelSpecs(productData); 8976 if (hasModelSpecs(productData)) { 8977 accordionData.modelSpecs = Utils.getModelSpecs(productId); 8978 } 8979 8980 if ($productSection.hasClass('size-fit-desc-enabled')) { 8981 var fitTypes = productData.productAttrsComplex['Details-6233']; 8982 if (fitTypes && fitTypes.values) { 8983 var fitDetails = fitTypes.values.reduce(function getFitDetails(previous, current) { 8984 if (current.value === 'Curvy' || current.value === 'Classic' || current.value === 'Curvy Love') { 8985 return current.value; 8986 } 8987 return previous; 8988 }, ''); 8989 var fitTypeTmntMapping = { 8990 'Curvy': 'PDP_CURVE_FIT', 8991 'Curvy Love': 'PDP_CURVE_FIT', 8992 'Classic': 'PDP_CLASSIC_FIT' 8993 }; 8994 if (fitDetails) { 8995 accordionData.fitType = fitDetails; 8996 accordionData.fitHeaderTMNT = ANF.messaging.product[fitTypeTmntMapping[fitDetails]]; 8997 accordionData.fitDescription = productData.productAttrs.FitDescription; 8998 accordionData.fitTMNT = ANF.messaging.product.PDP_FIT; 8999 sizeFitInformation.html(productPartialPdpFitTypeAndDescription.templates.pdp_fitTypeAndDescription(accordionData)); 9000 } 9001 } 9002 accordionData.riseTMNT = ANF.messaging.product.PDP_RISE_FIT; 9003 accordionData.lengthTMNT = ANF.messaging.product.PDP_LENGTH_FIT; 9004 accordionData.bottomRiseCode = productData.productAttrs.BottomsRiseCode; 9005 accordionData.bottomLengthCode = productData.productAttrs.BottomsLengthCode; 9006 accordionData.style = productData.productAttrs.Style; 9007 accordionData.styleDescription = productData.productAttrs.StyleDescription; 9008 accordionData.hasCurvyOrClassicFit = accordionData.fitType || accordionData.fitDescription || accordionData.bottoms
9008RiseCode || accordionData.bottomsLengthCode || accordionData.style; 9009 if (accordionData.hasCurvyOrClassicFit) { 9010 var sizeFitAccordion = $productSection.find('.size-fit__accordion'); 9011 sizeFitAccordion.find('button').attr('aria-expanded', 'true'); 9012 sizeFitAccordion.find('.js-accordion-panel').attr('aria-hidden', false).css('max-height', 'max-content'); 9013 } 9014 } 9015 var debounceInstance = debounceService.debounce(makeCompareFitLink, 500); 9016 debounceInstance(collection, productId); 9017 } 9018 9019 function makeCompareFitLink(collectionDetails, productIdDetails) { 9020 getCompareFitTemplate(collectionDetails, productIdDetails, accordionData, $productSection).done(function _updateSizeFitAccordion() { 9021 accordionData.hasFitInfo = accordionData.hasFitRecommendation || accordionData.hasModelSpecs || accordionData.compareFitLinkEspot || accordionData.hasCurvyOrClassicFit; 9022 var linkText = '<a href="#rating-and-reviews"> ' + accordionData.fitRecommendation + '</a>'; 9023 9024 if (accordionData.isQuickviewOpened) { 9025 accordionData.contextualFitFullTMNT = ANF.messaging.product.PDP_NEW_MOST_RECOMMEND.replace('{0}', accordionData.fitRecommendation); 9026 } else { 9027 accordionData.contextualFitFullTMNT = ANF.messaging.product.PDP_NEW_MOST_RECOMMEND.replace('{0}', linkText); 9028 } 9029 9030 accordionData.displayHR = accordionData.hasFitInfo && accordionData.hasGarmentMeasurements; 9031 updateSizeFitAccordionDisplay(accordionData, $productSection); 9032 modal.default().init(); 9033 $productSection 9034 .has('.size-fit__accordion .js-product-slider') 9035 .each(function applyMarketingProductSlider(index, element) { 9036 marketingProductSlider.init(element); 9037 }); 9038 }); 9039 } 9040 } 9041 9042 function updateSizeFitAccordion(e, $selectedSwatch, $productSection) { 9043 reformSizeFitAccordion($productSection); 9044 } 9045 9046 function getContentAPISettings() { 9047 return { 9048 stylesInResponse: false, 9049 locale: coreConstants.anf('params.storeAttributes.api.aem').locale, 9050 singleResponse: true, 9051 dataType: 'json', 9052 adapterSettings: { 9053 aemTimeout: coreConstants.anf('params.storeAttributes.api.aem').timeout.apiBin, 9054 aemHTMLTimeout: coreConstants.anf('params.storeAttributes.api.aem').timeout.content, 9055 isSingleResponse: true 9056 } 9057 }; 9058 } 9059 9060 function getISODate(value) { 9061 try { 9062 return new Date(value).toISOString(); 9063 } catch (e) { 9064 return null; 9065 } 9066 } 9067
9068 function getRequestParams() { 9069 var isStorePreview = coreConstants.anf('params.storeAttributes.api.aem.isStorePreview'); 9070 var espotContentSettings = getContentAPISettings(); 9071 var hasSiteExperienceByShipToCountry = coreConstants.anf('params.storeAttributes.userPreference.siteExperience'); 9072 var uPrefCookieValue = userPreferenceHelper.read({cookieName: 'uPref'}); 9073 var countryParams; 9074 9075 if (isStorePreview) { 9076 $.extend(true, espotContentSettings, getStorePreviewParams()); 9077 } 9078 9079 if (hasSiteExperienceByShipToCountry) { 9080 if (test.string.not.empty(coreConstants.anf('params.storeAttributes.countryFromIP'))) { 9081 countryParams = { 9082 countryCode: coreConstants.anf('params.storeAttributes.countryFromIP') 9083 }; 9084 } else if (test.object.containsKey(uPrefCookieValue, 'shipTo', false)) { 9085 countryParams = { 9086 countryCode: uPrefCookieValue.shipTo 9087 }; 9088 } else { 9089 countryParams = { 9090 countryCode: 'US' 9091 }; 9092 } 9093 espotContentSettings.countryCode = countryParams.countryCode; 9094 } 9095 9096 return espotContentSettings; 9097 } 9098 9099 function getStorePreviewParams() { 9100 var storePreviewParams = null; 9101 var previewFrameWrapper = parent.document.getElementById('previewFrameWrapper'); 9102 9103 if (previewFrameWrapper && previewFrameWrapper.dataset) { 9104 storePreviewParams = { 9105 previewStart: getISODate(previewFrameWrapper.dataset.start), 9106 previewMode: true 9107 }; 9108 } 9109 return storePreviewParams; 9110 } 9111 9112 function getCompareFitAEMContent(collection, settings, accordionData, $productSection) { 9113 var response = new $.Deferred(); 9114 var compareFitLinkEspotName = 'pdp_sizefitaccordion'; 9115 var compareFitLinkEspot = ''; 9116 9117 personalizedContentAPI.get(settings) 9118 .done(function _successFunction(results) { 9119 if (results.reason === personalizedContentAPI.reasons.AJAX_OK) { 9120 results.data.forEach(function getCompareFitsAccordion(espotContent) { 9121 if (test.object.containsKey(espotContent, 'espotId', false) && espotContent.espotId === compareFitLinkEspotName) { 9122 espotContent.pages.forEach(function concatContent(page) { 9123 compareFitLinkEspot += page.htmlContent; 9124 }); 9125 } 9126 }); 9127 } 9128 ANF.product.data[collection].compareOurFitContent += compareFitLinkEspot; 9129 ANF.product.data[collection].hasFetchedCompareOurFitContent = true; 9130 9131 response.resolve(compareFitLinkEspot); 9132 Object.assign(accordionData, {compareFitLinkEspot: compareFitLinkEspot}); 9133 updateSizeFitAccordionDisplay(accordionData, $productSection); 9134 }) 9135 .fail(function _failFunction() { 9136 response.reject(); 9137 }); 9138 9139 return response.promise(); 9140 } 9141 9142 function getCompareFitCMCContent(collection, productId) { 9143 var response = new $.Deferred(); 9144 var compareFitLinkEspot = ''; 9145 var espotSettings = [{ 9146 key: 'productId', 9147 value: productId 9148 }, { 9149 key: 'page', 9150 value: 'ProductDisplay' 9151 }]; 9152 eSpotContentProvider.fetch(['Product_CompareFitLink'], espotSettings).done(function _successFunction(content) { 9153 if (content[0]) { 9154 for (var i = 0; i < content[0].content.length; i++) { 9155 compareFitLinkEspot += content[0].content[i].text; 9156 } 9157 } 9158 ANF.product.data[collection].compareOurFitContent = compareFitLinkEspot; 9159 ANF.product.data[collection].hasFetchedCompareOurFitContent = true; 9160 response.resolve(compareFitLinkEspot); 9161 }).fail(function _failFunction() { 9162 response.resolve(''); 9163 }); 9164 9165 return response.promise(); 9166 } 9167 9168 function getEspotIds(productId) { 9169 return [productCatalog[productId].productPartNum, 'ALL_KIC_ALL_CTGY', coreConstants.anf('catalog.data.espotIdentifier')]; 9170 } 9171 9172 function getCompareFitTemplate(collection, productId, accordionData, $productSection) { 9173 var response = new $.Deferred(); 9174 var compareFitLinkEspot = ''; 9175 var settings; 9176 var espots; 9177 if (ANF.product.data[collection].hasFetchedCompareOurFitContent) { 9178 compareFitLinkEspot = ANF.product.data[collection].compareOurFitContent; 9179 response.resolve(compareFitLinkEspot); 9180 } else if (window.digitalData.get('flag.aemCompareFits', false)) { 9181 espots = getEspotIds(productId);
9182 settings = getRequestParams(); 9183 ANF.product.data[collection].compareOurFitContent = ''; 9184 $.when.apply($, espots.map(function fetchESpotContent(espotId) { 9185 settings.pageId = espotId; 9186 return getCompareFitAEMContent(collection, settings, accordionData, $productSection); 9187 })).always(function getAllEspotContent() { 9188 response.resolve(ANF.product.data[collection].compareOurFitContent); 9189 }); 9190 } else { 9191 return getCompareFitCMCContent(collection, productId); 9192 } 9193 9194 return response.promise(); 9195 } 9196 9197 function getFitRecommendation(anfObject, collection) { 9198 var reviewData = {}; 9199 var review = anfObject.product.data[collection] && anfObject.product.data[collection].reviews; 9200 var fitRecommendation; 9201 9202 if (review) { 9203 reviewData = reviewService.buildReviewSummaryModel(review); 9204 } 9205 9206 if (reviewData && reviewData.showFitRecommendation && reviewData.fitRecommendation) { 9207 fitRecommendation = reviewData.fitRecommendation; 9208 } 9209 9210 return fitRecommendation; 9211 } 9212 9213 function hasModelSpecs(productData) { 9214 var productAttrs = productData.productAttrs; 9215 return (productAttrs.OFPModelHeight && productAttrs.OFPModelSize); 9216 } 9217 9218 function updateSizeFitAccordionDisplay(accordionData, $productSection) { 9219 var $sizeFitAccordion = $productSection.find('.size-fit__accordion'); 9220 var $accordionContainer = $sizeFitAccordion.find('.size-fit-accordion__container'); 9221 9222 if (accordionData.fitRecommendation) { 9223 var $productSizeSectionContainer = $productSection.find('.product-size-section__container'); 9224 $productSizeSectionContainer.html(productPartialPdpFitRecommendation.templates.pdp_fitRecommendation(accordionData)); 9225 } 9226 9227 // hide and exit if shouldShowSizeFitAccordion == false 9228 if (!shouldShowSizeFitAccordion(accordionData)) { 9229 $sizeFitAccordion.hide(); 9230 return; 9231 } 9232 $accordionContainer.html(productPartialPdpSizeFitAccordionContent.templates.pdp_sizeFitAccordionContent(accordionData)); 9233 $sizeFitAccordion.show(); 9234 9235 // Accordion calcs a height on open, this fixes the height issue of updating content within that already open accordion 9236 $productSection.find('.size-fit__accordion .js-accordion-panel').css('max-height', ''); 9237 9238 if (accordionData.hasGarmentMeasurements) { 9239 // re-init the tooltips since they are injected post load 9240 dsTooltip.default().init(); 9241 } 9242 } 9243 9244 function shouldShowSizeFitAccordion(accordionData) { 9245 return accordionData.hasGarmentMeasurements || 9246 accordionData.hasFitInfo; 9247 } 9248 9249 function onModelChanged(e, eventData) { 9250 var modelSpecs = Utils.getModelSpecs(eventData.productId); 9251 var $container = eventData.$productSection.find('.model-specs-container'); 9252 $container.html(productPartialPdpModelSpecs.templates.pdp_modelSpecs(modelSpecs)); 9253 } 9254 9255 function triggerBTest() { 9256 var $productSection = $('.product-page-v2-modules').not('.quickview-preload'); 9257 $productSection.addClass('size-fit-desc-enabled'); 9258 var productSection = $productSection; 9259 if ($productSection.length > 1) { 9260 productSection = $productSection.not('.js-product-page'); 9261 } 9262 reformSizeFitAccordion(productSection); 9263 } 9264 9265 function renderMeasurementsAccordion(measurementData, $productSection) { 9266 var $measurementsAccordion = $productSection.find('.measurements__accordion'); 9267 var $measurementsAccordionContainer = $measurementsAccordion.find('.measurements-accordion__container'); 9268 9269 if (!measurementData && !measurementData.isEyeware) { 9270 $measurementsAccordion.hide(); 9271 return; 9272 } 9273 $measurementsAccordion.show(); 9274 $measurementsAccordionContainer.html(productPartialLensMeasurementsAccordionContent.templates.pdp_measurements(measurementData)); 9275 9276 dsTooltip.default().init(); 9277 } 9278 9279 var publicMembers = { 9280 listen: function listen() { 9281 messageBus.on('productPageInfo:swatchOnLoad productPageInfo:swatchSelected', updateSizeFitAccordion); 9282 messageBus.on('productPageMainImages:modelSelected', onModelChanged); 9283 messageBus.on('styleFitDesc:init', triggerBTest); 9284 } 9285 }; 9286 9287 return publicMembers; 9288}); 9289 9290redesign.define('productModules/product/sizeGuide/sizeGuideCharts',[
9291 'globalModulesHelpers/debounce', 9292 'productModules/helpers/curry', 9293 'productServices/product/template' 9294], function SizeGuideCharts(debounce, curry, template) { 9295 'use strict'; 9296 9297 var sizeGuideTableScrollSelector = '.size-guide__table-scroll'; 9298 var sizeGuideTableSelector = '.size-guide__guide'; 9299 var hideShadowClass = 'size-guide__table-shadow--hide'; 9300 var dropShadowLeftSelector = '.size-guide__table-shadow-left'; 9301 var dropShadowRightSelector = '.size-guide__table-shadow-right'; 9302 var sizeGuidePrimarySelector = '.size-guide__primary-column'; 9303 var sizeGuideTableRegionSelector = '.size-guide__country-column'; 9304 var sizeGuideShowInchesClass = 'size-guide__show-inches'; 9305 var sizeGuideShowCentimeterClass = 'size-guide__show-cm'; 9306 var selectedClass = 'selected'; 9307 9308 function renderSizeCharts($container, constants, sizeChartData, defaultRegion) { 9309 var defaultUnitClass = defaultRegion === 'usa' ? sizeGuideShowInchesClass : sizeGuideShowCentimeterClass; 9310 9311 var tables = template.renderTemplate('sizeGuideCharts', { 9312 sizeCharts: sizeChartData, 9313 defaultUnitClass: defaultUnitClass, 9314 defaultRegion: defaultRegion, 9315 sizeChartHeader: constants.sizeChartHeader 9316 }); 9317 9318 $container.html(tables); 9319 9320 // curry this with an extra argument so debounce calls it with zero args and the container as context 9321 var curriedRenderShadows = curry(renderTableScrollShadows, 2)($container); 9322 var debounced = debounce.debounce(curriedRenderShadows, 250); 9323 $container.find(sizeGuideTableScrollSelector).on('scroll', debounced); 9324 } 9325 9326 function renderTableScrollShadows($container) { 9327 $container.find(sizeGuideTableScrollSelector).each(function setScrollShadows(_idx, tableScroll) { 9328 var maxScrollLeft = tableScroll.scrollWidth - tableScroll.clientWidth; 9329 9330 // only when has scrolling 9331 if (tableScroll.scrollWidth > 375 && maxScrollLeft !== 0) { 9332 var $currentTableScroll = $(tableScroll); 9333 var $dropShadowLeft = $currentTableScroll.find(dropShadowLeftSelector); 9334 var $dropShadowRight = $currentTableScroll.find(dropShadowRightSelector); 9335 9336 var hideLeftShadow = tableScroll.scrollLeft === 0; 9337 var hideRightShadow = tableScroll.scrollLeft === maxScrollLeft; 9338 9339 // account for width of first column 9340 var $leftHeaders = $currentTableScroll.find(sizeGuidePrimarySelector); 9341 $dropShadowLeft.css('left', $leftHeaders[0].clientWidth); 9342 9343 $dropShadowLeft.toggleClass(hideShadowClass, hideLeftShadow); 9344 $dropShadowRight.toggleClass(hideShadowClass, hideRightShadow); 9345 } 9346 }); 9347 } 9348 9349 function toggleTableUnit($container, unit) { 9350 var $table = $container.find(sizeGuideTableSelector); 9351 9352 switch (unit) { 9353 case 'inches': 9354 $table 9355 .removeClass(sizeGuideShowCentimeterClass) 9356 .addClass(sizeGuideShowInchesClass); 9357 break; 9358 case 'centimeters': 9359 $table 9360 .removeClass(sizeGuideShowInchesClass) 9361 .addClass(sizeGuideShowCentimeterClass); 9362 break; 9363 default: 9364 $table 9365 .removeClass(sizeGuideShowCentimeterClass) 9366 .addClass(sizeGuideShowInchesClass); 9367 } 9368 9369 renderTableScrollShadows($container); 9370 } 9371 9372 function changeTableRegion($container, region, unit) { 9373 var $regionColumns = $container.find(sizeGuideTableRegionSelector); 9374 var regionSelectedClass = '.size-guide__country-' + region; 9375 9376 $regionColumns 9377 .removeClass(selectedClass) 9378 .filter(regionSelectedClass) 9379 .addClass(selectedClass); 9380 9381 toggleTableUnit($container, unit); 9382 } 9383 9384 function addAltBackgroundFlag(sizeChartData) { 9385 var enrichedSizeChartData = $.extend({}, sizeChartData); 9386 9387 $.each(sizeChartData, function sizeChartLoop(e, chart) { 9388 $.each(chart.sizes, function altColoringCalculation(i) { 9389 if (i % 2) { 9390 enrichedSizeChartData[e].sizes[i].hasAltBackground = true; 9391 } 9392 }); 9393 }); 9394 9395 return enrichedSizeChartData; 9396 } 9397 9398 function init($container, constants, sizeChartData) { 9399 var region = constants.sizeGuideDefaultRegion.toLowerCase(); 9400
9401 var enrichedSizeChartData = addAltBackgroundFlag(sizeChartData); 9402 renderSizeCharts($container, constants, enrichedSizeChartData, region); 9403 } 9404 9405 var publicMembers = { 9406 init: init, 9407 changeTableRegion: changeTableRegion, 9408 toggleTableUnit: toggleTableUnit 9409 }; 9410 9411 return publicMembers; 9412}); 9413 9414redesign.define('productModules/product/sizeGuide/sizeGuideChartControls',[ 9415 'productServices/product/template', 9416 'productModules/helpers/Utils', 9417 'productModules/product/sizeGuide/sizeGuideCharts' 9418], function SizeGuideChartControls(template, Utils, SizeGuideCharts) { 9419 'use strict'; 9420 9421 var sizeGuideParentSelector = '.size-guide'; 9422 var sizeGuideRegionSelector = '.size-guide-region-selector select'; 9423 var sizeGuideRegionDropDownSelector = '.size-guide__region-selector'; 9424 var unitTabContainerSelector = '.size-guide__unit-toggle-wrapper .toggle-radio'; 9425 var sizeGuideUnitToggleWrapperSelector = '.size-guide__unit-toggle-wrapper'; 9426 9427 // Treat this as immutable -- once data for a productId is set on init, it should not be modified 9428 var regionsByProductId = {}; 9429 9430 function bindEvents() { 9431 $(document).on('change', unitTabContainerSelector, handleUnitToggleChange); 9432 $(document).on('change', sizeGuideRegionSelector, handleRegionChange); 9433 } 9434 9435 function qvModifier() { 9436 return Utils.isQuickviewOpened ? '_qv' : ''; 9437 } 9438 9439 function handleUnitToggleChange(event) { 9440 var $target = $(event.currentTarget); 9441 var unitClicked = $target.val(); 9442 var $sizeGuideWrapper = $target.closest(sizeGuideParentSelector); 9443 9444 SizeGuideCharts.toggleTableUnit($sizeGuideWrapper, unitClicked); 9445 } 9446 9447 function handleRegionChange(event) { 9448 var $selectedRegion = $(event.target); 9449 9450 var region = $selectedRegion.val(); 9451 var unit = region.toLowerCase() === 'usa' ? 'inches' : 'centimeters'; 9452 9453 var $regionSelectorDropDown = $selectedRegion.closest(sizeGuideRegionDropDownSelector); 9454 var $unitToggle = $regionSelectorDropDown.siblings(sizeGuideUnitToggleWrapperSelector); 9455 var $sizeGuideWrapper = $regionSelectorDropDown.closest(sizeGuideParentSelector); 9456 9457 $unitToggle.find('input[value="' + unit + '"]').prop('checked', true); 9458 9459 SizeGuideCharts.changeTableRegion($sizeGuideWrapper, region, unit); 9460 } 9461 9462 // This is mostly a single use func, parses a messy size guide json response and gets the available regions 9463 function findAvailableRegions(data, constants) { 9464 // Gotta flatten out all these nested arrays so we can reduce them 9465 var types = data.reduce(function getSizes(acc, table) { 9466 return table.sizes.reduce(function getTypes(_callback, size) { 9467 return acc.concat(size.types); 9468 }, []); 9469 }, []); 9470 9471 // Reduce down using an object to merge duplicates 9472 var regions = types.reduce(function mergeCountryProps(acc, type) { 9473 if (type.country !== '') { 9474 acc[type.country] = true; 9475 } 9476 9477 return acc; 9478 }, {}); 9479 9480 // add default region 9481 regions[constants.sizeGuideDefaultRegion] = true; 9482 9483 return Object.keys(regions); 9484 } 9485 9486 function buildRegionSelectorTemplateModel(productId, regions, constants) { 9487 var options = regions.map(function formatRegionForTemplate(region) { 9488 return { 9489 title: region, 9490 value: region 9491 }; 9492 }); 9493 9494 return { 9495 id: 'size-guide-region-selector-' + productId + qvModifier(), 9496 productId: productId, 9497 productBrand: Utils.getProductData(productId).productAttrs.brand, 9498 classList: 'size-guide-region-selector', 9499 label: constants.sizeGuideRegionSelectorAriaLabel, 9500 value: constants.sizeGuideDefaultRegion, 9501 dataProperty: 'SIZE_GUIDE_REGION_LIST_ARIA_LABEL', 9502 options: options 9503 }; 9504 } 9505 9506 function buildUnitToggleTemplateModel(productId, constants) { 9507 var defaultInches = constants.sizeGuideDefaultRegion.toLowerCase() === 'usa'; 9508 9509 return { 9510 productId: productId, 9511 productBrand: Utils.getProductData(productId).productAttrs.brand, 9512 classList: 'size-guide__unit-toggle', 9513 name: 'size-guide-toggle-' + productId + qvModifier(), 9514 toggleLabel: constants.tmntSizeGuideUnitToggleLabel, 9515 toggleBlock: [ 9516 { 9517 'value': 'inches', 9518 'toggle-text': constants.tmntSizeGuideInch, 9519 'id': 'size-guide__toggle-inch-' + productId + qvModifier(), 9520 'checked': defaultInches 9521 }, 9522 { 9523 'value': 'centimeters', 9524 'toggle-text': constants.tmntSizeGuideCm, 9525 'id': 'size-guide__toggle-cm-' + productId + qvModifier(), 9526 'checked': !defaultInches 9527 } 9528 ] 9529 }; 9530 } 9531 9532 function renderChartControls($container, constants, productId, regions) { 9533 var unitToggle = template.renderTemplate('sizeGuideUnitToggle', buildUnitToggleTemplateModel(productId, constants)); 9534 var regionSelector = template.renderTemplate('sizeGuideRegionSelector', buildRegionSelectorTemplateModel(productId, regions, constants)); 9535 9536 $container.html([regionSelector, unitToggle]); 9537 } 9538 9539 function init($container, constants, productId, data) { 9540 if (!!data) { 9541 regionsByProductId[productId] = findAvailableRegions(data[productId], constants); 9542 } 9543 9544 renderChartControls($container, constants, productId, regionsByProductId[productId]); 9545 } 9546 9547 9548 var publicMembers = { 9549 init: init, 9550 bindEvents: bindEvents 9551 }; 9552 9553 return publicMembers; 9554}); 9555 9556redesign.define('productModules/product/sizeGuide/productSizeGuide',[ 9557 'productModules/helpers/Utils', 9558 'productModules/product/sizeGuide/sizeGuideChartControls', 9559 'productModules/product/sizeGuide/sizeGuideCharts', 9560 'anfcore/components/tabs', 9561 'anfcore/components/modal', 9562 'anfcore/helpers/scope' 9563], function ProductSizeGuide( 9564 Utils, 9565 SizeGuideChartControls, 9566 SizeGuideCharts, 9567 tabs, 9568 modal, 9569 scope 9570) { 9571 'use strict'; 9572 9573 var sizeGuideLinkSelector = '.details__link-size-chart, .product-page__info-size-chart, .size-fit__accordion .view-size-fit-btn'; 9574 var sizeGuideTableContainerSelector = '.size-guide__guide-wrapper'; 9575 var sizeGuideControlsContainerSelector = '.size-guide__cta-wrapper'; 9576 9577 var sizeChartsData = {}; 9578 9579 // have to bind utils here bc utils.getvalue uses 'this' 9580 var getInANF = Utils.getValueFromObject.bind(Utils, ANF); 9581 9582 var constants = { 9583 apiURLRoot: getInANF('params.storeAttributes.apiURLRoot', ''), 9584 sizeGuideDefaultRegion: getInANF('params.storeAttributes.catalogV2.sizeGuideDefaultRegion', ''), 9585 sizeGuideRegionSelectorAriaLabel: getInANF('messaging.product.TMNT.SIZE_GUIDE_REGION_SELECTOR_ARIA_LABEL.value', ''), 9586 tmntSizeChartHeader: getInANF('messaging.product.TMNT.SIZE_CHART_HEADER.value', ''), 9587 tmntSizeGuideUnitToggleLabel: getInANF('messaging.product.TMNT.SIZE_GUIDE_UNIT_TOGGLE_LABEL.value', ''), 9588 tmntSizeGuideInch: getInANF('messaging.product.TMNT.SIZE_GUIDE_INCH.value', ''), 9589 tmntSizeGuideCm: getInANF('messaging.product.TMNT.SIZE_GUIDE_CM.value', '') 9590 }; 9591 9592 function sizeGuideClickHandler(event) { 9593 // keep the event executed only on the <button></button> 9594 event.stopPropagation(); 9595 9596 var $currentTarget = $(event.currentTarget); 9597 var $productSection = $currentTarget.closest('.product-page-v2-modules'); 9598 var productId = $productSection.data('productid'); 9599 9600 var sizeChartName = Utils.getProductData(productId).sizeChartName; 9601 9602 var $tableContainer = $productSection.find(sizeGuideTableContainerSelector); 9603 var $controlsContainer = $productSection.find(sizeGuideControlsContainerSelector); 9604 9605 if (!sizeChartsData[productId] || !sizeChartsData[productId].length) { 9606 $.get(ANF.params.storeAttributes.apiURLRoot + '/product/sizeguide/' + sizeChartName) 9607 .done(function handleSizeChartData(data) { 9608 sizeChartsData[productId] = data.sizeguides[0].sizecharts; 9609 9610 SizeGuideCharts.init($tableContainer, constants, sizeChartsData[productId]); 9611 SizeGuideChartControls.init($controlsContainer, constants, productId, sizeChartsData); 9612 }); 9613 } else { 9614 SizeGuideCharts.init($tableContainer, constants, sizeChartsData[productId]); 9615 SizeGuideChartControls.init($controlsContainer, constants, productId); 9616 } 9617 9618 // With the size guide now in QV the ds components we init on page load need to be re-inited() so that the beautiful tabbies work 9619 // below I set the scope to '.scope-1892' because the scope is being set too small on other pages on the site 9620 // Since PDP (Qv, Edit) is pretty much on every page we have to make sure the components init properly.
9621 if (Utils.isQuickviewOpened) { 9622 scope.default.setScope('.scope-1892'); 9623 tabs.default().init(); 9624 } 9625 modal.default().init(); 9626 } 9627 9628 var publicMembers = { 9629 bindEvents: function bindEvents() { 9630 $(document).on('click', sizeGuideLinkSelector, sizeGuideClickHandler); 9631 } 9632 }; 9633 9634 return publicMembers; 9635}); 9636 9637redesign.define('productModules/product/sizeGuide/sizeGuideHeader',[ 9638 'productModules/helpers/Utils', 9639 'coreModulesHelpers/StringUtils', 9640 'productServices/product/template', 9641 'coreClass/ANFImage', 9642 'productModules/helpers/garmentMeasurements', 9643 'anfcore/components/tooltip' 9644], function SizeGuideHeader(Utils, StringUtils, template, ANFImage, garmentMeasurements, dsTooltip) { 9645 'use strict'; 9646 9647 var productSectionSelector = '.product-page-v2-modules'; 9648 var infoPanelSelector = '.size-guide-info-panel'; 9649 9650 var sizeGuideRecommendedFitSelector = '.size-guide-info-panel .size-guide-fit-most-reviewers-say'; 9651 var reviewRecommendedFitSelector = '.size-fit-accordion__container .size-fit-reviewer-recommendation'; 9652 var detailsHeightSelector = '.model-specs__height-measurement'; 9653 var detailsSizeSelector = '.model-specs__size-measurement'; 9654 var sizeGuideImagesSelector = '.size-guide-info-panel .size-guide-images'; 9655 var garmentMeasurementsSelector = '.garment-measurements'; 9656 9657 function gatherImagesToDisplay(selectedModel, imageObject) { 9658 var imagesToDisplay = []; 9659 if (selectedModel.length >= 3) { 9660 imagesToDisplay.push(selectedModel[0], selectedModel[2]); 9661 } else if (selectedModel.length >= 1 && imageObject.prod.length >= 1) { 9662 imagesToDisplay.push(selectedModel[0], imageObject.prod[0]); 9663 } else { 9664 imagesToDisplay.push(imageObject.prod[0]); 9665 } 9666 9667 return imagesToDisplay; 9668 } 9669 9670 function imageBannerErrorHandler(e) { 9671 var $failedImage = $(e.target); 9672 var hasFixedErrorBefore = $failedImage.closest('.image-fail-fix').length > 0; 9673 9674 if (hasFixedErrorBefore) { 9675 $failedImage.parent().remove(); 9676 } else { 9677 var imageIdParts = $failedImage.data('image-id').split('_'); 9678 9679 // hard coding the prod1 image type here, since Image data is stale/broken at this point 9680 var imageId = imageIdParts[0] + '_' + imageIdParts[1] + '_prod1'; 9681 9682 var fallbackSrc = ANFImage.instance() 9683 .setImageName(imageId) 9684 .setWidth(800) 9685 .url; 9686 9687 $failedImage.attr('src', fallbackSrc); 9688 9689 $failedImage.closest('.size-guide-images').addClass('image-fail-fix'); 9690 } 9691 } 9692 9693 var publicMembers = { 9694 bindEvents: function bindEvents() { 9695 $(document).on('dsModal:Open', '[class*="details__size-guide-modal"]', publicMembers.handleSizeGuideOpen); 9696 }, 9697 9698 handleSizeGuideOpen: function handleSizeGuideOpen(event) { 9699 var $productSection = $(event.currentTarget).closest(productSectionSelector); 9700 var $infoPanel = $productSection.find(infoPanelSelector); 9701 var productId = $productSection.data('productid'); 9702 9703 publicMembers.setSizeGuideFitRecommendation($productSection); 9704 publicMembers.sizeGuideImageHandler($productSection, productId); 9705 publicMembers.setSizeGuideInfoPanel($infoPanel, productId); 9706 publicMembers.setGarmentMeasurements(productId, $productSection, $infoPanel); 9707 dsTooltip.default().init(); 9708 }, 9709 9710 setSizeGuideInfoPanel: function setSizeGuideInfoPanel($infoPanel, productId) { 9711 var productData = Utils.getProductData(productId); 9712 // Related with from product/base/js/modules/product/productDetails.js 9713 var defaultModelStr = window.selectedModelSegment || Utils.getValueFromObject(productData, 'productAttrs.ModelData.defaultModel', null); 9714 var selectedModel = defaultModelStr && productData.productAttrs.ModelData ? productData.productAttrs.ModelData[defaultModelStr] : null; 9715 9716 var modelHeight = selectedModel ? selectedModel.height : Utils.getValueFromObject(productData, 'productAttrs.OFPModelHeight', ''); 9717 var modelSize = selectedModel ? selectedModel.size : Utils.getValueFromObject(productData, 'productAttrs.OFPModelSize', ''); 9718 9719 $infoPanel.find(detailsHeightSelector).html(modelHeight);
9720 $infoPanel.find(detailsSizeSelector).html(modelSize); 9721 }, 9722 9723 setGarmentMeasurements: function setGarmentMeasurements(productId, $productSection, $infoPanel) { 9724 var anfObject = Utils.getANFObject(); 9725 var $measurementContainer = $infoPanel.find(garmentMeasurementsSelector); 9726 var garmentData = garmentMeasurements.getGarmentMeasurementData(productId, anfObject, $productSection); 9727 $measurementContainer.html(template.renderTemplate('garmentMeasurements', garmentData)); 9728 }, 9729 9730 setSizeGuideFitRecommendation: function setSizeGuideFitRecommendation($productSection) { 9731 var $recommendedFit = $productSection.find(sizeGuideRecommendedFitSelector); 9732 9733 if ($recommendedFit.html()) { 9734 return; 9735 } 9736 9737 var fitRec = $productSection.find(reviewRecommendedFitSelector).first(); 9738 if (fitRec.length > 0 && fitRec.css('display') === 'block') { 9739 $recommendedFit.append(fitRec.html()); 9740 $recommendedFit.find('a').contents().unwrap(); 9741 } 9742 }, 9743 9744 sizeGuideImageHandler: function sizeGuideImageHandler($productSection, productId) { 9745 var imageObject = $productSection.data('imageDataModel'); 9746 9747 if (!imageObject) { 9748 return; 9749 } 9750 9751 var defaultModelStr = window.selectedModelSegment || Utils.getValueFromObject(Utils.getProductData(productId), 'productAttrs.ModelData.defaultModel', null); 9752 var model = defaultModelStr ? StringUtils.camelize(defaultModelStr, false) : 'model'; 9753 // double check that we have the correct model 9754 var selectedModel = imageObject[model] ? imageObject[model] : imageObject.model; 9755 9756 var imagesToDisplay = gatherImagesToDisplay(selectedModel, imageObject); 9757 var images = []; 9758 imagesToDisplay.forEach(function addSrc(image) { 9759 var imageToDisplay = { 9760 src: ANFImage.instance() 9761 .setImageName(image.imageId) 9762 .setWidth(800) 9763 .url, 9764 altText: image.imageId.indexOf('prod') > -1 ? 'prod' : 'model', 9765 imageId: image.imageId 9766 }; 9767 9768 images.push(imageToDisplay); 9769 }); 9770 9771 var markup = template.renderTemplate('sizeGuideImages', {images: images}); 9772 var $imageBannerWrapper = $productSection.find(sizeGuideImagesSelector); 9773 9774 document.querySelector(sizeGuideImagesSelector).addEventListener('error', imageBannerErrorHandler, true); 9775 9776 $imageBannerWrapper.html(markup); 9777 } 9778 }; 9779 9780 return publicMembers; 9781}); 9782 9783redesign.define('productModules/product/sizeGuide/sizeGuideAnalytics',[], function SizeGuideAnalytics() { 9784 'use strict'; 9785 9786 var unitTabContainerSelector = '.details__size-guide-modal .size-guide__unit-toggle-wrapper .toggle-block'; 9787 var measurementGuideTabSelector = 'button[id^="size-guide__tab-guide"]'; 9788 var sizeChartTabSelectorId = 'button[id^="size-guide__tab-chart"]'; 9789 var sizeGuideLinkSelector = '.details__link-size-chart, .product-page__info-size-chart, .size-fit__accordion .view-size-fit-btn'; 9790 var sizeGuideRegionSelector = '.size-guide-region-selector select'; 9791 9792 function handleMeasurementGuideTabClick() { 9793 $(window).trigger('analytics.modalAction', { 9794 'name': 'size & fit guide', 9795 'action': 'tab click', 9796 'attribute': 'how to measure' 9797 }); 9798 } 9799 function handleRegionChange(event) { 9800 $(window).trigger('analytics.modalAction', { 9801 'name': 'size & fit guide', 9802 'action': 'region selected', 9803 'attribute': event.currentTarget.value 9804 }); 9805 } 9806 function handleSizeGuideLinkClick() { 9807 $(window).trigger('analytics.miscProductFeature', { 9808 'type': 'size & fit guide', 9809 'action': 'click' 9810 }); 9811 } 9812 function handleSizeChartTabClick() { 9813 $(window).trigger('analytics.modalAction', { 9814 'name': 'size & fit guide', 9815 'action': 'tab click', 9816 'attribute': 'chart' 9817 }); 9818 } 9819 function handleUnitToggleChange(event) { 9820 var unit = $(event.currentTarget).find('input').val(); 9821 9822 $(window).trigger('analytics.modalAction', { 9823 'name': 'size & fit guide', 9824 'action': 'unit change', 9825 'attribute': unit 9826 }); 9827 } 9828 var publicMembers = { 9829 bindEvents: function bindEvents() { 9830 var $document = $(document); 9831 9832 $document.on('click', measurementGuideTabSelector, handleMeasurementGuideTabClick); 9833 $document.on('click', sizeChartTabSelectorId, handleSizeChartTabClick); 9834 $document.on('click', sizeGuideLinkSelector, handleSizeGuideLinkClick); 9835 9836 $document.on('change', sizeGuideRegionSelector, handleRegionChange); 9837 $document.on('change', unitTabContainerSelector, handleUnitToggleChange); 9838 } 9839 9840 }; 9841 9842 return publicMembers; 9843}); 9844 9845redesign.define('productServices/product/retrieveFabricCareAttrs',['productModules/helpers/Utils'], function RetrieveFabricCareAttrs(Utils) { 9846 'use strict'; 9847 9848 function isArrayOfValues(content) { 9849 return typeof content.values !== 'undefined' && content.values !== null; 9850 } 9851 9852 function isSingleValue(content) { 9853 return typeof content.value !== 'undefined' && content.value !== null; 9854 } 9855 9856 function getCurrentData(productId) { 9857 var productData = Utils.getProductData(productId); 9858 var data = ( 9859 typeof productData !== 'undefined' 9860 ) ? productData.productAttrsComplex : {}; 9861 9862 return data; 9863 } 9864 9865 function fromContentValues(content) { 9866 var data = []; 9867 if (typeof content !== 'undefined') { 9868 if (isArrayOfValues(content)) { 9869 $(content.values).each(function each() { 9870 data.push(this.value); 9871 }); 9872 } else if (isSingleValue(content)) { 9873 data = [content.value]; 9874 } 9875 } 9876 return data; 9877 } 9878 9879 var publicMembers = { 9880 9881 getFabricCareData: function getFabricCareData(productId) { 9882 var fabric = []; 9883 var care = []; 9884 9885 if (typeof productId !== 'undefined') { 9886 var productData = getCurrentData(productId); 9887 9888 fabric = fromContentValues(productData.FiberContent); 9889 care = fromContentValues(productData.CareInstructions); 9890 } 9891 9892 return { 9893 fabric: fabric, 9894 care: care 9895 }; 9896 } 9897 }; 9898 9899 return publicMembers; 9900}); 9901 9902redesign.define('productModules/product/productFabricCare',[ 9903 'productServices/product/retrieveFabricCareAttrs', 9904 'productModules/helpers/Utils' 9905], function ProductFabricCare(fabricCareService, Utils) { 9906 'use strict'; 9907 9908 var messageBus = $(ANF); 9909 9910 var fabricCareContainerSelectorClass = '.fabric-care__container'; 9911 var fabricCareListSelectorClass = '.fabric-care__list'; 9912 var productV2SelectorClass = '.product-page-v2-modules'; 9913 var quickviewProductSelector = '.product-quickview__modal .product-page-v2-modules'; 9914 var productV2Class = 'product-page-v2-modules'; 9915 var fiberContentContainer = '.fiber-content__label'; 9916 9917 function getFabricCareInfo(DOMElement) { 9918 var $productSection = $(DOMElement).hasClass(productV2Class) ? $(DOMElement) : $(DOMElement).closest(productV2SelectorClass); 9919 9920 var fabricCareInfo = { 9921 $fabricCareContainer: $productSection.find(fabricCareContainerSelectorClass), 9922 $fiberContentContainer: $productSection.find(fiberContentContainer), 9923 $fabricCareList: $productSection.find(fabricCareListSelectorClass), 9924 productId: $productSection.data('productid') 9925 }; 9926 9927 return fabricCareInfo; 9928 } 9929 9930 var publicMembers = { 9931 createFabricCareListItem: function createFabricCareListItem(text) { 9932 return $('<li></li>').text(text); 9933 }, 9934 9935 renderFabricCareList: function renderFabricCareList(fabricCareInfo) { 9936 var fabricCareData = fabricCareService.getFabricCareData(fabricCareInfo.productId); 9937 var $careListItems; 9938 9939 fabricCareInfo.$fiberContentContainer.html(fabricCareData.fabric.join(' / ')); 9940 $careListItems = $.map(fabricCareData.care, publicMembers.createFabricCareListItem); 9941 fabricCareInfo.$fabricCareList.empty().append($careListItems); 9942 9943 // We still want to empty the fabricCareList if there are no care instructions, but also not show the container 9944 var hasCareData = fabricCareData.length > 0 || (fabricCareData.care.length > 0 && fabricCareData.fabric.length > 0); 9945 9946 if (hasCareData) { 9947 fabricCareInfo.$fabricCareContainer.addClass('has-care-data'); 9948 } else { 9949 fabricCareInfo.$fabricCareContainer.removeClass('has-care-data'); 9950 } 9951 }, 9952 9953 handleSwatchSelect: function handleSwatchSelect(e, swatch) { 9954 var fabricCareInfo = getFabricCareInfo(swatch); 9955 9956 publicMembers.renderFabricCareList(fabricCareInfo); 9957 }, 9958 9959 handleFabricCareLoad: function handleFabricCareLoad(e, isQuickview) { 9960 var $products = isQuickview ? $(quickviewProductSelector) : $(productV2SelectorClass); 9961 if (!isQuickview && Utils.hasProductMFEEnabled()) { 9962 return; 9963 } 9964 $products.each(function products(idx, $product) { 9965 var fabricCareInfo = getFabricCareInfo($product); 9966 publicMembers.renderFabricCareList(fabricCareInfo); 9967 }); 9968 } 9969 9970 }; 9971 9972 publicMembers.listen = function listen() { 9973 messageBus.on('productPageInfo:swatchSelected', publicMembers.handleSwatchSelect); 9974 messageBus.on('productFabricCare:load', publicMembers.handleFabricCareLoad); 9975 }; 9976 9977 9978 return publicMembers; 9979}); 9980 9981redesign.define('productModules/product/productRecommendationAnalytics',['productModules/helpers/Utils'], 9982 function ProductRecommendationAnalytics(Utils) { 9983 'use strict'; 9984 9985 return function ProductRecommendationAnalyticsClosure(productIntersectionObserver) { 9986 function wireIntersectionObserver(targetContainer) { 9987 var productSliderObserver = new productIntersectionObserver.IntersectionObserver( 9988 function productIntersectionObserverCallback(entries) { 9989 for (var i = 0, len = entries.length; i < len; i++) { 9990 var recContainer = entries[i]; 9991 9992 if (recContainer.isIntersecting) { 9993 productSliderObserver.unobserve(recContainer.target); 9994 var ProductRecommendationId = $(recContainer.target).attr('data-sli
9994der-id'); 9995 var ProductRecommendationContainer = document.querySelector('[data-slider-id=' + ProductRecommendationId + ']'); 9996 if (ProductRecommendationContainer.hasChildNodes()) { 9997 window.digitalData.trigger('universal_scroll', { 9998 Tealium_event: 'scroll', 9999 data_text: 'user scrolled to', 10000 data_action: ProductRecommendationId 10001 }); 10002 } 10003 } 10004 } 10005 }, 10006 { 10007 threshold: 0 10008 } 10009 ); 10010 10011 productSliderObserver.observe(targetContainer); 10012 } 10013 10014 function init() { 10015 var $productRecContainers = $('.product-page-user-content__recommendations .js-recommendation-slider'); 10016 10017 $productRecContainers.each(function recContainerWireLoop(i, container) { 10018 wireIntersectionObserver(container); 10019 }); 10020 } 10021 10022 var publicMembers = { 10023 'bindEvents': function bindEvents() { 10024 if (!Utils.isExternalUser()) { 10025 init(); 10026 } 10027 } 10028 }; 10029 10030 return publicMembers; 10031 }; 10032 }); 10033 10034redesign.define('productModules/product/productSizeMessaging',['productServices/product/template', 10035 'productModules/helpers/Utils' 10036], function ProductSizeMessaging(template, Utils) { 10037 'use strict'; 10038 10039 function handleSizeMessaging(e, $selectedSwatch, $productSection, productId, ANFObject) { 10040 var sizeChartName = Utils.getProductData(productId).sizeChartName; 10041 var templateData = ANFObject.messaging.catalogV2.sizeMessaging[sizeChartName]; 10042 var markup; 10043 10044 if (templateData) { 10045 markup = template.renderTemplate('productSizeMessaging', templateData); 10046 var $sizeMessagingContainer = $productSection 10047 .find('.size-messaging-container') 10048 .append(markup) 10049 .show(); 10050 10051 $sizeMessagingContainer 10052 .eq(1) 10053 .find('.size-messaging__header') 10054 .addClass('h2'); 10055 } 10056 } 10057 10058 var publicMembers = { 10059 listen: function listen() { 10060 $(ANF).on('productPageInfo:swatchOnLoad', handleSizeMessaging); 10061 } 10062 }; 10063 10064 return publicMembers; 10065}); 10066 10067redesign.define('productModules/product/sizebaySetup',['productModules/helpers/Utils', 10068 'productModules/helpers/featureSwitchHelper' 10069], function SizeBaySetup(Utils, featureSwitchHelper) { 10070 'use strict'; 10071 10072 function listen() { 10073 $(ANF).on('productPageInfo:swatchOnLoad', checkForSizebayEnabledAndSetup); 10074 } 10075 10076 function checkForSizebayEnabledAndSetup(e, $selectedSwatch, $productSection, productId) { 10077 if (Utils.isJeansProduct(productId)) { 10078 featureSwitchHelper.checkFeatureSwitchAndSplitTest('sizebay-enabled', setupSizeBay); 10079 } 10080 } 10081 10082 function setupSizeBay() { 10083 if ($('#sizebay-vfr-v4').length === 0) { 10084 var $sizebayScript = $('<script id="sizebay-vfr-v4" />') 10085 .attr('src', Utils.getANFObject().params.urls.thirdParty.sizebayUrl); 10086 10087 $('head').append($sizebayScript); 10088 } 10089 } 10090 10091 var publicMembers = { 10092 listen: listen 10093 }; 10094 10095 return publicMembers; 10096}); 10097 10098redesign.define('productModules/product/productMemberEarlyAccess',[ 10099 'globalServices/userState', 10100 'productModules/helpers/Utils' 10101], function productMemberEarlyAccess(userStateService, Utils) { 10102 'use strict'; 10103 10104 var messageBus = $(ANF); 10105 var nonEarlyAccess = 'non-early-access-member'; 10106 var quickviewLoginButton = '.product-quickview__modal .js-product-login-button'; 10107 10108 function handleMemberEarlyAccess(e, $selectedSwatch, $productSection, productId) { 10109 var earlyAccessFlag = Utils.productHasEarlyAccessIndicator(productId); 10110 var isLoyaltyRegion = ANF.params.storeAttributes.loyalty.enabled; 10111 var atbWrapper = $productSection.find('.atb-member-wrapper'); 10112 10113 if (isLoyaltyRegion && earlyAccessFlag && !userStateService.isLoggedIn()) { 10114 atbWrapper.addClass(nonEarlyAccess); 10115 } else { 10116 atbWrapper.removeClass(nonEarlyAccess); 10117 } 10118 } 10119 var publicMembers = { 10120 listen: function listen() { 10121 messageBus.on('productPageInfo:swatchOnLoad productPageInfo:swatchSelected', handleMemberEarlyAccess); 10122 }, 10123 bindEvents: function bindEvents() { 10124 $(document).on('click', quickviewLoginButton, function quickviewLoginClicked() { 10125 messageBus.trigger('quickviewLogin:clicked'); 10126 }); 10127 } 10128 }; 10129 10130 return publicMembers; 10131}); 10132 10133redesign.define('productModules/product/productReviewsLegalText',[ 10134 'productModules/helpers/Utils' 10135], 10136function ReviewsLegalText(Utils) { 10137 'use strict'; 10138 10139 var legalCopyTextSelectorClass = '.reviews-legal-text'; 10140 var legalCopyContainerSelectorClass = '.reviews-legal-text-container'; 10141 10142 function showLegalCopy() { 10143 var hasReviewLegalText = window.digitalData.get('flag.pdp-reviews-legal-text', false); 10144
10145 if (!Utils.isQuickviewOpened && hasReviewLegalText) { 10146 var legalTextRaw = Utils.getANFObject().messaging.product.PDP_REVIEWS_LEGAL_TEXT; 10147 var legalTextWithLink = legalTextRaw.replace('{0}', Utils.getANFObject().params.urls.catalogV2.reviewsLegalTextUrl); 10148 var $legalCopyContainer = $(legalCopyContainerSelectorClass); 10149 var $legalCopyText = $legalCopyContainer.find(legalCopyTextSelectorClass); 10150 10151 $legalCopyText.append(legalTextWithLink); 10152 $legalCopyContainer.addClass('show'); 10153 } 10154 } 10155 10156 var publicMembers = { 10157 listen: function listen() { 10158 $(ANF).on('productPageInfo:swatchOnLoad mfe:SwatchSelected', showLegalCopy); 10159 } 10160 }; 10161 10162 return publicMembers; 10163}); 10164 10165redesign.define('productModules/product/finalSale',['productModules/helpers/Utils'], function ProductDetailsAccordion(Utils) { 10166 'use strict'; 10167 10168 var messageBus = $(ANF); 10169 10170 function checkForFinalSale(e, $selectedSwatch, $productSection) { 10171 if (Utils.hasFinalSaleEnabled()) { 10172 var productId = $productSection.data('productid'); 10173 var isFinalSaleProduct = Utils.getProductData(productId).isFinalSale; 10174 var $finalSaleContainer = $productSection.find('.final-sale-container'); 10175 10176 if (isFinalSaleProduct) { 10177 $finalSaleContainer.show(); 10178 } else { 10179 $finalSaleContainer.hide(); 10180 } 10181 } 10182 } 10183 10184 var publicMembers = { 10185 listen: function listen() { 10186 messageBus.on('productPageInfo:swatchOnLoad productPageInfo:swatchSelected', checkForFinalSale); 10187 } 10188 }; 10189 10190 return publicMembers; 10191}); 10192 10193redesign.define('productModules/helpers/analyticsHelper',[], 10194 function AnalyticsHelper() { 10195 'use strict'; 10196 10197 var completeTheLookLinkSelectorClass = '.product-main-images__wrapper.scope-1892 a.stylitics-jumplink.jumplink'; 10198 10199 function handleAccordionOpen(event) { 10200 var accordionType = event.target.dataset && event.target.dataset.accordionName; 10201 10202 if (accordionType) { 10203 window.digitalData.trigger('analytics.pdp_accordion_open', { 10204 tealium_event: 'click', 10205 data_text: accordionType, 10206 data_action: 'open' 10207 }); 10208 } 10209 } 10210 10211 function getConfig(configName) { 10212 var triggerConfig = { 10213 sizeGuideClick: { 10214 analytics: 'analytics.pdp_size_guide_click', 10215 data_text: 'size guide', 10216 data_action: 'open' 10217 }, 10218 howToMeasure: { 10219 analytics: 'pdp_size_guide_how_to_measure_click', 10220 data_text: 'size guide - how to measure', 10221 data_action: 'open' 10222 }, 10223 completeTheLook: { 10224 analytics: 'pdp_stylitics_complete_this_look_click', 10225 data_text: 'stylitics-complete-this-look', 10226 data_action: 'click' 10227 }, 10228 viewLook: { 10229 analytics: 'pdp_stylitics_view_this_look_click', 10230 data_text: 'stylitics-view-this-look', 10231 data_action: 'open' 10232 } 10233 }; 10234 10235 if (triggerConfig[configName]) { 10236 var defaultConfig = { 10237 analytics: triggerConfig[configName].analytics, 10238 data: { 10239 tealium_event: 'click', 10240 data_text: triggerConfig[configName].data_text, 10241 data_action: triggerConfig[configName].data_action 10242 } 10243 }; 10244 return defaultConfig; 10245 } 10246 return null; 10247 } 10248 10249 function handleProductAnalytics(event) { 10250 var configName = event.data.configName; 10251 var config = getConfig(configName); 10252 10253 if (config) {
10254 window.digitalData.trigger(config.analytics, config.data); 10255 } 10256 } 10257 10258 var publicMembers = { 10259 bindEvents: function bindEvents() { 10260 $(document).off('click', '.product-page__info-size-chart.js-open-modal').on('click', '.product-page__info-size-chart.js-open-modal', function handleModalClick() { 10261 handleProductAnalytics({ data: { configName: 'sizeGuideClick' } }); 10262 }); 10263 $(document).on('dsAccordion:Open', handleAccordionOpen); 10264 $(document).on('guideSize:howToMeasure', {configName: 'howToMeasure'}, handleProductAnalytics); 10265 $(completeTheLookLinkSelectorClass).on('click', {configName: 'completeTheLook'}, handleProductAnalytics); 10266 $(window).on('styliticsV3Item:viewLook', {configName: 'viewLook'}, handleProductAnalytics); 10267 } 10268 }; 10269 10270 return publicMembers; 10271 }); 10272 10273redesign.define('productModules/product/productExitRateAnalytics',['productModules/helpers/Utils'], 10274 function ProductExitRateAnalytics(Utils) { 10275 'use strict'; 10276 10277 return function ProductShopSimilarAnalyticsClosure(productIntersectionObserver) { 10278 function wireIntersectionObserver(targetContainer) { 10279 var shopSimilarObserver = new productIntersectionObserver.IntersectionObserver( 10280 function productIntersectionObserverCallback(entries) { 10281 for (var i = 0, len = entries.length; i < len; i++) { 10282 var shopSimilarContainer = entries[i]; 10283 10284 if (shopSimilarContainer.isIntersecting) { 10285 shopSimilarObserver.unobserve(shopSimilarContainer.target); 10286 window.digitalData.trigger('universal_scroll', { 10287 event_name: 'scroll', 10288 data_text: 'user scrolled to', 10289 data_action: 'pdp shop similar grid' 10290 }); 10291 } 10292 } 10293 }, 10294 { 10295 threshold: 0 10296 } 10297 ); 10298 10299 shopSimilarObserver.observe(targetContainer); 10300 } 10301 10302 function init() { 10303 var $productShopSimilarContainer = $('.pdp-grid-container'); 10304 if (Utils.isExternalUser() && !Utils.isQuickviewOpened && !Utils.hasProductMFEEnabled) { 10305 wireIntersectionObserver($productShopSimilarContainer[0]); 10306 } 10307 } 10308 10309 var publicMembers = { 10310 'bindEvents': function bindEvents() { 10311 if (Utils.isPdpPage) { 10312 init(); 10313 } 10314 } 10315 }; 10316 10317 return publicMembers; 10318 }; 10319 }); 10320 10321redesign.define('productSections/product/productPageInfo',[ 10322 'coreSectionHelpers', 10323 'productModules/product/productSizes', 10324 'productModules/product/productMultipleAttributeFilter', 10325 'productModules/product/productSwatches', 10326 'productModules/product/productPrice', 10327 'productModules/product/addToBag/pdpAddToBag', 10328 'productModules/product/addToBag/quickviewAddToBag', 10329 'productModules/product/productNameAndDescription', 10330 'productModules/product/addToBag/productStickyAddToBag', 10331 'productModules/product/productDetailBadging', 10332 'productModules/product/buyGridAccordions/productDetailsAccordion', 10333 'productModules/product/buyGridAccordions/productSizeFitAccordion', 10334 'productModules/product/sizeGuide/productSizeGuide', 10335 'productModules/product/sizeGuide/sizeGuideHeader', 10336 'productModules/product/sizeGuide/sizeGuideChartControls', 10337 'productModules/product/sizeGuide/sizeGuideAnalytics', 10338 'productModules/product/productFabricCare', 10339 'productModules/product/productRecommendationAnalytics', 10340 'productModules/helpers/productIntersectionObserver', 10341 'productModules/product/productSizeMessaging', 10342 'productModules/product/sizebaySetup', 10343 'productModules/product/productMemberEarlyAccess', 10344 'productModules/product/productReviewsLegalText', 10345 'productModules/helpers/featureSwitchHelper', 10346 'productModules/product/finalSale', 10347 'productModules/helpers/analyticsHelper', 10348 'productModules/helpers/ProductMFEAdapter', 10349 'productModules/product/productExitRateAnalytics' 10350], function ProductPageInfo( 10351 coreSectionHelpers, 10352 productSizes, 10353 productMultipleAttributeFilter, 10354 productSwatches, 10355 productPrice, 10356 pdpAddToBag, 10357 quickviewAddToBag, 10358 productNameAndDescription, 10359 productStickyAddToBag, 10360 productDetailBadging, 10361 productDetailsAccordion, 10362 productSizeFitAccordion, 10363 sizeGuide, 10364 sizeGuideHeader, 10365 sizeGuideControls, 10366 sizeGuideAnalytics, 10367 productFabricCare, 10368 productRecommendationAnalytics, 10369 productIntersectionObserver, 10370 productSizeMessaging, 10371 sizebaySetup, 10372 productMemberEarlyAccess, 10373 productReviewsLegalText, 10374 featureSwitchHelper, 10375 finalSale, 10376 analyticsHelper, 10377 productMFEAdapter, 10378 productExitRateAnalytics 10379) { 10380 'use strict'; 10381 10382 var privateMembers = { 10383 DEFAULT_MODULES: { 10384 productSizes: productSizes, 10385 productMultipleAttributeFilter: productMultipleAttributeFilter, 10386 productSwatches: productSwatches, 10387 productPrice: productPrice, 10388 pdpAddToBag: pdpAddToBag, 10389 quickviewAddToBag: quickviewAddToBag, 10390 productNameAndDescription: productNameAndDescription, 10391 productStickyAddToBag: productStickyAddToBag(productIntersectionObserver), 10392 productDetailBadging: productDetailBadging, 10393 productDetailsAccordion: productDetailsAccordion, 10394 productSizeFitAccordion: productSizeFitAccordion, 10395 '.details__size-chart-modal': sizeGuide, 10396 sizeGuideHeader: sizeGuideHeader, 10397 sizeGuideControls: sizeGuideControls, 10398 sizeGuideAnalytics: sizeGuideAnalytics, 10399 productFabricCare: productFabricCare, 10400 productRecommendationAnalytics: productRecommendationAnalytics(productIntersectionObserver), 10401 productSizeMessaging: productSizeMessaging, 10402 sizebaySetup: sizebaySetup, 10403 productMemberEarlyAccess: productMemberEarlyAccess, 10404 productReviewsLegalText: productReviewsLegalText, 10405 finalSale: finalSale,
10406 analyticsHelper: analyticsHelper, 10407 productMFEAdapter: productMFEAdapter, 10408 productExitRateAnalytics: productExitRateAnalytics(productIntersectionObserver) 10409 }, 10410 GLOBAL_SCOPE: '', 10411 MODULES: {}, 10412 parentPage: '' 10413 }; 10414 10415 return function scope(settings) { 10416 privateMembers.GLOBAL_SCOPE = settings.scope; 10417 privateMembers.parentPage = settings.parentPage; 10418 privateMembers.MODULES = privateMembers.DEFAULT_MODULES; 10419 10420 if (typeof settings.modules === 'object') { 10421 $.extend(true, privateMembers.MODULES, settings.modules); 10422 } 10423 10424 var messageBus = $(ANF); 10425 10426 return { 10427 'init': function init() { 10428 coreSectionHelpers.init({ 10429 'modules': privateMembers.MODULES, 10430 'scope': privateMembers.GLOBAL_SCOPE 10431 }); 10432 10433 productDetailBadging.init(); 10434 }, 10435 'subscribe': function subscribe() { 10436 messageBus.on('productPageMain:init', privateMembers.parentPage.productPageInfo.productPageInfoLoad); 10437 messageBus.on('productPageMain:init', privateMembers.parentPage.productPageInfo.productFabricCareLoad); 10438 }, 10439 'productPageInfoLoad': function productPageInfoLoad() { 10440 messageBus.trigger('productPageInfo:load'); 10441 featureSwitchHelper.styleFitDesc(); 10442 featureSwitchHelper.shortDescriptorSplitTest(); 10443 }, 10444 'productFabricCareLoad': function productFabricCareLoad() { 10445 messageBus.trigger('productFabricCare:load'); 10446 } 10447 }; 10448 }; 10449}); 10450 10451redesign.define('coreServices/helpers/featureSwitch',[], function featureSwitch() { 10452 'use strict'; 10453 10454 var publicMembers = { 10455 10456 checkFeatureSwitchAndSplitTest: function checkFeatureSwitchAndSplitTest(featureAndSplitTestKey, cb) { 10457 var deferredResult = new $.Deferred(); 10458 var featureFlagKey = 'flag.' + featureAndSplitTestKey; 10459 var splitTestKey = 'test.' + featureAndSplitTestKey; 10460 if (window.digitalData.get(featureFlagKey, false ) || window.digitalData.get(splitTestKey, false)) { 10461 deferredResult.resolve(true); 10462 } else { 10463 var digitalDataSubscription = window.digitalData.subscribe(splitTestKey, function checkTestSegment(segment) { 10464 if (typeof segment !== 'undefined') { 10465 this.unsubscribe(); 10466 deferredResult.resolve(segment); 10467 } 10468 }); 10469 setTimeout(function setTimeout() { 10470 digitalDataSubscription.unsubscribe(); 10471 deferredResult.resolve(false); 10472 }, 2000); 10473 } 10474 deferredResult.promise() 10475 .then(function checkSegment(isEnabled) { 10476 return cb(isEnabled); 10477 }); 10478 }, 10479 checkFeatureSwitchAndSplitTestValue: function checkFeatureSwitchAndSplitTestValue(featureAndSplitTestKey, cb) { 10480 var deferredResult = new $.Deferred(); 10481 var featureFlagKey = 'flag.' + featureAndSplitTestKey; 10482 deferredResult.resolve(window.digitalData.get(featureFlagKey, null )); 10483 deferredResult.promise() 10484 .then(function checkSegment(value) { 10485 return cb(value); 10486 }); 10487 }, 10488 getLocationSearch: function getLocationSearch() { 10489 return window.location.search; 10490 }, 10491 getDigitalDataFlagValue: function getDigitalDataFlagValue(key) { 10492 var storeAttributeFlags = window.digitalData && window.digitalData.export() && window.digitalData.export().flag; 10493 // The return value can be undefined if LD is not setup, so implementer needs to handle this. 10494 // supports comma separates like "?ldo=ful-123:1,ful-456:0,;purx-123:1" 10495 try { 10496 var locationSearch = this.getLocationSearch(); 10497 if (locationSearch) { 10498 var modeField = locationSearch.match(/ldo=(([\w-0-9]+:[01],?)+)&?/); 10499 var modeFieldValue = (modeField && modeField[1]); 10500 if (modeFieldValue) { 10501 var modeFields = modeFieldValue.split(','); 10502 var overrides = modeFields.reduce(function reduceOverrides(accum, override) { 10503 if (!override) { 10504 return accum; 10505 } 10506 var parts = override.split(':'); 10507 var data = {}; 10508 data[parts[0]] = parseInt(parts[1], 10); 10509 return $.extend({}, accum, data); 10510 }, {}); 10511 if (key in overrides) { 10512 return overrides[key]; 10513 } 10514 } 10515 } 10516 if (JSON.parse(sessionStorage.getItem('debug_mode')) && 10517 JSON.parse(sessionStorage.getItem(key)) !== null) { 10518 // returned overridden value from sessionStorage 10519 return JSON.parse(sessionStorage.getItem(key)); 10520 } 10521 } catch (e) { 10522 // eslint-disable-next-line no-console 10523 console.error(e); 10524 } 10525 10526 return storeAttributeFlags[key]; 10527 } 10528 }; 10529 return publicMembers; 10530}); 10531 10532redesign.define('productModules/product/quickview/quickview',[ 10533 'anfcore/components/statefulButton', 10534 'anfcore/components/modal', 10535 'anfcore/components/dropDown',
10536 'anfcore/components/accordion', 10537 'productModules/helpers/Utils', 10538 'productModules/helpers/breakpoints', 10539 'coreServices/storage', 10540 'productModules/helpers/featureSwitchHelper', 10541 'coreServices/helpers/featureSwitch', 10542 'util/analytics/enum/dataPoint', 10543 'util/analytics/enum/subscriptionName', 10544 'anfUtil/constants', 10545 'coreServices/geoLocationService' 10546], function ProductQuickview( 10547 statefulButton, 10548 modal, 10549 dropDown, 10550 accordion, 10551 Utils, 10552 breakpoints, 10553 storage, 10554 featureSwitchHelper, 10555 featureSwitch, 10556 analyticsDataPoints, 10557 analyticSubscriptionName, 10558 constants 10559) { 10560 'use strict'; 10561 var dsModal = modal.default(); 10562 10563 var quickviewModalSelectorClass = '.product-quickview__modal'; 10564 var quickviewButtonSelectorClass = '.product-quickview__button'; 10565 var editQuickviewButtonClass = 'product-quickview__edit-button'; 10566 var quickviewProductSectionSelector = '.product-quickview__content .product-page-v2-modules'; 10567 var quickviewContentSelectorClass = '.product-quickview__content'; 10568 var addToBagButtonSelector = '.product-page-add-to-bag__button'; 10569 var definingAttrSelectorClass = '.product-attrs__attr'; 10570 var quickviewEditModalSelector = '.product-quickview__modal.edit-product'; 10571 var quickviewEditModalClass = 'edit-product'; 10572 var detailsSelectorClass = '.details'; 10573 var addToBagFormSelector = '.product-page-add-to-bag__container'; 10574 var quickviewDetailsWrapperHookSelector = '#quickview-details__wrapper-hook .accordion-panel-content'; 10575 var fullProductLinkSelector = '.product-quickview__modal .product-quickview__view-full-product-link'; 10576 var quickviewSoldOutSelectorClass = '.product-quickview__sold-out'; 10577 var exchangesModalButtonClass = 'exchanges-quickview__button'; 10578 var exchangesModalClass = 'exchanges-quickview__modal'; 10579 var exchangesSaveButtonSelector = '.exchanges-quickview__save-button'; 10580 var exchangesEditModalClass = 'exchanges-quickview__modal-edit'; 10581 var exchangesNoLongerAvailableSelector = '#exchangesNoLongerExists'; 10582 var changeLinkClass = 'return-change_link'; 10583 var shoppingCartQVSelectorClass = '#checkout-cart button.js-pc-edit-button'; 10584 var styliticsQuickviewButtonClass = 'stylitics-widget-item-container'; 10585 var styliticsModalClass = 'stylitics-quickview'; 10586 var isEditFromMiniBag = false; 10587 10588 var messageBus = $(ANF); 10589 10590 var openExtraAnalyticsData = {}; 10591 10592 function getFaceoutValue($quickviewLink, quickviewLinkAttrs) { 10593 var faceout = 'prod'; 10594 var hasOfpAttr = typeof quickviewLinkAttrs.showOfp !== 'undefined' && quickviewLinkAttrs.showOfp; 10595 var $imageWrapperLink = $quickviewLink.closest('.product-template').find('.product-card__image-link'); 10596 var hasImageWrapperLinkOfp = $imageWrapperLink.length > 0 && $imageWrapperLink.attr('href').indexOf('ofp=true') > -1; 10597 if (typeof quickviewLinkAttrs.faceout !== 'undefined') { 10598 faceout = quickviewLinkAttrs.faceout.replace(/[0-9]/g, '');// remove number character 10599 } else if (hasOfpAttr || hasImageWrapperLinkOfp) { 10600 faceout = 'model'; 10601 } 10602 return faceout; 10603 } 10604 10605 function getReturnFocus($quickviewLink) { 10606 var $returnFocus = $quickviewLink; 10607 // if the link clicked to open qv was edit in minibag, focus will go to bag icon on modal close 10608 var $shoppingBagParent = $quickviewLink.parents('.rs-nav__util-item--shopping-bag'); 10609 if ($shoppingBagParent.length > 0) { 10610 $returnFocus = $shoppingBagParent.find('.rs-nav-icon--shopping-bag'); 10611 } 10612 return $returnFocus; 10613 } 10614 10615 function constructQuickviewURL(quickviewState) { 10616 var quickviewParams = { 10617 productId: quickviewState.productId, 10618 collectionId: quickviewState.collectionId, 10619 seq: quickviewState.seq, 10620 faceout: quickviewState.faceout 10621 }; 10622 if (quickviewState.isSoldOutInCart) { 10623 quickviewParams.isSoldOutInCart = true; 10624 } 10625 if (quickviewState.categoryId) { 10626 quickviewParams.categoryId = quickviewState.categoryId; 10627 } 10628 if (quickviewState.origin) { 10629 quickviewParams.origin = quickviewState.origin; 10630 } 10631 if (quickviewState.afsource) { 10632 quickviewParams.afsource = quickviewState.afsource; 10633 } 10634 10635 if (quickviewState.pagevm) { 10636 quickviewParams.pagevm = quickviewState.pagevm; 10637 } 10638 10639 if (quickviewState.prodvm) { 10640 quickviewParams.prodvm = quickviewState.prodvm; 10641 } 10642 10643 // PRO-1479: Add dataText and dataAction to the quickview URL 10644 // These are specific to recommendations swatch click analytics 10645 if (quickviewState.dataText) { 10646 quickviewParams.dataText = quickviewState.dataText; 10647 } 10648 10649 if (quickviewState.dataAction) { 10650 quickviewParams.dataAction = quickviewState.dataAction; 10651 } 10652 10653 var baseUrl = ANF.params.urls.ajax.productQuickView; 10654 10655 return baseUrl + '&' + $.param(quickviewParams); 10656 } 10657 10658 function getBadgeDataFromProduct(productId, key) { 10659 var productData = Utils.getProductData(productId); 10660 if (!productData) { 10661 return null; 10662 } 10663 var keys = key.split('.'); 10664 return keys.reduce(function _reduce(acc, currKey) { 10665 return (acc && acc.hasOwnProperty(currKey)) ? acc[currKey] : null; 10666 }, productData); 10667 } 10668 10669 function getProductBadge(productId) { 10670 var isNewArrival = getBadgeDataFromProduct(productId, 'newArrival'); 10671 var earlyAccessFlag = getBadgeDataFromProduct(productId, 'productAttrs.EarlyAccessIndicator'); 10672 var isEarlyAccess = earlyAccessFlag ? earlyAccessFlag.toString().toLowerCase() === 'true' : false;
10673 var isTopRated = getBadgeDataFromProduct(productId, 'productAttrs.topRated'); 10674 var isBestSeller = getBadgeDataFromProduct(productId, 'productAttrs.bestSeller'); 10675 return [isNewArrival ? 'new' : '', isEarlyAccess ? 'early access' : '', isTopRated ? 'top rated' : '', isBestSeller ? 'top seller' : ''].filter(Boolean).join(', '); 10676 } 10677 10678 function getIndividualProductShortDescriptor(firstShortDescriptor, secondShortDescriptor) { 10679 var shortDescriptor = firstShortDescriptor || secondShortDescriptor; 10680 var hasBothShortDescriptors = firstShortDescriptor && secondShortDescriptor; 10681 10682 if (hasBothShortDescriptors) return firstShortDescriptor + ' | ' + secondShortDescriptor; 10683 10684 return shortDescriptor || 'null'; 10685 } 10686 10687 function fireDelayedQuickviewEvents() { 10688 if (Utils.isQuickviewOpened) { 10689 var $productSection = $(quickviewProductSectionSelector); 10690 var productId = $productSection.attr('data-productid'); 10691 var shortSku = $productSection.find('select[name="sku"]').val(); 10692 var dataPointEmptyValue = 'null'; 10693 10694 var orEmptyObject = function orEmptyObjectFn(value) { 10695 return value || {}; 10696 }; 10697 10698 var productData = Utils.getProductData(productId); 10699 var listPrice = orEmptyObject( 10700 Object.values( 10701 orEmptyObject( 10702 orEmptyObject( 10703 productPrices[productId] 10704 ).items 10705 ) 10706 ).pop() 10707 ).listPrice; 10708 10709 var listPriceUsd = orEmptyObject( 10710 Object.values( 10711 orEmptyObject( 10712 orEmptyObject( 10713 productPrices[productId] 10714 ).items 10715 ) 10716 ).pop() 10717 ).listPriceUSD; 10718 10719 var offerPrice = orEmptyObject( 10720 Object.values( 10721 orEmptyObject( 10722 orEmptyObject( 10723 productPrices[productId] 10724 ).items 10725 ) 10726 ).pop() 10727 ).offerPrice; 10728 10729 var categoryId = orEmptyObject( 10730 orEmptyObject( 10731 orEmptyObject( 10732 ANF 10733 ).catalog 10734 ).data 10735 ).categoryId; 10736 10737 var longSku = orEmptyObject( 10738 orEmptyObject( 10739 document.querySelector('.product-page-v2-modules.js-quickview') 10740 ).dataset 10741 ).longSku; 10742 10743 // Deprecated 10744 $(window).trigger('analytics.quickViewComplete', [openExtraAnalyticsData]); 10745 10746 var eventData = {}; 10747 var primaryShortDescriptor = productData ? productData.shortDesc1 : ''; 10748 var secondaryShortDescriptor = productData ? productData.shortDesc2 : ''; 10749 10750 if ($(exchangesNoLongerAvailableSelector).length) { 10751 try { 10752 window.digitalData.trigger( 10753 'universal_click', { 10754 event_name: 'click', 10755 data_text: 'invalid submit', 10756 data_action: 'the item you wish to exchange is no longer available' 10757 }); 10758 } catch (error) { 10759 // error 10760 } 10761 } else { 10762 eventData[analyticsDataPoints.BRAND] = productData.productAttrs && productData.productAttrs.brand || dataPointEmptyValue; 10763 eventData[analyticsDataPoints.COLLECTION_ID] = productData.collection || dataPointEmptyValue; 10764 eventData[analyticsDataPoints.KEY_ITEM_COLOR] = productData.kicId || dataPointEmptyValue; 10765 eventData[analyticsDataPoints.PRICE_LIST_LOCAL] = listPrice || dataPointEmptyValue; 10766 eventData[analyticsDataPoints.PRICE_LIST_USD] = listPriceUsd || dataPointEmptyValue; 10767 eventData[analyticsDataPoints.PRICE_OFFER_LOCAL] = offerPrice || dataPointEmptyValue; 10768 eventData[analyticsDataPoints.CATEGORY_ID] = categoryId || dataPointEmptyValue; 10769 eventData[analyticsDataPoints.PRICE_FLAG] = productData.priceFlag || dataPointEmptyValue; 10770 eventData[analyticsDataPoints.LONG_SKU] = longSku || dataPointEmptyValue; 10771 eventData[analyticsDataPoints.PRODUCT_ID] = productId || dataPointEmptyValue; 10772 eventData[analyticsDataPoints.PRODUCT_NAME] = productData.name || dataPointEmptyValue; 10773 eventData[analyticsDataPoints.PAGE_DESCRIPTION] = productData.name || dataPointEmptyValue; 10774 eventData[analyticsDataPoints.PRODUCT_BADGE] = getProductBadge(productId) || dataPointEmptyValue; 10775 eventData[analyticsDataPoints.SHORT_DESCRIPTOR] = getIndividualProductShortDescriptor(getIndividualProductShortDescriptor(primaryShortDescriptor, secondaryShortDescriptor)); 10776 window.digitalData.trigger(analyticSubscriptionName.VIEW.QUICK_VIEW, eventData); 10777 10778 try { 10779 window.digitalData.trigger( 10780 'productQuickview.load-complete', 10781 $.extend( 10782 { 10783 brand: productData.productAttrs.brand, 10784 gender: productData.productAttrs.gender, 10785 kic: productData.kicId, 10786 priceFlag: productData.priceFlag, 10787 productId: productId, 10788 shortSku: shortSku 10789 }, 10790 openExtraAnalyticsData 10791 ) 10792 ); 10793 } catch (error) { 10794 // error 10795 } finally { 10796 openExtraAnalyticsData = {}; 10797 } 10798 } 10799 } 10800 } 10801
10802 function initQuickviewFromData(event) { 10803 var quickviewState = $.extend(event.detail, { 10804 $returnFocus: $(event.detail.returnFocus), 10805 $modal: $(quickviewModalSelectorClass), 10806 quickviewUrl: constructQuickviewURL(event.detail) 10807 }); 10808 10809 openQuickviewModal(quickviewState); 10810 isEditFromMiniBag = false; 10811 } 10812 10813 /* 10814 * TODO: Find a better way to pass additional data from an event that opens a QV model to analytics 10815 * We are removing analytics from the details to prevent side effects caused by it being there 10816 */ 10817 function initializeQuickviewFromClick(e) { 10818 e.preventDefault(); 10819 var $currentTarget = $(e.currentTarget); 10820 var isMySaves = $(e.currentTarget).parents('.mysaves-content').length > 0; 10821 if (isMySaves 10822 && featureSwitch.getDigitalDataFlagValue('quickview-mfe-checkout-my-saves') 10823 ) { 10824 var productId = $currentTarget.attr('data-productid'); 10825 var collectionId = $currentTarget.attr('data-collectionid'); 10826 var eventData = { 10827 productId: productId, 10828 collectionId: collectionId, 10829 returnFocus: $currentTarget, 10830 pageViewMethod: 'modal-quickview',
10831 productViewMethod: 'navigation-my saves' 10832 }; 10833 var mfeQVEvent = new CustomEvent('quickview:mfe:openModal', { detail: eventData }); 10834 document.dispatchEvent(mfeQVEvent); 10835 } else { 10836 try { 10837 // TODO: Find a better way to pass additional data from an event that opens a QV model to analytics 10838 openExtraAnalyticsData = e.originalEvent.analytics || {}; 10839 } catch (ewww) { 10840 openExtraAnalyticsData = {}; 10841 } 10842 initializeQuickview($currentTarget); 10843 isEditFromMiniBag = false; 10844 } 10845 } 10846 10847 function initializeQuickviewFromMySaves(e, data) { 10848 initializeQuickview(data.currentTarget); 10849 } 10850 10851 function initializeQuickviewFromCart(e) { 10852 e.preventDefault(); 10853 var $quickviewLink = $(e.currentTarget).parents('.product-template'); 10854 initializeQuickview($quickviewLink); 10855 isEditFromMiniBag = false; 10856 } 10857 10858 function initializeEditModalOnPDP(e) { 10859 var quickviewState = $.extend(e.detail, { 10860 $returnFocus: $(e.detail.returnFocus), 10861 $modal: $(quickviewModalSelectorClass), 10862 quickviewUrl: constructQuickviewURL(e.detail) 10863 }); 10864 10865 setEditModalAttributes(quickviewState); 10866 openQuickviewModal(quickviewState); 10867 isEditFromMiniBag = false; 10868 } 10869 10870 function initializeQuickview($quickviewLink) { 10871 var quickviewState = initQuickviewState($quickviewLink); 10872 10873 if (quickviewState.isEdit) { 10874 setEditModalAttributes(quickviewState); 10875 } else if (quickviewState.isExchange) { 10876 addExchangesModalClassesAndSku(quickviewState); 10877 } 10878 10879 openQuickviewModal(quickviewState); 10880 } 10881 10882 function initQuickviewState($quickviewLink) { 10883 var quickviewLinkAttrs = $quickviewLink.data(); 10884 var quickviewState = { 10885 $modal: $(quickviewModalSelectorClass), 10886 isExchange: $quickviewLink.hasClass(exchangesModalButtonClass), 10887 isEdit: $quickviewLink.hasClass(editQuickviewButtonClass) || ($quickviewLink.parents('.shopping-bag-list').length > 0) || quickviewLinkAttrs.isedit, 10888 isSoldOutInCart: $quickviewLink.data('status') === 'Sold Out', 10889 isStylitics: $quickviewLink.hasClass(styliticsQuickviewButtonClass), 10890 hasChangeLink: $quickviewLink.hasClass(changeLinkClass), 10891 seq: quickviewLinkAttrs.seq, 10892 sku: quickviewLinkAttrs.sku && quickviewLinkAttrs.sku.toString(), 10893 orderItemId: quickviewLinkAttrs.orderitemid || $quickviewLink.closest(detailsSelectorClass).attr('data-orderitemid'), 10894 productId: quickviewLinkAttrs.productid, 10895 collectionId: quickviewLinkAttrs.collectionid || quickviewLinkAttrs.collection, 10896 faceout: getFaceoutValue($quickviewLink, quickviewLinkAttrs), 10897 $returnFocus: getReturnFocus($quickviewLink) 10898 }; 10899 quickviewState.quickviewUrl = constructQuickviewURL(quickviewState); 10900 return quickviewState; 10901 } 10902 10903 function setEditModalAttributes(quickviewState) { 10904 quickviewState.$modal.attr('data-orderitemid', quickviewState.orderItemId); 10905 quickviewState.$modal 10906 .addClass(quickviewEditModalClass) 10907 .find(quickviewContentSelectorClass) 10908 .data('sku', quickviewState.sku); 10909 } 10910 10911 function addExchangesModalClassesAndSku(quickviewState) { 10912 if (quickviewState.hasChangeLink) { 10913 quickviewState.$modal.addClass(exchangesEditModalClass); 10914 } 10915 quickviewState.$modal 10916 .addClass(exchangesModalClass) 10917 .find(quickviewContentSelectorClass) 10918 .data('sku', quickviewState.sku); 10919 } 10920 function isMobile() { 10921 return (constants.anf('params.env.isMobile') === 'true'); 10922 } 10923 function isShoppingBagPage() { 10924 return $('body').hasClass('cart-view'); 10925 } 10926 10927 function setMfeQuickviewData(quickviewState) { 10928 var quickviewType = 'default'; 10929 if (quickviewState.isEdit) { 10930 quickviewType = 'edit'; 10931 } else if (quickviewState.isExchange) { 10932 quickviewType = 'exchange'; 10933 } 10934 var quickviewData = { 10935 productId: quickviewState.productId, 10936 collectionId: quickviewState.collectionId, 10937 categoryId: '', 10938 sku: quickviewState.sku, 10939 faceout: quickviewState.faceout, 10940 type: quickviewType, 10941 orderItemId: quickviewState.orderItemId, 10942 isStylitics: quickviewState.isStylitics, 10943 returnFocus: quickviewState.$returnFocus[0] 10944 }; 10945 return quickviewData; 10946 } 10947 10948 function checkForLocalStorageOverrideMFEQuickviewEnabled() { 10949 var isDebugModeEnabled = storage.session.get('debug_mode'); 10950 var hasLocalStorageOverrideMFEQuickviewEnabled = storage.session.get('pe-has-quickview-mfe-dev-enabled'); 10951 return isDebugModeEnabled && hasLocalStorageOverrideMFEQuickviewEnabled; 10952 } 10953 10954 function openQuickviewModal(quickviewState) { 10955 if (Utils.hasQuickviewMfeEnabled() && (checkForLocalStorageOverrideMFEQuickviewEnabled() || window.digitalData.get('flag.pe-has-quickview-mfe-dev-enabled', false))) { 10956 document.dispatchEvent(new CustomEvent('quickview:mfe:openModal', {detail: setMfeQuickviewData(quickviewState)})); 10957 } else { 10958 dsModal.openModal('product-quickview__modal'); 10959 quickviewState.$modal.find('.js-modal-heading').focus(); 10960 10961 // DS modal Close event 10962 quickviewState.$modal.on('dsModal:Close', function handleClose(closeEvent) { 10963 if ($(closeEvent.target).is(quickviewState.$modal)) { 10964 quickviewState.$returnFocus.focus(); 10965 resetQuickview(quickviewState); 10966 storage.local.remove('storedQuickviewProductId'); 10967 quickviewState.$modal.off('dsModal:Close', handleClose); 10968 if (publicMembers.isEditFromMiniBagFn() && !isMobile() && !isShoppingBagPage()) { 10969 var event = new Event('miniBag-toaster:open'); 10970 window.dispatchEvent(event); 10971 } 10972 } 10973 }); 10974 loadQuickview(quickviewState); 10975 } 10976 } 10977 10978 function resetQuickview(quickviewState) { 10979 // removing classes and content so next opening of quickview is fresh 10980 quickviewState.$modal 10981 .removeClass(quickviewEditModalClass) 10982 .find(quickviewContentSelectorClass).html(''); 10983 } 10984 10985 function loadQuickview(quickviewState) { 10986 $.ajax({ 10987 url: quickviewState.quickviewUrl 10988 }).done(function success(data) { 10989 setUpQuickview(quickviewState, data); 10990 }); 10991 } 10992 10993 function setUpQuickview(quickviewState, data) { 10994 var $quickviewContent = quickviewState.$modal.find(quickviewContentSelectorClass); 10995 $quickviewContent.html(data); 10996 if (quickviewState.$modal.attr('data-open') === 'false') { 10997 $quickviewContent.empty(); 10998 } else { 10999 if ($(quickviewSoldOutSelectorClass).length === 0) { 11000 setupInStockQuickview(quickviewState); 11001 } else { 11002 $quickviewContent.fadeIn(500); 11003 } 11004 11005 modal.default().init(); 11006 } 11007 } 11008 11009 function setupInStockQuickview(quickviewState) { 11010 if (quickviewState.isEdit) { 11011 setupEditModal(quickviewState); 11012 } else if (quickviewState.isExchange) { 11013 setupExchangeModal(quickviewState); 11014 } else if (quickviewState.isStylitics) { 11015 setupStyliticsClass(quickviewState); 11016 } 11017 11018 triggerQuickviewLoadEvents(quickviewState); 11019 quickviewState.$modal.find(quickviewProductSectionSelector).data('quickviewProductOpened', quickviewState.productId); 11020
11021 quickviewState.$modal.find(detailsSelectorClass).appendTo(quickviewDetailsWrapperHookSelector); 11022 accordion.default().init(); 11023 } 11024 11025 function setupEditModal(quickviewState) { 11026 var $button = quickviewState.$modal.find(addToBagButtonSelector); 11027 preSelectProductSize(quickviewState); 11028 statefulButton.default().changeState({button: $button[0], state: 'updateInitial'}); 11029 $button.attr('disabled', true); 11030 } 11031 11032 function setupExchangeModal(quickviewState) { 11033 quickviewState.$modal.find(exchangesSaveButtonSelector).attr('disabled', true); 11034 dropDown.default().init(); 11035 } 11036 11037 function setupStyliticsClass(quickviewState) { 11038 quickviewState.$modal.addClass(styliticsModalClass); 11039 } 11040 11041 function triggerQuickviewLoadEvents(quickviewState) { 11042 if (quickviewState.$modal.find('.details__modals .details__size-guide-modal').length > 0) { 11043 messageBus.trigger('productSizeCharts:load'); 11044 } 11045 messageBus.trigger('productPageImages:load', {URL: quickviewState.quickviewUrl, isQuickview: true}); 11046 messageBus.trigger('productPageInfo:load'); 11047 messageBus.trigger('productFabricCare:load', true); 11048 featureSwitchHelper.styleFitDesc(); 11049 } 11050 11051 function preSelectProductSize(quickviewState) { 11052 var $addToBagForm = quickviewState.$modal.find(addToBagFormSelector); 11053 var skuNum = quickviewState.$modal.find(quickviewContentSelectorClass).data('sku'); 11054 var $skuOptions = $addToBagForm.find('.product-page__skus option[value="' + skuNum + '"]'); 11055 var sizePrimary = $skuOptions.data('size-primary'); 11056 var sizeSecondary = $skuOptions.data('size-secondary'); 11057 11058 if (typeof sizePrimary !== 'undefined' && (sizePrimary.length > 0 || typeof sizePrimary === 'number')) { 11059 var sizeInputPrimary = $addToBagForm.find('.product-sizes input[value="' + sizePrimary + '"]'); 11060 if (sizeInputPrimary.length > 0) { 11061 sizeInputPrimary.prop('checked', true); 11062 sizeInputPrimary.closest(definingAttrSelectorClass).addClass('selected'); 11063 } 11064 } 11065 11066 if (typeof sizeSecondary !== 'undefined' && (sizePrimary.length > 0 || typeof sizePrimary === 'number')) { 11067 var sizeInputSecondary = $addToBagForm.find('.product-sizes input[value="' + sizeSecondary + '"]'); 11068 if (sizeInputSecondary.length > 0) { 11069 sizeInputSecondary.prop('checked', true); 11070 sizeInputSecondary.closest(definingAttrSelectorClass).addClass('selected'); 11071 } 11072 } 11073 } 11074 11075 function toggleUpdateButtonState(e, data) { 11076 var $editQuickview = $(quickviewEditModalSelector); 11077 if ($editQuickview.length > 0) { 11078 var currentSKU = $editQuickview.find(quickviewContentSelectorClass).data('sku'); 11079 var newSKU = data.sku; 11080 var disabledValue = newSKU && currentSKU && (newSKU !== currentSKU) ? false : 'disabled'; 11081 $editQuickview.find(addToBagButtonSelector).attr('disabled', disabledValue); 11082 } 11083 } 11084 11085 function updateViewFullProductLink(e, link) { 11086 var $fullProductLinkSelector = $(fullProductLinkSelector); 11087 var analyticsParams = $fullProductLinkSelector.parent().attr('data-analytics-params'); 11088 $(fullProductLinkSelector).attr('href', link + analyticsParams); 11089 } 11090 11091 function storeQuickviewState() { 11092 var initialProductId = $(quickviewProductSectionSelector).data('quickviewProductOpened'); 11093 storage.local.set('storedQuickviewProductId', initialProductId); 11094 } 11095 11096 function checkForStoredQuickviewState() { 11097 var storedQuickviewProductId = storage.local.get('storedQuickviewProductId'); 11098 var $quickviewButton = $(quickviewButtonSelectorClass + '[data-productid="' + storedQuickviewProductId + '"]'); 11099 if ($quickviewButton.length > 0) { 11100 $quickviewButton.click(); 11101 } 11102 } 11103 11104 var publicMembers = { 11105 initQuickviewFromData: initQuickviewFromData, 11106 'listen': function listen() { 11107 messageBus.on('mysaves:quickviewInitialize', initializeQuickviewFromMySaves); 11108 messageBus.on('quickview:updateFullProductLink', updateViewFullProductLink); 11109 messageBus.on('productSize:skuSelected', toggleUpdateButtonState); 11110 messageBus.on('quickviewLifecycle:complete', fireDelayedQuickviewEvents);
11111 messageBus.on('quickviewLogin:clicked', storeQuickviewState); 11112 messageBus.on('quickviewModules:loaded', checkForStoredQuickviewState); 11113 11114 window.addEventListener('quickview:openModal', initQuickviewFromData); 11115 window.addEventListener('quickview:openEditModalOnPDP', initializeEditModalOnPDP); 11116 }, 11117 11118 'bindEvents': function bindEvents() { 11119 $(document).on('click', shoppingCartQVSelectorClass, initializeQuickviewFromCart); 11120 $(document).on('click', quickviewButtonSelectorClass, initializeQuickviewFromClick); 11121 11122 // ACR-14504 MFE MiniBag initialize Quick View Event 11123 window.addEventListener('initializeQuickViewFromMiniBag', function initializeQuickviewFromMiniBag(e) { 11124 e.preventDefault(); 11125 var divRef = e.quickViewLinkAttrs.pdpDivElement; 11126 initializeQuickview($(divRef)); 11127 var quickviewLinkAttrs = $(divRef).data(); 11128 $(window).trigger('analytics.cartItemEdited', { 11129 'collectionId': quickviewLinkAttrs.collectionId, 11130 'longSku': quickviewLinkAttrs.longSku, 11131 'shortSku': quickviewLinkAttrs.sku, 11132 'inventoryAvailability': quickviewLinkAttrs.inventoryAvailability 11133 }); 11134 isEditFromMiniBag = true; 11135 }, false); 11136 }, 11137 'isEditFromMiniBagFn': function isEditFromMiniBagFn() { 11138 return isEditFromMiniBag; 11139 } 11140 }; 11141 11142 return publicMembers; 11143}); 11144 11145/*! 11146 Zoom 1.7.18 11147 license: MIT 11148 http://www.jacklmoore.com/zoom 11149*/ 11150(function ($) { 11151 var defaults = { 11152 url: false, 11153 callback: false, 11154 target: false, 11155 duration: 120, 11156 on: 'mouseover', // other options: grab, click, toggle 11157 touch: true, // enables a touch fallback 11158 onZoomIn: false, 11159 onZoomOut: false, 11160 magnify: 1 11161 }; 11162 11163 // Core Zoom Logic, independent of event listeners. 11164 $.zoom = function(target, source, img, magnify) { 11165 var targetHeight, 11166 targetWidth, 11167 sourceHeight, 11168 sourceWidth, 11169 xRatio, 11170 yRatio, 11171 offset, 11172 $target = $(target), 11173 position = $target.css('position'), 11174 $source = $(source); 11175 11176 // The parent element needs positioning so that the zoomed element can be correctly positioned within. 11177 target.style.position = /(absolute|fixed)/.test(position) ? position : 'relative'; 11178 target.style.overflow = 'hidden'; 11179 img.style.width = img.style.height = ''; 11180 11181 $(img) 11182 .addClass('zoomImg') 11183 .css({ 11184 position: 'absolute', 11185 top: 0, 11186 left: 0, 11187 opacity: 0, 11188 width: img.width * magnify, 11189 height: img.height * magnify, 11190 border: 'none', 11191 maxWidth: 'none', 11192 maxHeight: 'none' 11193 }) 11194 .appendTo(target); 11195 11196 return { 11197 init: function() { 11198 targetWidth = $target.outerWidth(); 11199 targetHeight = $target.outerHeight(); 11200 11201 if (source === target) { 11202 sourceWidth = targetWidth; 11203 sourceHeight = targetHeight; 11204 } else { 11205 sourceWidth = $source.outerWidth(); 11206 sourceHeight = $source.outerHeight(); 11207 } 11208 11209 xRatio = (img.width - targetWidth) / sourceWidth; 11210 yRatio = (img.height - targetHeight) / sourceHeight; 11211 11212 offset = $source.offset(); 11213 }, 11214 move: function (e) { 11215 var left = (e.pageX - offset.left), 11216 top = (e.pageY - offset.top); 11217 11218 top = Math.max(Math.min(top, sourceHeight), 0); 11219 left = Math.max(Math.min(left, sourceWidth), 0); 11220 11221 img.style.left = (left * -xRatio) + 'px'; 11222 img.style.top = (top * -yRatio) + 'px'; 11223 } 11224 }; 11225 }; 11226 11227 $.fn.zoom = function (options) { 11228 return this.each(function () { 11229 var 11230 settings = $.extend({}, defaults, options || {}), 11231 //target will display the zoomed image 11232 target = settings.target && $(settings.target)[0] || this, 11233 //source will provide zoom location info (thumbnail) 11234 source = this, 11235 $source = $(source), 11236 img = document.createElement('img'), 11237 $img = $(img), 11238 mousemove = 'mousemove.zoom', 11239 clicked = false, 11240 touched = false; 11241 11242 // If a url wasn't specified, look for an image element. 11243 if (!settings.url) { 11244 var srcElement = source.querySelector('img'); 11245 if (srcElement) { 11246 settings.url = srcElement.getAttribute('data-src') || srcElement.currentSrc || srcElement.src; 11247 } 11248 if (!settings.url) { 11249 return; 11250 } 11251 } 11252 11253 $source.one('zoom.destroy', function(position, overflow){ 11254 $source.off(".zoom"); 11255 target.style.position = position; 11256 target.style.overflow = overflow; 11257 img.onload = null; 11258 $img.remove(); 11259 }.bind(this, target.style.position, target.style.overflow)); 11260 11261 img.onload = function () { 11262 var zoom = $.zoom(target, source, img, settings.magnify); 11263 11264 function start(e) { 11265 zoom.init(); 11266 zoom.move(e); 11267 11268 // Skip the fade-in for IE8 and lower since it chokes on fading-in 11269 // and changing position based on mousemovement at the same time. 11270 $img.stop() 11271 .fadeTo($.support.opacity ? settings.duration : 0, 1, $.isFunction(settings.onZoomIn) ? settings.onZoomIn.call(img) : false); 11272 } 11273 11274 function stop() { 11275 $img.stop() 11276 .fadeTo(settings.duration, 0, $.isFunction(settings.onZoomOut) ? settings.onZoomOut.call(img) : false); 11277 } 11278 11279 // Mouse events 11280 if (settings.on === 'grab') { 11281 $source 11282 .on('mousedown.zoom', 11283 function (e) { 11284 if (e.which === 1) { 11285 $(document).one('mouseup.zoom', 11286 function () { 11287 stop(); 11288 11289 $(document).off(mousemove, zoom.move); 11290 } 11291 ); 11292 11293 start(e); 11294 11295 $(document).on(mousemove, zoom.move); 11296 11297 e.preventDefault(); 11298 } 11299 } 11300 ); 11301 } else if (settings.on === 'click') { 11302 $source.on('click.zoom', 11303 function (e) { 11304 if (clicked) { 11305 // bubble the event up to the document to trigger the unbind. 11306 return; 11307 } else { 11308 clicked = true; 11309 start(e); 11310 $(document).on(mousemove, zoom.move); 11311 $(document).one('click.zoom', 11312 function () { 11313 stop(); 11314 clicked = false;
11315 $(document).off(mousemove, zoom.move); 11316 } 11317 ); 11318 return false; 11319 } 11320 } 11321 ); 11322 } else if (settings.on === 'toggle') { 11323 $source.on('click.zoom', 11324 function (e) { 11325 if (clicked) { 11326 stop(); 11327 } else { 11328 start(e); 11329 } 11330 clicked = !clicked; 11331 } 11332 ); 11333 } else if (settings.on === 'mouseover') { 11334 zoom.init(); // Preemptively call init because IE7 will fire the mousemove handler before the hover handler. 11335 11336 $source 11337 .on('mouseenter.zoom', start) 11338 .on('mouseleave.zoom', stop) 11339 .on(mousemove, zoom.move); 11340 } 11341 11342 // Touch fallback 11343 if (settings.touch) { 11344 $source 11345 .on('touchstart.zoom', function (e) { 11346 e.preventDefault(); 11347 if (touched) { 11348 touched = false; 11349 stop(); 11350 } else { 11351 touched = true; 11352 start( e.originalEvent.touches[0] || e.originalEvent.changedTouches[0] ); 11353 } 11354 }) 11355 .on('touchmove.zoom', function (e) { 11356 e.preventDefault(); 11357 zoom.move( e.originalEvent.touches[0] || e.originalEvent.changedTouches[0] ); 11358 }) 11359 .on('touchend.zoom', function (e) { 11360 e.preventDefault(); 11361 if (touched) { 11362 touched = false; 11363 stop(); 11364 } 11365 }); 11366 } 11367 11368 if ($.isFunction(settings.callback)) { 11369 settings.callback.call(img); 11370 } 11371 }; 11372 11373 img.src = settings.url; 11374 }); 11375 }; 11376 11377 $.fn.zoom.defaults = defaults; 11378}(window.jQuery)); 11379 11380redesign.define("jquery_zoom", function(){}); 11381 11382redesign.define('productModules/product/quickview/quickviewImages',[ 11383 'coreClass/ANFImage', 11384 'jquery_zoom' 11385], function ProductQuickviewImages(ANFImage) { 11386 'use strict'; 11387 11388 var quickviewNonUGCMainImageWapperSelectorClass = '.product-main-images__image:not(.product-main-images__image--ugc)'; 11389 var quickviewModalSelectorClass = '.product-quickview__modal'; 11390 var tabFocusClass = 'tab-focused'; 11391 11392 var isClick = null; 11393 var isMobile = ANF.params.env.isMobile; 11394 var magnifyWidth = 1200; 11395 11396 var messageBus = $(ANF); 11397 11398 function handleZoomImageBlur(e) { 11399 var image = $(e.currentTarget).removeClass(tabFocusClass); 11400 image.scrollTop(0); 11401 image.scrollLeft(0); 11402 } 11403 11404 function setupMagnifyQuickviewImage(e, productSection) { 11405 if (isMobile === 'true') { 11406 return; 11407 } 11408 11409 var getSetZoomAltText = function getSetZoomAltText(alt) { 11410 return function setZoomAltText() { 11411 $(this).attr('alt', alt); 11412 }; 11413 }; 11414 11415 $(productSection).find(quickviewNonUGCMainImageWapperSelectorClass).each(function iterateProductImages(_i, imageContainer) { 11416 var $imageContainer = $(imageContainer); 11417 var $productImage = $imageContainer.find('img'); 11418 var altText = $productImage.attr('alt'); 11419 11420 var magnifyImageSrc = ANFImage.instance() 11421 .setImageName($productImage.data().imageId) 11422 .setWidth(magnifyWidth) 11423 .url; 11424 11425 $imageContainer.zoom({ 11426 callback: getSetZoomAltText(altText), 11427 duration: 300, // Fade-in blocked for lte IE 11428 icon: false, 11429 url: magnifyImageSrc 11430 }); 11431 11432 $imageContainer.on('focusin', zoomFocus); 11433 $imageContainer.on('focusout', handleZoomImageBlur); 11434 }); 11435 } 11436 11437 function zoomFocus(e) { 11438 setTimeout( function setTimeout() { 11439 if (!isClick) { 11440 var image = $(e.target); 11441 image.addClass(tabFocusClass); 11442 image.scrollTop(0); 11443 image.scrollLeft(0); 11444 image.find('.zoomImg').css({'top': 0, 'left': 0}); 11445 } 11446 }, 150); 11447 } 11448 11449 var publicMembers = { 11450 // This function should only be called for setting private module vars during unit tests. P.S. I hate this. 11451 unitTestSetup: function unitTestSetup(obj) { 11452 isMobile = obj.isMobile; 11453 magnifyWidth = obj.magnifyWidth; 11454 }, 11455 11456 'listen': function listen() { 11457 messageBus.on('quickviewImages:mainImageLoaded', setupMagnifyQuickviewImage); 11458 }, 11459 11460 'bindEvents': function bindEvents() { 11461 $(document).on('click', quickviewModalSelectorClass, function click() {isClick = true; }); 11462 $(document).on('keydown', quickviewModalSelectorClass, function keydown() {isClick = false; }); 11463 } 11464 }; 11465 return publicMembers; 11466}); 11467 11468redesign.define('productModules/product/quickview/quickviewLifecycle',[], function QuickviewLifecycle() { 11469 'use strict'; 11470 11471 var quickviewButtonSelectorClass = '.product-quickview__button, button.js-pc-edit-button'; 11472 var shoppingCartQVImageSelector = '#checkout-cart .product-image:not(.gift-card-image-container) + .product-image-overlay'; 11473 var quickviewModalSelectorClass = '.product-quickview__modal'; 11474 var soldOutSelector = '.product-quickview__sold-out'; 11475 var quickviewURLMatcher = 'QuickView'; 11476 11477 var messageBus = $(ANF); 11478 11479 var quickviewCompleteEvents = []; 11480 var $quickviewModal; 11481 11482 function onAjaxComplete(event, request, settings) { 11483 if (settings.url.indexOf(quickviewURLMatcher) !== -1) { 11484 messageBus.trigger('quickviewLifecycle:postAjax', [$quickviewModal]); 11485 11486 if ($quickviewModal.find(soldOutSelector).length) { 11487 messageBus.trigger('quickviewLifecycle:complete', [$quickviewModal]); 11488 } 11489 } 11490 } 11491 11492 function onAjaxSend(event, request, settings) { 11493 if (settings.url.indexOf(quickviewURLMatcher) !== -1) { 11494 messageBus.trigger('quickviewLifecycle:preAjax', [$quickviewModal]); 11495 } 11496 } 11497 11498 function onCompleteEvent(event) { 11499 quickviewCompleteEvents.push(event); 11500 11501 if (quickviewCompleteEvents.length > 1) { 11502 messageBus.trigger('quickviewLifecycle:complete', [$quickviewModal]); 11503 document.dispatchEvent(new CustomEvent('quickview:recs:openModal')); 11504 } 11505 } 11506 11507 function onQuickviewClose(event) { 11508 if ($(event.target).is($quickviewModal)) { 11509 messageBus.trigger('quickviewLifecycle:close', [$quickviewModal]); 11510 11511 $(document).off('ajaxSend', onAjaxSend); 11512 $(document).off('ajaxComplete', onAjaxComplete); 11513 $quickviewModal.off('dsModal:Close'); 11514 11515 quickviewCompleteEvents = []; 11516 } 11517 } 11518
11519 function onQuickviewOpen() { 11520 $quickviewModal = $(quickviewModalSelectorClass); 11521 messageBus.trigger('quickviewLifecycle:open', [$quickviewModal]); 11522 11523 $(document).on('ajaxSend', onAjaxSend); 11524 $(document).on('ajaxComplete', onAjaxComplete); 11525 11526 $quickviewModal.on('dsModal:Close', onQuickviewClose); 11527 11528 messageBus.one('thumbnailSlider:InitComplete', onCompleteEvent); 11529 messageBus.one('quickviewImages:mainImageLoaded', onCompleteEvent); 11530 } 11531 11532 return { 11533 onQuickviewOpen: onQuickviewOpen, 11534 bindEvents: function bindEvents() { 11535 $(document).on('click', quickviewButtonSelectorClass, onQuickviewOpen); 11536 $(document).on('click', shoppingCartQVImageSelector, onQuickviewOpen); 11537 }, 11538 listen: function listen() { 11539 messageBus.on('mysaves:quickviewInitialize', onQuickviewOpen); 11540 window.addEventListener('quickview:openModal', onQuickviewOpen); 11541 window.addEventListener('quickview:openEditModalOnPDP', onQuickviewOpen); 11542 } 11543 }; 11544}); 11545 11546redesign.define('productModules/product/quickview/quickviewPreload',[ 11547 'productServices/product/template',
11548 'productModules/helpers/curry', 11549 'anfUtil/testFramework', 11550 'coreClass/ANFImage', 11551 'productModules/helpers/Utils' 11552], function QuickviewPreload(template, curry, test, ANFImage, Utils) { 11553 'use strict'; 11554 11555 var quickviewButtonSelector = '.product-quickview__button, button.js-pc-edit-button'; 11556 var shoppingCartQVImageSelector = '#checkout-cart .product-image:not(.gift-card-image-container) + .product-image-overlay'; 11557 11558 var quickviewModalSelector = '.product-quickview__modal'; 11559 var quickviewPreloadContentSelector = '.product-quickview__preload-content'; 11560 11561 var partialQuickviewTemplate = curry(template.renderTemplate)('quickviewPreload'); 11562 11563 var messageBus = $(ANF); 11564 11565 function initPreload(event) { 11566 var $button = $(event.currentTarget); 11567 var $container = $button.closest('.product-template'); 11568 11569 // On recommendations, the product container is actually nested under the button 11570 // On shop this look, the product container is product-card, not product-template 11571 // On stylitics outfitting and the grid mixer, the product container is the button :mind_blown: 11572 if (!$container.length) { 11573 if ($button.hasClass('stylitics-widget-item-container')) { 11574 $container = $button; 11575 var styliticsPreloadData = gatherPreloadDataForStylitics($container); 11576 } else if ($button.hasClass('grid-mixer-button')) { 11577 $container = $button; 11578 var gridMixerPreloadData = gatherPreloadDataForGridMixer($container); 11579 } else { 11580 $container = $button.find('.product-template, .product-card'); 11581 } 11582 } 11583 11584 // If there's no recognized container to pull image/data from, just revert to original qv 11585 if ($container.length) { 11586 // check to see if the stylitics object has been compiled. if not call it with the contaner. 11587 var preloadDataObj = styliticsPreloadData || gridMixerPreloadData || collectProductData($container, $button); 11588 renderPreload(preloadDataObj); 11589 } else { 11590 teardownPreload(); 11591 } 11592 } 11593 11594 function initPreloadFromMySaves(event, data) { 11595 initPreload(data); 11596 } 11597 11598 function initPreloadFromData(event) { 11599 var data = event.detail; 11600 var preloadImage = ANFImage.instance() 11601 .setImageName(data.imageId) 11602 .setWidth(400) 11603 .url; 11604 11605 var preloadImageElement = document.createElement('img'); 11606 preloadImageElement.setAttribute('src', preloadImage); 11607 preloadImageElement.setAttribute('class', 'product-card__image'); 11608 11609 var preloadDataObj = $.extend(data, { 11610 img: preloadImageElement.outerHTML, 11611 qtyLabel: Utils.getANFObject().messaging.product.GLB_QTY, 11612 atbLabel: Utils.getANFObject().messaging.product.GLB_ADDTOBAG 11613 }); 11614 11615 renderPreload(preloadDataObj); 11616 } 11617 11618 function collectProductData($container, $button) { 11619 var img = $container.find('img'); 11620 var shortDescription = $container.find('.short-description, .product-card__description'); 11621 var $productName = $container.find('.product-name'); 11622 var $productPrice = $container.find('.product-price'); 11623 var productBrand = $button.data('product-brand') || ANF.params.urls.STATIC_DIR; 11624 11625 // Make sure we don't add a screen reader version of this text, usually on shopping cart 11626 $productName = $productName.children().length < 2 ? $productName : $productName.children().not('.screen-reader-text'); 11627 11628 // Some qv link containers don't match the right size of the qv price label 11629 $productPrice.find('.product-price-font-size').addClass('ds-override'); 11630 11631 return { 11632 img: test.type.defined(img[0], false) ? img[0].outerHTML : '', 11633 shortDescription: shortDescription.text(), 11634 productBrand: productBrand, 11635 productName: $productName.text(), 11636 productPrice: test.type.defined($productPrice[0], false) ? $productPrice[0].outerHTML : '', 11637 qtyLabel: ANF.messaging.product.GLB_QTY, 11638 atbLabel: ANF.messaging.product.GLB_ADDTOBAG 11639 }; 11640 } 11641 11642 function gatherPreloadDataForStylitics($container) { 11643 // stylitics injects line break characters... lol i know. 11644 var productName = $container.find('.stylitics-widget-item-name a').text().trim() || ''; 11645 var productPrice = $container.find('.stylitics-widget-item-price').text().trim() || ''; 11646 var imageSource = ANFImage.instance() 11647 .setImageName($container.data('item-remote-id') + '_prod1') 11648 .setWidth(400) 11649 .url; 11650 11651 var preloadImageElement = document.createElement('img'); 11652 preloadImageElement.setAttribute('src', imageSource); 11653 preloadImageElement.setAttribute('class', 'product-card__image'); 11654 11655 return { 11656 img: preloadImageElement.outerHTML, 11657 productName: productName, 11658 productPrice: productPrice, 11659 qtyLabel: ANF.messaging.product.GLB_QTY, 11660 atbLabel: ANF.messaging.product.GLB_ADDTOBAG 11661 }; 11662 } 11663 11664 function gatherPreloadDataForGridMixer($container) { 11665 var img = $container.find('img'); 11666 var shortDescription = $container.data('shortDesc'); 11667 var productName = $container.data('productName'); 11668 var productBrand = $container.data('brand') || ANF.params.urls.STATIC_DIR; 11669 11670 /* 11671 Not populating "productPrice" for the grid mixer's preload data because we don't have the original product card 11672 to source the price from (there is extensive logic on the card to determine what the displayed price looks like 11673 for various pricing scenarios, and duplicating it here just for the sake of preload display would be overkill) 11674 */ 11675 11676 return { 11677 img: test.type.defined(img[0], false) ? img[0].outerHTML : '',
11678 shortDescription: shortDescription, 11679 productBrand: productBrand, 11680 productName: productName, 11681 productPrice: '', 11682 qtyLabel: ANF.messaging.product.GLB_QTY, 11683 atbLabel: ANF.messaging.product.GLB_ADDTOBAG 11684 }; 11685 } 11686 11687 function crossfadePreload(event, $modal) { 11688 $modal.addClass('crossfade'); 11689 } 11690 11691 function renderPreload(data) { 11692 $(quickviewPreloadContentSelector).html(partialQuickviewTemplate(data)); 11693 } 11694 11695 function teardownPreload(event, $modal) { 11696 var $modalCopy = $modal ? $modal : $(quickviewModalSelector); 11697 11698 $modalCopy.find(quickviewPreloadContentSelector).html(''); 11699 $modalCopy.removeClass('crossfade'); 11700 } 11701 11702 var publicMembers = { 11703 initPreloadFromData: initPreloadFromData, 11704 bindEvents: function bindEvents() { 11705 $(document).on('click', quickviewButtonSelector, initPreload); 11706 $(document).on('click', shoppingCartQVImageSelector, initPreload); 11707 }, 11708 listen: function listen() { 11709 messageBus.on('mysaves:quickviewInitialize', initPreloadFromMySaves); 11710 messageBus.on('quickviewLifecycle:complete', crossfadePreload); 11711 messageBus.on('quickviewLifecycle:close', teardownPreload); 11712 11713 window.addEventListener('quickview:openModal', initPreloadFromData); 11714 window.addEventListener('quickview:openEditModalOnPDP', initPreloadFromData); 11715 } 11716 }; 11717 11718 return publicMembers; 11719}); 11720 11721redesign.define('productSections/product/quickviewModules',[ 11722 'coreSectionHelpers', 11723 'productModules/product/quickview/quickview', 11724 'productModules/helpers/analyticsHelper', 11725 'productModules/product/quickview/quickviewImages', 11726 'productModules/product/quickview/quickviewLifecycle', 11727 'productModules/product/quickview/quickviewPreload' 11728], function QuickviewModules(coreSectionHelpers, quickview, analyticsHelper, quickviewImages, quickviewLifecycle, quickviewPreload) { 11729 'use strict'; 11730 11731 var privateMembers = { 11732 'DEFAULT_MODULES': { 11733 'quickview': quickview, 11734 'quickviewImages': quickviewImages, 11735 'quickviewLifecycle': quickviewLifecycle, 11736 'quickviewPreload': quickviewPreload, 11737 'analyticsHelper': analyticsHelper 11738 }, 11739 'GLOBAL_SCOPE': '', 11740 'MODULES': {}, 11741 'parentPage': '' 11742 }; 11743 11744 return function scope(settings) { 11745 privateMembers.GLOBAL_SCOPE = settings.scope; 11746 privateMembers.parentPage = settings.parentPage; 11747 privateMembers.MODULES = privateMembers.DEFAULT_MODULES; 11748 11749 if (typeof settings.modules === 'object') { 11750 $.extend(true, privateMembers.MODULES, settings.modules); 11751 } 11752 11753 var messageBus = $(ANF); 11754 11755 return { 11756 'init': function init() { 11757 coreSectionHelpers.init({ 11758 'modules': privateMembers.MODULES, 11759 'scope': privateMembers.GLOBAL_SCOPE 11760 }); 11761 11762 messageBus.trigger('quickviewModules:loaded'); 11763 } 11764 }; 11765 }; 11766}); 11767 11768redesign.define('productPartial/pdp_preferredStore',['handlebars','dsPartial/selector','dsPartial/logo','wcsHelper/i18n','dsPartial/omni-info'], function(Handlebars) { 11769 11770this["ANF"] = this["ANF"] || {}; 11771this["ANF"]["templates"] = this["ANF"]["templates"] || {}; 11772 11773Handlebars.registerPartial("pdp_preferredStore", this["ANF"]["templates"]["pdp_preferredStore"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { 11774 var stack1, alias1=container.lambda, alias2=container.escapeExpression, alias3=depth0 != null ? depth0 : (container.nullContext || {}); 11775 11776 return " <span class=\"selector-map-icon\" aria-hidden=\"true\"></span>\n <span class=\"description\" data-brand=\"" 11777 + alias2(alias1((depth0 != null ? depth0.brand : depth0), depth0)) 11778 + "\">\n" 11779 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.showBranding : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11780 + " <span>" 11781 + alias2(alias1((depth0 != null ? depth0.storeName : depth0), depth0)) 11782 + "</span>\n" 11783 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.showBranding : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11784 + " </span>\n"; 11785},"2":function(container,depth0,helpers,partials,data) { 11786 var stack1; 11787 11788 return ((stack1 = container.invokePartial(partials.logo,depth0,{"name":"logo","hash":{"icon-aria-label":(depth0 != null ? depth0.brandName : depth0),"type":"horizontal","brand":(depth0 != null ? depth0.brand : depth0)},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); 11789},"4":function(container,depth0,helpers,partials,data) {
11790 return " <span class=\"screen-reader-text\">" 11791 + container.escapeExpression(container.lambda((depth0 != null ? depth0.checkStoreAvailability : depth0), depth0)) 11792 + "</span>\n"; 11793},"6":function(container,depth0,helpers,partials,data) { 11794 var stack1; 11795 11796 return " <div class=\"preferred-store-module__choose-size-message item-availability-message\">\n" 11797 + ((stack1 = container.invokePartial(partials["omni-info"],(depth0 != null ? depth0.selectSizeCardInfo : depth0),{"name":"omni-info","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 11798 + " </div>\n"; 11799},"8":function(container,depth0,helpers,partials,data) { 11800 var stack1; 11801 11802 return " <div class=\"preferred-store-module-info item-availability-message\">\n <div class=\"preferred-store-module__store-available preferred-store-module__inventory-status\">\n" 11803 + ((stack1 = container.invokePartial(partials["omni-info"],(depth0 != null ? depth0.isAvailableCardInfo : depth0),{"name":"omni-info","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 11804 + " </div>\n </div>\n"; 11805},"10":function(container,depth0,helpers,partials,data) { 11806 var stack1; 11807 11808 return " <div class=\"preferred-store-module-info item-availability-message\">\n <div class=\"preferred-store-module__store-not-available preferred-store-module__inventory-status not-available\">\n" 11809 + ((stack1 = container.invokePartial(partials["omni-info"],(depth0 != null ? depth0.noStockCardInfo : depth0),{"name":"omni-info","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 11810 + " </div>\n </div>\n"; 11811},"12":function(container,depth0,helpers,partials,data) { 11812 var stack1; 11813 11814 return " <div class=\"preferred-store-module-info item-availability-message\">\n <div class=\"preferred-store-module__store-available preferred-store-module__inventory-status\">\n" 11815 + ((stack1 = container.invokePartial(partials["omni-info"],(depth0 != null ? depth0.popinsCardInfo : depth0),{"name":"omni-info","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 11816 + " </div>\n </div>\n"; 11817},"14":function(container,depth0,helpers,partials,data) { 11818 var stack1; 11819 11820 return " <div class=\"preferred-store-module-info item-availability-message\">\n <div class=\"preferred-store-module__store-not-available preferred-store-module__inventory-status not-available\">\n" 11821 + ((stack1 = container.invokePartial(partials["omni-info"],(depth0 != null ? depth0.noPickupCardInfo : depth0),{"name":"omni-info","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 11822 + " </div>\n </div>\n"; 11823},"16":function(container,depth0,helpers,partials,data) { 11824 var stack1; 11825 11826 return " <div class=\"preferred-store-module-info item-availability-message\">\n <div class=\"preferred-store-module__store-not-available preferred-store-module__inventory-status not-available\">\n" 11827 + ((stack1 = container.invokePartial(partials["omni-info"],(depth0 != null ? depth0.notAvailableCardInfo : depth0),{"name":"omni-info","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 11828 + " </div>\n </div>\n"; 11829},"18":function(container,depth0,helpers,partials,data) { 11830 return " <p class=\"store-messaging item-availability-message\">" 11831 + container.escapeExpression(container.lambda((depth0 != null ? depth0.storeMessage : depth0), depth0)) 11832 + "</p>\n"; 11833},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { 11834 var stack1, alias1=container.lambda, alias2=container.escapeExpression, alias3=depth0 != null ? depth0 : (container.nullContext || {}); 11835 11836 return "<div class=\"preferred-store-module__template\">\n <div class=\"preferred-store-module\" data-storeid=\"" 11837 + alias2(alias1((depth0 != null ? depth0.storeID : depth0), depth0)) 11838 + "\" data-store-zip=\"" 11839 + alias2(alias1((depth0 != null ? depth0.zipCode : depth0), depth0)) 11840 + "\" data-store-city=\"" 11841 + alias2(alias1((depth0 != null ? depth0.city : depth0), depth0)) 11842 + "\">\n" 11843 + ((stack1 = container.invokePartial(partials.selector,depth0,{"name":"selector","hash":{"data-
11843prop":"data-modal='find-store-form__modal-wrapper'","class-list":"js-open-modal find-store-form__modal-wrapper"},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") 11844 + " <div class=\"preferred-store-module__content\">\n <div class=\"preferred-store-module__popins-error\" data-property=\"GLB_PREFERRED_STORE_ITEM_ERROR\">\n " 11845 + ((stack1 = helpers.i18n.call(alias3,"storelocator.GLB_PREFERRED_STORE_ITEM_ERROR",{"name":"i18n","hash":{},"data":data})) != null ? stack1 : "") 11846 + "\n </div>\n\n" 11847 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.isSelectSizeMsg : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11848 + "\n" 11849 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.isAvailableInStore : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11850 + "\n" 11851 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.isNotInStock : depth0),{"name":"if","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11852 + "\n" 11853 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.isStorePopinsEligible : depth0),{"name":"if","hash":{},"fn":container.program(12, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11854 + "\n" 11855 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.isNotAvailableForPickup : depth0),{"name":"if","hash":{},"fn":container.program(14, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11856 + "\n" 11857 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.isNotAvailableAtLocation : depth0),{"name":"if","hash":{},"fn":container.program(16, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11858 + "\n" 11859 + ((stack1 = helpers["if"].call(alias3,(depth0 != null ? depth0.storeMessage : depth0),{"name":"if","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") 11860 + " </div>\n </div>\n</div>\n"; 11861},"usePartial":true,"useData":true})); 11862 11863return this["ANF"]; 11864 11865}); 11866redesign.define('productModules/popins/preferredStore',[ 11867 'anfUtil/testFramework', 11868 'coreServices/findastore/productAvailability', 11869 'coreModules/brandedPage', 11870 'productServices/product/brandMap', 11871 'anfcore/components/modal', 11872 'productModules/helpers/Utils', 11873 'jsCookie', 11874 'productPartial/pdp_preferredStore', 11875 'coreServices/geo/core' 11876], function PreferredStore( 11877 test, 11878 productAvailability, 11879 brandedPage, 11880 brandMap, 11881 modal, 11882 Utils, 11883 cookies, 11884 productPartialPdpPreferredStore, 11885 GeoCore 11886) { 11887 'use strict'; 11888 11889 11890 var productSectionSelector = '.product-page-v2-modules'; 11891 var availabilityWrapper = '.product__actions--availability-wrapper'; 11892 var changeButton = '.preferred-store-module .selector, .preferred-store-module__change-store'; 11893 var preferredStoreWidget = '.preferred-store-module'; 11894 var selectedSizeVariant1 = '.product-sizes__select option:selected'; 11895 var selectedSizeVariant2 = '.product-details__size-select option:selected'; 11896 var selectedSizeVariant3 = '.size-select option:selected'; 11897 var skuSelect = '.product-page__skus'; 11898 var reserveError = '.preferred-store-module__popins-error'; 11899 var preferredStoreBadgeSelectorClass = '.preferred-store-module__badge'; 11900 var isAvailable = '.defining-attribute-value.selected'; 11901 var catalogProductSelectorClass = '.catalog-product'; 11902 var productViewSelectorClass = '.product-view'; 11903 var productPageV2SelectorClass = '.product-page-v2'; 11904 var preferredStoreDefaultClass = 'preferred-store-module__default-state'; 11905 var itemAvailabilitySelectorClass = '.item-availability-message'; 11906 11907 var storeObj = {}; 11908 var scopeData = {}; 11909 11910 function getPhysicalStore() { 11911 GeoCore.getService('default'); 11912 // get from uPref cookie 11913 brandedPage.getStoreFromCookie() 11914 .done(function done(data) { 11915 storeObj = data; 11916 widgetSetup(); 11917 }) 11918 .fail(function fail() { 11919 // get from preferred store 11920 brandedPage.getStoreFromPreferredStore() 11921 .done(function failDone(data) { 11922 storeObj = data; 11923 widgetSetup(); 11924 }) 11925 .fail(function failFail() { 11926 // get from geolocation 11927 brandedPage.getGeolocatedStore() 11928 .done(function failFailDone(data) { 11929 storeObj = data; 11930 widgetSetup(); 11931 }) 11932 .fail(function failFailFail() { 11933 storeObj = {}; 11934 widgetSetup(); 11935 }); 11936 }); 11937 }); 11938 } 11939 11940 // eslint-disable-next-line consistent-return 11941 function widgetSetup() { 11942 var dataObj = {}; 11943 // In order to maintain focus after closing modal & re-rendering wrapper 11944 var selectorHasFocus = $(availabilityWrapper).find('.selector').is(':focus'); 11945 11946 // This length used to be fetched in privateMembers, but now that this element is 11947 // rendered on client-side after an API call, it wouldn't exist when this check ran 11948 // So, we look in the DOM for this length HERE, instead of in privateMembers 11949 // This is kind of a hack, but at least it's just tweaking an existing hack, not 11950 // creating a new one. 11951 if ($('.ds-rs-facet-tile-location__wrapper').length) { 11952 return false;
11953 } 11954 11955 if (test.string.not.empty(storeObj.storeId, false)) { 11956 $(availabilityWrapper).html(productPartialPdpPreferredStore.templates.pdp_preferredStore(buildPreferredWidgetObj())); 11957 11958 updateProductAvailabilityHandler(); 11959 11960 $(preferredStoreBadgeSelectorClass).addClass(storeObj.brand); 11961 } else { 11962 // set up the defaults 11963 dataObj.isSelectSizeMsg = false; // default to false cuz it should only be seen when geo located 11964 dataObj.storeName = ANF.messaging.catalogV2.defaultStoreAvailability; // set default 'store name' 11965 dataObj.defaultClass = preferredStoreDefaultClass; // set class for 'store name' styling 11966 dataObj.showBranding = false; 11967 11968 $(availabilityWrapper).html(productPartialPdpPreferredStore.templates.pdp_preferredStore(dataObj)); 11969 } 11970 11971 if (selectorHasFocus) { 11972 $(availabilityWrapper).find('.selector').focus(); 11973 } 11974 $(availabilityWrapper).fadeIn(); 11975 reInitializeModal(); 11976 } 11977 11978 function showPreferredStoreModal(e) { 11979 e.preventDefault(); 11980 var shortSku = $(selectedSizeVariant1).val(); 11981 var $productSection = $(e.target).closest(productSectionSelector); 11982 var templateData = {}; 11983 11984 if (!shortSku && $(selectedSizeVariant2).length > 0) { 11985 shortSku = $(selectedSizeVariant2).val(); 11986 } else if (!shortSku && $(selectedSizeVariant3).length > 0) { 11987 shortSku = $(selectedSizeVariant3).val(); 11988 } else if ($(skuSelect).length > 0) { 11989 shortSku = $productSection.find(skuSelect).val(); 11990 } 11991 11992 templateData.shortSku = shortSku ? shortSku : ''; 11993 11994 $(ANF).trigger('FISModal:build', templateData); 11995 } 11996 11997 function buildPreferredWidgetObj(msgData) { 11998 var dataObj = {}; 11999 12000 if (test.string.not.empty(storeObj.storeId, false)) { 12001 dataObj = { 12002 'brand': brandMap.dsBrandFromBrand(storeObj.brand), 12003 'brandName': brandMap.nameFromBrand(storeObj.brand), 12004 'storeName': storeObj.storeName, 12005 'storeID': storeObj.storeId, 12006 'zipCode': storeObj.zipCode, 12007 'city': storeObj.city, 12008 'showBranding': true 12009 }; 12010 } 12011 12012 dataObj.isSelectSizeMsg = true; 12013 12014 if (msgData) { 12015 dataObj = $.extend({}, dataObj, msgData); 12016 } 12017 12018 // Once we know the FIS options, we need to set the store card's content and type before breaking. 12019 if (dataObj.isSelectSizeMsg) { 12020 dataObj.selectSizeCardInfo = { 12021 'state': 'initial', 12022 'main-text': { 12023 'text': ANF.messaging.popins.GLB_SELECT_SIZE_TO_SEE_INSTORE_AVAILABILITY 12024 } 12025 }; 12026 } 12027 12028 if (dataObj.isAvailableInStore) { 12029 dataObj.isAvailableCardInfo = { 12030 'state': 'available', 12031 'main-text': { 12032 'text': ANF.messaging.popins.GLB_ITEMAVAILABLENOW 12033 } 12034 }; 12035 if (ANF.params.env.region === 'US') { 12036 dataObj.isAvailableCardInfo['sub-text'] = { 12037 'text': ANF.messaging.popins.GLB_INELIGIBLEFORSAMEDAYDELIVERY 12038 }; 12039 } 12040 } 12041 12042 if (dataObj.isNotInStock) { 12043 dataObj.noStockCardInfo = { 12044 'state': 'unavailable', 12045 'main-text': { 12046 'text': ANF.messaging.popins.GLB_NOTINSTOCKATSTORE 12047 } 12048 }; 12049 } 12050 12051 if (dataObj.isStorePopinsEligible) { 12052 dataObj.popinsCardInfo = { 12053 'state': 'available', 12054 'main-text': { 12055 'text': ANF.messaging.popins.OMNI_POPINSAVAILABLE_PRIMARY, 12056 'help-button': { 12057 'aria-label': ANF.messaging.popins.OMNI_POPINSAVAILABLE_HELPBUTTONLABEL, 12058 'class-list': 'product__actions--popins-info js-open-modal', 12059 'data-prop': "data-modal='product__actions--popins-info-espot-v2'" 12060 } 12061 } 12062 }; 12063 } 12064 12065 if (dataObj.isNotAvailableAtLocation) { 12066 dataObj.notAvailableCardInfo = { 12067 'state': 'unavailable', 12068 'main-text': { 12069 'text': ANF.messaging.popins.GLB_CLICKCOLLECT_NOTAVAILABLE 12070 } 12071 }; 12072 } 12073 12074 if (dataObj.isNotAvailableForPickup) { 12075 dataObj.noPickupCardInfo = { 12076 'state': 'unavailable', 12077 'main-text': { 12078 'text': ANF.messaging.popins.GLB_NOTAVAILABLEFORPICKUP 12079 } 12080 }; 12081 } 12082 12083 return dataObj; 12084 } 12085 12086 function updateProductAvailabilityHandler() {
12087 if (!Utils.isQuickviewOpened) { 12088 if (!scopeData) { 12089 scopeData = { 12090 $parentContainer: $(productSectionSelector) 12091 }; 12092 } 12093 12094 updateProductAvailability(); 12095 } 12096 } 12097 12098 function getStoreId() { 12099 return $(preferredStoreWidget).attr('data-storeid'); 12100 } 12101 12102 function getOmniMfeCrsDataBridge() { 12103 var pdpDataBridge = {}; 12104 12105 if (window.digitalData.export().pdp) { 12106 pdpDataBridge = window.digitalData.export().pdp.omniMfeCrsDataBridge || {}; 12107 } 12108 12109 return pdpDataBridge; 12110 } 12111 12112 function updateProductAvailability() { 12113 var storeID = null; 12114 var skuEligibility = null; 12115 var tempObj = null; 12116 var omniMfeCrsDataBridge = getOmniMfeCrsDataBridge(); 12117 var $productSection = $(productSectionSelector); 12118 var shortSku = omniMfeCrsDataBridge.qualifiedSku || $productSection.find(skuSelect).val(); 12119 12120 if ( 12121 test.string.not.empty(storeObj.storeId, false) && 12122 test.string.not.empty(shortSku, false) 12123 ) { 12124 var productID = omniMfeCrsDataBridge.productId || $productSection.data('productid'); 12125 12126 storeID = getStoreId(); 12127 skuEligibility = productAvailability.checkSkuAvailability(productID, shortSku); 12128 productAvailability.retrieveStoreEligibility(storeID, shortSku, skuEligibility, updateWidget); 12129 } else if (test.string.not.empty(storeObj.storeId, false)) { 12130 tempObj = { 12131 'isPhase2Messaging': true, 12132 'isSelectSizeMsg': false, 12133 'isAvailableInStore': false, 12134 'isNotInStock': false, 12135 'isStorePopinsEligible': false, 12136 'isNotAvailableForPickup': false, 12137 'isNotAvailableAtLocation': false 12138 }; 12139 12140 if ($productSection.find(isAvailable).hasClass('inactive')) { 12141 tempObj.isNotInStock = true; 12142 } else { 12143 tempObj.isSelectSizeMsg = true; 12144 } 12145 12146 updateWidget(tempObj); 12147 } 12148 } 12149 12150 function updateWidget(data) { 12151 var $errorContainer = $(reserveError); 12152 var templateData = buildPreferredWidgetObj(data); 12153 var processedTemplate = productPartialPdpPreferredStore.templates.pdp_preferredStore(templateData); 12154 12155 if ($errorContainer.hasClass('show')) { 12156 $errorContainer.removeClass('show'); 12157 } 12158 12159 $(availabilityWrapper).html(processedTemplate); 12160 12161 if (test.string.not.empty(storeObj.brand, false)) { 12162 $(preferredStoreBadgeSelectorClass).addClass(storeObj.brand); 12163 } 12164 12165 $(availabilityWrapper).data('selected-store-hours-object', data.storeHours); 12166 reInitializeModal(); 12167 } 12168 12169 function displayReserveError() { 12170 $(reserveError).addClass('show'); 12171 $(itemAvailabilitySelectorClass).hide(); 12172 12173 $(ANF).trigger('product:AJAXError'); 12174 } 12175 12176 function hideReserveError() { 12177 $(reserveError).removeClass('show'); 12178 } 12179 12180 function reInitializeModal() { 12181 modal.default().init(); 12182 } 12183 12184 var publicMembers = { 12185 'bindEvents': function bindEvents() { 12186 $(document).on('click', changeButton, showPreferredStoreModal); 12187 }, 12188 'listen': function listen() { 12189 var messageBus = $(ANF); 12190 var selectedStoreCookie = cookies.get('selectedStore'); 12191 var $bodySelector = $('body'); 12192 12193 messageBus.on('popins:getPhysicalStore', getPhysicalStore); 12194 messageBus.on('findAStore:storeCookied', widgetSetup); 12195 messageBus.on('findAStore:noStoreCookied', widgetSetup); 12196 window.addEventListener('omni-mfe-setup-crs-widget', widgetSetup); 12197 messageBus.on('product:availabilityError', displayReserveError); 12198 messageBus.on('product:availabilitySuccess', hideReserveError); 12199 12200 if (($bodySelector.is(catalogProductSelectorClass) || $bodySelector.is(productViewSelectorClass) || $bodySelector.is(productPageV2SelectorClass)) && selectedStoreCookie !== 'null') { 12201 messageBus.on('PDP:skuSelected', updateProductAvailabilityHandler); 12202 window.digitalData.subscribe('pdp.omniMfeCrsDataBridge.qualifiedSku', updateProductAvailabilityHandler); 12203 } 12204 } 12205 }; 12206 12207 return publicMembers; 12208}); 12209 12210redesign.define('productSections/product/popins',[ 12211 'coreSectionHelpers', 12212 'coreModules/floatingLabels', 12213 'productModules/popins/preferredStore' 12214], function Popins(coreSectionHelpers, floatingLabels, preferredStore) { 12215 'use strict'; 12216 12217 12218 var privateMembers = { 12219 'DEFAULT_MODULES': { 12220 'preferredStore': preferredStore, 12221 'floatingLabels': floatingLabels 12222 }, 12223 'GLOBAL_SCOPE': '', 12224 'MODULES': {} 12225 }; 12226 12227 return function setScope(settings) { 12228 privateMembers.GLOBAL_SCOPE = settings.scope; 12229 12230 privateMembers.MODULES = privateMembers.DEFAULT_MODULES; 12231 12232 if (typeof settings.modules === 'object') { 12233 $.extend(true, privateMembers.MODULES, settings.modules); 12234 } 12235 12236 return {
12237 'init': function init() { 12238 coreSectionHelpers.init({ 12239 'modules': privateMembers.MODULES, 12240 'scope': privateMembers.GLOBAL_SCOPE 12241 }); 12242 } 12243 }; 12244 }; 12245}); 12246 12247redesign.define('polyfill/IntersectionObserver',[], function () { 12248 "use strict"; 12249 /** 12250 * Copyright 2016 Google Inc. All Rights Reserved. 12251 * 12252 * Licensed under the W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE. 12253 * 12254 * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 12255 * 12256 */ 12257 (function() { 12258 'use strict'; 12259 12260 // Exit early if we're not running in a browser. 12261 if (typeof window !== 'object') { 12262 return; 12263 } 12264 12265 // Exit early if all IntersectionObserver and IntersectionObserverEntry 12266 // features are natively supported. 12267 if ('IntersectionObserver' in window && 12268 'IntersectionObserverEntry' in window && 12269 'intersectionRatio' in window.IntersectionObserverEntry.prototype) { 12270 12271 // Minimal polyfill for Edge 15's lack of `isIntersecting` 12272 // See: https://github.com/w3c/IntersectionObserver/issues/211 12273 if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) { 12274 Object.defineProperty(window.IntersectionObserverEntry.prototype, 12275 'isIntersecting', { 12276 get: function () { 12277 return this.intersectionRatio > 0; 12278 } 12279 }); 12280 } 12281 return; 12282 } 12283 12284 12285 /** 12286 * A local reference to the document. 12287 */ 12288 var document = window.document; 12289 12290 12291 /** 12292 * An IntersectionObserver registry. This registry exists to hold a strong 12293 * reference to IntersectionObserver instances currently observing a target 12294 * element. Without this registry, instances without another reference may be 12295 * garbage collected. 12296 */ 12297 var registry = []; 12298 12299 12300 /** 12301 * Creates the global IntersectionObserverEntry constructor. 12302 * https://w3c.github.io/IntersectionObserver/#intersection-observer-entry 12303 * @param {Object} entry A dictionary of instance properties. 12304 * @constructor 12305 */ 12306 function IntersectionObserverEntry(entry) { 12307 this.time = entry.time; 12308 this.target = entry.target; 12309 this.rootBounds = entry.rootBounds; 12310 this.boundingClientRect = entry.boundingClientRect; 12311 this.intersectionRect = entry.intersectionRect || getEmptyRect(); 12312 this.isIntersecting = !!entry.intersectionRect; 12313 12314 // Calculates the intersection ratio. 12315 var targetRect = this.boundingClientRect; 12316 var targetArea = targetRect.width * targetRect.height; 12317 var intersectionRect = this.intersectionRect; 12318 var intersectionArea = intersectionRect.width * intersectionRect.height; 12319 12320 // Sets intersection ratio. 12321 if (targetArea) { 12322 // Round the intersection ratio to avoid floating point math issues: 12323 // https://github.com/w3c/IntersectionObserver/issues/324 12324 this.intersectionRatio = Number((intersectionArea / targetArea).toFixed(4)); 12325 } else { 12326 // If area is zero and is intersecting, sets to 1, otherwise to 0 12327 this.intersectionRatio = this.isIntersecting ? 1 : 0; 12328 } 12329 } 12330 12331 12332 /** 12333 * Creates the global IntersectionObserver constructor. 12334 * https://w3c.github.io/IntersectionObserver/#intersection-observer-interface 12335 * @param {Function} callback The function to be invoked after intersection 12336 * changes have queued. The function is not invoked if the queue has 12337 * been emptied by calling the `takeRecords` method. 12338 * @param {Object=} opt_options Optional configuration options. 12339 * @constructor 12340 */ 12341 function IntersectionObserver(callback, opt_options) { 12342 12343 var options = opt_options || {}; 12344 12345 if (typeof callback != 'function') { 12346 throw new Error('callback must be a function'); 12347 } 12348 12349 if (options.root && options.root.nodeType != 1) { 12350 throw new Error('root must be an Element'); 12351 } 12352 12353 // Binds and throttles `this._checkForIntersections`. 12354 this._checkForIntersections = throttle( 12355 this._checkForIntersections.bind(this), this.THROTTLE_TIMEOUT); 12356 12357 // Private properties. 12358 this._callback = callback; 12359 this._observationTargets = []; 12360 this._queuedEntries = []; 12361 this._rootMarginValues = this._parseRootMargin(options.rootMargin); 12362 12363 // Public properties. 12364 this.thresholds = this._initThresholds(options.threshold); 12365 this.root = options.root || null; 12366 this.rootMargin = this._rootMarginValues.map(function(margin) { 12367 return margin.value + margin.unit; 12368 }).join(' '); 12369 } 12370 12371 12372 /** 12373 * The minimum interval within which the document will be checked for 12374 * intersection changes. 12375 */ 12376 IntersectionObserver.prototype.THROTTLE_TIMEOUT = 100; 12377 12378 12379 /** 12380 * The frequency in which the polyfill polls for intersection changes. 12381 * this can be updated on a per instance basis and must be set prior to 12382 * calling `observe` on the first target. 12383 */ 12384 IntersectionObserver.prototype.POLL_INTERVAL = null; 12385 12386 /** 12387 * Use a mutation observer on the root element 12388 * to detect intersection changes. 12389 */ 12390 IntersectionObserver.prototype.USE_MUTATION_OBSERVER = true; 12391 12392 12393 /** 12394 * Starts observing a target element for intersection changes based on 12395 * the thresholds values. 12396 * @param {Element} target The DOM element to observe. 12397 */ 12398 IntersectionObserver.prototype.observe = function(target) { 12399 var isTargetAlreadyObserved = this._observationTargets.some(function(item) { 12400 return item.element == target; 12401 }); 12402 12403 if (isTargetAlreadyObserved) { 12404 return; 12405 } 12406
12407 if (!(target && target.nodeType == 1)) { 12408 throw new Error('target must be an Element'); 12409 } 12410 12411 this._registerInstance(); 12412 this._observationTargets.push({element: target, entry: null}); 12413 this._monitorIntersections(); 12414 this._checkForIntersections(); 12415 }; 12416 12417 12418 /** 12419 * Stops observing a target element for intersection changes. 12420 * @param {Element} target The DOM element to observe. 12421 */ 12422 IntersectionObserver.prototype.unobserve = function(target) { 12423 this._observationTargets = 12424 this._observationTargets.filter(function(item) { 12425 12426 return item.element != target; 12427 }); 12428 if (!this._observationTargets.length) { 12429 this._unmonitorIntersections(); 12430 this._unregisterInstance(); 12431 } 12432 }; 12433 12434 12435 /** 12436 * Stops observing all target elements for intersection changes. 12437 */ 12438 IntersectionObserver.prototype.disconnect = function() { 12439 this._observationTargets = []; 12440 this._unmonitorIntersections(); 12441 this._unregisterInstance(); 12442 }; 12443 12444 12445 /** 12446 * Returns any queue entries that have not yet been reported to the 12447 * callback and clears the queue. This can be used in conjunction with the 12448 * callback to obtain the absolute most up-to-date intersection information. 12449 * @return {Array} The currently queued entries. 12450 */ 12451 IntersectionObserver.prototype.takeRecords = function() { 12452 var records = this._queuedEntries.slice(); 12453 this._queuedEntries = []; 12454 return records; 12455 }; 12456 12457 12458 /** 12459 * Accepts the threshold value from the user configuration object and 12460 * returns a sorted array of unique threshold values. If a value is not 12461 * between 0 and 1 and error is thrown. 12462 * @private 12463 * @param {Array|number=} opt_threshold An optional threshold value or 12464 * a list of threshold values, defaulting to [0]. 12465 * @return {Array} A sorted list of unique and valid threshold values. 12466 */ 12467 IntersectionObserver.prototype._initThresholds = function(opt_threshold) { 12468 var threshold = opt_threshold || [0]; 12469 if (!Array.isArray(threshold)) threshold = [threshold]; 12470 12471 return threshold.sort().filter(function(t, i, a) { 12472 if (typeof t != 'number' || isNaN(t) || t < 0 || t > 1) { 12473 throw new Error('threshold must be a number between 0 and 1 inclusively'); 12474 } 12475 return t !== a[i - 1]; 12476 }); 12477 }; 12478 12479 12480 /** 12481 * Accepts the rootMargin value from the user configuration object 12482 * and returns an array of the four margin values as an object c
12482ontaining 12483 * the value and unit properties. If any of the values are not properly 12484 * formatted or use a unit other than px or %, and error is thrown. 12485 * @private 12486 * @param {string=} opt_rootMargin An optional rootMargin value, 12487 * defaulting to '0px'. 12488 * @return {Array<Object>} An array of margin objects with the keys 12489 * value and unit. 12490 */ 12491 IntersectionObserver.prototype._parseRootMargin = function(opt_rootMargin) { 12492 var marginString = opt_rootMargin || '0px'; 12493 var margins = marginString.split(/\s+/).map(function(margin) { 12494 var parts = /^(-?\d*\.?\d+)(px|%)$/.exec(margin); 12495 if (!parts) { 12496 throw new Error('rootMargin must be specified in pixels or percent'); 12497 } 12498 return {value: parseFloat(parts[1]), unit: parts[2]}; 12499 }); 12500 12501 // Handles shorthand. 12502 margins[1] = margins[1] || margins[0]; 12503 margins[2] = margins[2] || margins[0]; 12504 margins[3] = margins[3] || margins[1]; 12505 12506 return margins; 12507 }; 12508 12509 12510 /** 12511 * Starts polling for intersection changes if the polling is not already 12512 * happening, and if the page's visibility state is visible. 12513 * @private 12514 */ 12515 IntersectionObserver.prototype._monitorIntersections = function() { 12516 if (!this._monitoringIntersections) { 12517 this._monitoringIntersections = true; 12518 12519 // If a poll interval is set, use polling instead of listening to 12520 // resize and scroll events or DOM mutations. 12521 if (this.POLL_INTERVAL) { 12522 this._monitoringInterval = setInterval( 12523 this._checkForIntersections, this.POLL_INTERVAL); 12524 } 12525 else { 12526 addEvent(window, 'resize', this._checkForIntersections, true); 12527 addEvent(document, 'scroll', this._checkForIntersections, true); 12528 12529 if (this.USE_MUTATION_OBSERVER && 'MutationObserver' in window) { 12530 this._domObserver = new MutationObserver(this._checkForIntersections); 12531 this._domObserver.observe(document, { 12532 attributes: true, 12533 childList: true, 12534 characterData: true, 12535 subtree: true 12536 }); 12537 } 12538 } 12539 } 12540 }; 12541 12542 12543 /** 12544 * Stops polling for intersection changes. 12545 * @private 12546 */ 12547 IntersectionObserver.prototype._unmonitorIntersections = function() { 12548 if (this._monitoringIntersections) { 12549 this._monitoringIntersections = false; 12550 12551 clearInterval(this._monitoringInterval); 12552 this._monitoringInterval = null; 12553 12554 removeEvent(window, 'resize', this._checkForIntersections, true); 12555 removeEvent(document, 'scroll', this._checkForIntersections, true); 12556 12557 if (this._domObserver) { 12558 this._domObserver.disconnect(); 12559 this._domObserver = null; 12560 } 12561 } 12562 }; 12563 12564 12565 /** 12566 * Scans each observation target for intersection changes and adds them 12567 * to the internal entries queue. If new entries are found, it 12568 * schedules the callback to be invoked. 12569 * @private 12570 */ 12571 IntersectionObserver.prototype._checkForIntersections = function() { 12572 var rootIsInDom = this._rootIsInDom(); 12573 var rootRect = rootIsInDom ? this._getRootRect() : getEmptyRect(); 12574 12575 this._observationTargets.forEach(function(item) { 12576 var target = item.element; 12577 var targetRect = getBoundingClientRect(target); 12578 var rootContainsTarget = this._rootContainsTarget(target); 12579 var oldEntry = item.entry; 12580 var intersectionRect = rootIsInDom && rootContainsTarget && 12581 this._computeTargetAndRootIntersection(target, rootRect); 12582 12583 var newEntry = item.entry = new IntersectionObserverEntry({ 12584 time: now(), 12585 target: target, 12586 boundingClientRect: targetRect, 12587 rootBounds: rootRect, 12588 intersectionRect: intersectionRect 12589 }); 12590 12591 if (!oldEntry) { 12592 this._queuedEntries.push(newEntry); 12593 } else if (rootIsInDom && rootContainsTarget) { 12594 // If the new entry intersection ratio has crossed any of the 12595 // thresholds, add a new entry. 12596 if (this._hasCrossedThreshold(oldEntry, newEntry)) { 12597 this._queuedEntries.push(newEntry); 12598 } 12599 } else { 12600 // If the root is not in the DOM or target is not contained within 12601 // root but the previous entry for this target had an intersection, 12602 // add a new record indicating removal. 12603 if (oldEntry && oldEntry.isIntersecting) { 12604 this._queuedEntries.push(newEntry); 12605 } 12606 } 12607 }, this); 12608 12609 if (this._queuedEntries.length) { 12610 this._callback(this.takeRecords(), this); 12611 } 12612 }; 12613 12614 12615 /** 12616 * Accepts a target and root rect computes the intersection between then 12617 * following the algorithm in the spec. 12618 * TODO(philipwalton): at this time clip-path is not considered. 12619 * https://w3c.github.io/IntersectionObserver/#calculate-intersection-rect-algo 12620 * @param {Element} target The target DOM element 12621 * @param {Object} rootRect The bounding rect of the root after being 12622 * expanded by the rootMargin value. 12623 * @return {?Object} The final intersection rect object or undefined if no 12624 * intersection is found. 12625 * @private 12626 */ 12627 IntersectionObserver.prototype._computeTargetAndRootIntersection = 12628 function(target, rootRect) { 12629 12630 // If the element isn't displayed, an intersection can't happen. 12631 if (window.getComputedStyle(target).display == 'none') return; 12632 12633 var targetRect = getBoundingClientRect(target); 12634 var intersectionRect = targetRect; 12635 var parent = getParentNode(target); 12636 var atRoot = false;
vendor: 8,734 bytes, lines 12637-12907
12637 12638 while (!atRoot) { 12639 var parentRect = null; 12640 var parentComputedStyle = parent.nodeType == 1 ? 12641 window.getComputedStyle(parent) : {}; 12642 12643 // If the parent isn't displayed, an intersection can't happen. 12644 if (parentComputedStyle.display == 'none') return; 12645 12646 if (parent == this.root || parent == document) { 12647 atRoot = true; 12648 parentRect = rootRect; 12649 } else { 12650 // If the element has a non-visible overflow, and it's not the <body> 12651 // or <html> element, update the intersection rect. 12652 // Note: <body> and <html> cannot be clipped to a rect that's not also 12653 // the document rect, so no need to compute a new intersection. 12654 if (parent != document.body && 12655 parent != document.documentElement && 12656 parentComputedStyle.overflow != 'visible') { 12657 parentRect = getBoundingClientRect(parent); 12658 } 12659 } 12660 12661 // If either of the above conditionals set a new parentRect, 12662 // calculate new intersection data. 12663 if (parentRect) { 12664 intersectionRect = computeRectIntersection(parentRect, intersectionRect); 12665 12666 if (!intersectionRect) break; 12667 } 12668 parent = getParentNode(parent); 12669 } 12670 return intersectionRect; 12671 }; 12672 12673 12674 /** 12675 * Returns the root rect after being expanded by the rootMargin value. 12676 * @return {Object} The expanded root rect. 12677 * @private 12678 */ 12679 IntersectionObserver.prototype._getRootRect = function() { 12680 var rootRect; 12681 if (this.root) { 12682 rootRect = getBoundingClientRect(this.root); 12683 } else { 12684 // Use <html>/<body> instead of window since scroll bars affect size. 12685 var html = document.documentElement; 12686 var body = document.body; 12687 rootRect = { 12688 top: 0, 12689 left: 0, 12690 right: html.clientWidth || body.clientWidth, 12691 width: html.clientWidth || body.clientWidth, 12692 bottom: html.clientHeight || body.clientHeight, 12693 height: html.clientHeight || body.clientHeight 12694 }; 12695 } 12696 return this._expandRectByRootMargin(rootRect); 12697 }; 12698 12699 12700 /** 12701 * Accepts a rect and expands it by the rootMargin value. 12702 * @param {Object} rect The rect object to expand. 12703 * @return {Object} The expanded rect. 12704 * @private 12705 */ 12706 IntersectionObserver.prototype._expandRectByRootMargin = function(rect) { 12707 var margins = this._rootMarginValues.map(function(margin, i) { 12708 return margin.unit == 'px' ? margin.value : 12709 margin.value * (i % 2 ? rect.width : rect.height) / 100; 12710 }); 12711 var newRect = { 12712 top: rect.top - margins[0], 12713 right: rect.right + margins[1], 12714 bottom: rect.bottom + margins[2], 12715 left: rect.left - margins[3] 12716 }; 12717 newRect.width = newRect.right - newRect.left; 12718 newRect.height = newRect.bottom - newRect.top; 12719 12720 return newRect; 12721 }; 12722 12723 12724 /** 12725 * Accepts an old and new entry and returns true if at least one of the 12726 * threshold values has been crossed. 12727 * @param {?IntersectionObserverEntry} oldEntry The previous entry for a 12728 * particular target element or null if no previous entry exists. 12729 * @param {IntersectionObserverEntry} newEntry The current entry for a 12730 * particular target element. 12731 * @return {boolean} Returns true if a any threshold has been crossed. 12732 * @private 12733 */ 12734 IntersectionObserver.prototype._hasCrossedThreshold = 12735 function(oldEntry, newEntry) { 12736 12737 // To make comparing easier, an entry that has a ratio of 0 12738 // but does not actually intersect is given a value of -1 12739 var oldRatio = oldEntry && oldEntry.isIntersecting ? 12740 oldEntry.intersectionRatio || 0 : -1; 12741 var newRatio = newEntry.isIntersecting ? 12742 newEntry.intersectionRatio || 0 : -1; 12743 12744 // Ignore unchanged ratios 12745 if (oldRatio === newRatio) return; 12746 12747 for (var i = 0; i < this.thresholds.length; i++) { 12748 var threshold = this.thresholds[i]; 12749 12750 // Return true if an entry matches a threshold or if the new ratio 12751 // and the old ratio are on the opposite sides of a threshold. 12752 if (threshold == oldRatio || threshold == newRatio || 12753 threshold < oldRatio !== threshold < newRatio) { 12754 return true; 12755 } 12756 } 12757 }; 12758 12759 12760 /** 12761 * Returns whether or not the root element is an element and is in the DOM. 12762 * @return {boolean} True if the root element is an element and is in the DOM. 12763 * @private 12764 */ 12765 IntersectionObserver.prototype._rootIsInDom = function() { 12766 return !this.root || containsDeep(document, this.root); 12767 }; 12768 12769 12770 /** 12771 * Returns whether or not the target element is a child of root. 12772 * @param {Element} target The target element to check. 12773 * @return {boolean} True if the target element is a child of root. 12774 * @private 12775 */ 12776 IntersectionObserver.prototype._rootContainsTarget = function(target) { 12777 return containsDeep(this.root || document, target); 12778 }; 12779 12780 12781 /** 12782 * Adds the instance to the global IntersectionObserver registry if it isn't 12783 * already present. 12784 * @private 12785 */ 12786 IntersectionObserver.prototype._registerInstance = function() { 12787 if (registry.indexOf(this) < 0) { 12788 registry.push(this); 12789 } 12790 }; 12791 12792 12793 /** 12794 * Removes the instance from the global IntersectionObserver registry. 12795 * @private 12796 */ 12797 IntersectionObserver.prototype._unregisterInstance = function() { 12798 var index = registry.indexOf(this); 12799 if (index != -1) registry.splice(index, 1); 12800 }; 12801 12802 12803 /** 12804 * Returns the result of the performance.now() method or null in browsers 12805 * that don't support the API. 12806 * @return {number} The elapsed time since the page was requested. 12807 */ 12808 function now() { 12809 return window.performance && performance.now && performance.now(); 12810 } 12811 12812 12813 /** 12814 * Throttles a function and delays its execution, so it's only called at most 12815 * once within a given time period. 12816 * @param {Function} fn The function to throttle. 12817 * @param {number} timeout The amount of time that must pass before the 12818 * function can be called again. 12819 * @return {Function} The throttled function. 12820 */ 12821 function throttle(fn, timeout) { 12822 var timer = null; 12823 return function () { 12824 if (!timer) { 12825 timer = setTimeout(function() { 12826 fn(); 12827 timer = null; 12828 }, timeout); 12829 } 12830 }; 12831 } 12832 12833 12834 /** 12835 * Adds an event handler to a DOM node ensuring cross-browser compatibility. 12836 * @param {Node} node The DOM node to add the event handler to. 12837 * @param {string} event The event name. 12838 * @param {Function} fn The event handler to add. 12839 * @param {boolean} opt_useCapture Optionally adds the even to the capture 12840 * phase. Note: this only works in modern browsers. 12841 */ 12842 function addEvent(node, event, fn, opt_useCapture) { 12843 if (typeof node.addEventListener == 'function') { 12844 node.addEventListener(event, fn, opt_useCapture || false); 12845 } 12846 else if (typeof node.attachEvent == 'function') { 12847 node.attachEvent('on' + event, fn); 12848 } 12849 } 12850 12851 12852 /** 12853 * Removes a previously added event handler from a DOM node. 12854 * @param {Node} node The DOM node to remove the event handler from. 12855 * @param {string} event The event name. 12856 * @param {Function} fn The event handler to remove. 12857 * @param {boolean} opt_useCapture If the event handler was added with this 12858 * flag set to true, it should be set to true here in order to remove it. 12859 */ 12860 function removeEvent(node, event, fn, opt_useCapture) { 12861 if (typeof node.removeEventListener == 'function') { 12862 node.removeEventListener(event, fn, opt_useCapture || false); 12863 } 12864 else if (typeof node.detatchEvent == 'function') { 12865 node.detatchEvent('on' + event, fn); 12866 } 12867 } 12868 12869 12870 /** 12871 * Returns the intersection between two rect objects. 12872 * @param {Object} rect1 The first rect. 12873 * @param {Object} rect2 The second rect. 12874 * @return {?Object} The intersection rect or undefined if no intersection 12875 * is found. 12876 */ 12877 function computeRectIntersection(rect1, rect2) { 12878 var top = Math.max(rect1.top, rect2.top); 12879 var bottom = Math.min(rect1.bottom, rect2.bottom); 12880 var left = Math.max(rect1.left, rect2.left); 12881 var right = Math.min(rect1.right, rect2.right); 12882 var width = right - left; 12883 var height = bottom - top; 12884 12885 return (width >= 0 && height >= 0) && { 12886 top: top, 12887 bottom: bottom, 12888 left: left, 12889 right: right, 12890 width: width, 12891 height: height 12892 }; 12893 } 12894 12895 12896 /** 12897 * Shims the native getBoundingClientRect for compatibility with older IE. 12898 * @param {Element} el The element whose bounding rect to get. 12899 * @return {Object} The (possibly shimmed) rect of the element. 12900 */ 12901 function getBoundingClientRect(el) { 12902 var rect; 12903 12904 try { 12905 rect = el.getBoundingClientRect(); 12906 } catch (err) { 12907 // Ignore Windows 7 IE11 "Unspecified error"
12908 // https://github.com/w3c/IntersectionObserver/pull/205 12909 } 12910 12911 if (!rect) return getEmptyRect(); 12912 12913 // Older IE 12914 if (!(rect.width && rect.height)) { 12915 rect = { 12916 top: rect.top, 12917 right: rect.right, 12918 bottom: rect.bottom, 12919 left: rect.left, 12920 width: rect.right - rect.left, 12921 height: rect.bottom - rect.top 12922 }; 12923 } 12924 return rect; 12925 } 12926 12927 12928 /** 12929 * Returns an empty rect object. An empty rect is returned when an element 12930 * is not in the DOM. 12931 * @return {Object} The empty rect. 12932 */ 12933 function getEmptyRect() { 12934 return { 12935 top: 0, 12936 bottom: 0, 12937 left: 0, 12938 right: 0, 12939 width: 0, 12940 height: 0 12941 }; 12942 } 12943 12944 /** 12945 * Checks to see if a parent element contains a child element (including inside 12946 * shadow DOM). 12947 * @param {Node} parent The parent element. 12948 * @param {Node} child The child element. 12949 * @return {boolean} True if the parent node contains the child node. 12950 */ 12951 function containsDeep(parent, child) { 12952 var node = child; 12953 while (node) { 12954 if (node == parent) return true; 12955 12956 node = getParentNode(node); 12957 } 12958 return false; 12959 } 12960 12961 12962 /** 12963 * Gets the parent node of an element or its host element if the parent node 12964 * is a shadow root. 12965 * @param {Node} node The node whose parent to get. 12966 * @return {Node|null} The parent node or null if no parent exists. 12967 */ 12968 function getParentNode(node) { 12969 var parent = node.parentNode; 12970 12971 if (parent && parent.nodeType == 11 && parent.host) { 12972 // If the parent is a shadow root, return the host element. 12973 return parent.host; 12974 } 12975 12976 if (parent && parent.assignedSlot) { 12977 // If the parent is distributed in a <slot>, return the parent of a slot. 12978 return parent.assignedSlot.parentNode; 12979 } 12980 12981 return parent; 12982 } 12983 12984 12985 // Exposes the constructors globally. 12986 window.IntersectionObserver = IntersectionObserver; 12987 window.IntersectionObserverEntry = IntersectionObserverEntry; 12988 12989 }()); 12990 12991 return window.IntersectionObserver; 12992 }); 12993redesign.define('productModules/helpers/productIntersectionObserverSetup',[ 12994 'productModules/helpers/productIntersectionObserver', 12995 'polyfill/IntersectionObserver' 12996], function productIntersectionObserverSetup(productIntersectionObserver, IntersectionObserver) { 12997 'use strict'; 12998 12999 var getIntersectionObserver = function getIntersectionObserver() { 13000 return IntersectionObserver; 13001 }; 13002 13003 var publicMembers = { 13004 init: function init() { 13005 productIntersectionObserver.create('IntersectionObserver', getIntersectionObserver); 13006 } 13007 }; 13008 13009 return publicMembers; 13010}); 13011 13012redesign.define('productModules/product/productGridSaves',[ 13013 'coreServices/mysaves', 13014 'globalModulesHelpers/mysavesUtility' 13015], 13016function productGridSaves(savesService, savesUtility) { 13017 'use strict'; 13018 13019 var retryCount = 1; 13020 function callSavesApi(serviceMethod, products) { 13021 var savesApiPromise = savesService[serviceMethod]({ 13022 serviceMethod: serviceMethod, 13023 products: products 13024 }); 13025 13026 savesApiPromise.done(function saveApiPromiseDone(response) { 13027 var APIResponseCode = response.reason; 13028 var isFirstTimeSaveResponseCode = 5; 13029 var isFirstTimeSave = APIResponseCode === isFirstTimeSaveResponseCode; 13030 var itemSaved = serviceMethod === 'add'; 13031 13032 if (itemSaved) { 13033 savesUtility.saveSuccess(isFirstTimeSave); 13034 } 13035 13036 $(window.ANF).trigger('mySaves:updatedMySavesCount'); 13037 }); 13038 13039 savesApiPromise.fail(function onFailure() { 13040 if (retryCount < 3) { 13041 retryCount++; 13042 callSavesApi(serviceMethod, products); 13043 } 13044 }); 13045 } 13046 13047 var productAddSaveListener = function addSaveListener(e) { 13048 callSavesApi('add', [e.detail]); 13049 }; 13050 13051 var productRemoveSaveListener = function removeSaveListener(e) { 13052 callSavesApi('remove', [e.detail]); 13053 }; 13054 13055 var publicMembers = { 13056 gridSavesPublicFunctions: function gridSavesPublicFunctions() { 13057 window.addEventListener('mfe:productAddSave', productAddSaveListener); 13058 window.addEventListener('mfe:productRemoveSave', productRemoveSaveListener); 13059 } 13060 }; 13061 13062 return publicMembers; 13063}); 13064 13065 13066redesign.define('productPages/productPageMain',[ 13067 'productSections/product/productPageImages', 13068 'productSections/product/productPageReviews', 13069 'productSections/product/productPageInfo', 13070 'productSections/product/quickviewModules', 13071 'productSections/product/popins', 13072 'productModules/helpers/productIntersectionObserverSetup', 13073 'productModules/helpers/Utils', 13074 'productModules/product/productGridSaves' 13075], function ProductPageMain(productPageImages, productPageReviews, productPageInfo, quickviewModules, popins, productIntersectionObserverSetup, Utils, productGridSaves) { 13076 'use strict'; 13077 13078 function main() { 13079 if (typeof ANF.product !== 'object') { 13080 ANF.product = {}; 13081 } 13082 13083 function isPdP() { 13084 return $('body').is('#catalog-productpageview, #catalog-product, .product-page-v2'); 13085 } 13086 function hasLocationFilter() { 13087 return $('.ds-rs-facet-tile-location__wrapper').length; 13088 } 13089 13090 ANF.product.productPageMain = (function productPageMain($, ANF) { 13091 var messageBus = $(ANF); 13092 13093 var publicMembers = {
13094 init: function init() { 13095 ANF.product.productPageMain.start.productPageImagesStart({ 13096 'scope': 'body', 13097 'parentPage': ANF.product.productPageMain 13098 }); 13099 ANF.product.productPageMain.start.productPageReviewsStart({ 13100 'scope': 'body', 13101 'parentPage': ANF.product.productPageMain 13102 }); 13103 ANF.product.productPageMain.start.productPageInfoStart({ 13104 'scope': 'body', 13105 'parentPage': ANF.product.productPageMain 13106 }); 13107 ANF.product.productPageMain.start.quickviewModulesStart({ 13108 'scope': 'body', 13109 'parentPage': ANF.product.productPageMain 13110 }); 13111 ANF.product.productPageMain.start.popinsStart({ 13112 'scope': '.product__actions--availability' 13113 }); 13114 }, 13115 start: { 13116 productPageImagesStart: function productPageImagesStart(settings) { 13117 ANF.product.productPageMain.productPageImages = productPageImages(settings); 13118 ANF.product.productPageMain.productPageImages.init(); 13119 ANF.product.productPageMain.productPageImages.subscribe(); 13120 }, 13121 productPageReviewsStart: function productPageReviewsStart(settings) { 13122 if (ANF.params.storeAttributes.catalogV2.hasBazaarVoiceEnable) { 13123 ANF.product.productPageMain.productPageReviews = productPageReviews(settings); 13124 ANF.product.productPageMain.productPageReviews.init(); 13125 ANF.product.productPageMain.productPageReviews.subscribe(); 13126 } 13127 }, 13128 productPageInfoStart: function productPageInfoStart(settings) { 13129 ANF.product.productPageMain.productPageInfo = productPageInfo(settings); 13130 ANF.product.productPageMain.productPageInfo.init(); 13131 ANF.product.productPageMain.productPageInfo.subscribe(); 13132 }, 13133 quickviewModulesStart: function quickviewModulesStart(settings) { 13134 ANF.product.productPageMain.quickviewModules = quickviewModules(settings); 13135 ANF.product.productPageMain.quickviewModules.init(); 13136 }, 13137 popinsStart: function popinsStart(settings) { 13138 if (isPdP() || hasLocationFilter()) { 13139 ANF.product.productPageMain.popins = popins(settings); 13140 ANF.product.productPageMain.popins.init(); 13141 } 13142 } 13143 }, 13144 ajaxInContent: function ajaxInContent() { 13145 messageBus.trigger('productPageMain:init'); 13146 13147 if (Utils.isExternalUser() && !Utils.isQuickviewOpened) { 13148 productGridSaves.gridSavesPublicFunctions(); 13149 } 13150 if (isPdP()) { 13151 messageBus.trigger('popins:getPhysicalStore'); 13152 } 13153 } 13154 }; 13155 13156 return publicMembers; 13157 })(jQuery, ANF); 13158 13159 ANF.deferredStart = ANF.deferredStart || $.deferred(); 13160 ANF.deferredStart.then(function deferredStart() { 13161 // this is needed to set up the productIntersectionObserver module which has the Intersection Observer API 13162 productIntersectionObserverSetup.init(); 13163 ANF.product.productPageMain.init(); 13164 ANF.product.productPageMain.ajaxInContent(); 13165 }); 13166 } 13167 13168 return main; 13169}); 13170 13171redesign.define('productSections/product/quickviewImages',[ 13172 'coreSectionHelpers', 13173 'productModules/product/productThumbnails', 13174 'productModules/product/productMainImage', 13175 'productModules/product/productImageLoader' 13176], function QuickviewImages(coreSectionHelpers, thumbnails, mainImage, imageLoader) { 13177 'use strict'; 13178 13179 var privateMembers = { 13180 'DEFAULT_MODULES': { 13181 '.product-thumbnails': thumbnails, 13182 '.product-main-images': mainImage, 13183 'product-main-images;=': imageLoader 13184 }, 13185 'GLOBAL_SCOPE': '', 13186 'parentPage': '', 13187 'MODULES': {} 13188 }; 13189 13190 return function quickviewImagesScope(settings) { 13191 privateMembers.GLOBAL_SCOPE = settings.scope; 13192 privateMembers.parentPage = settings.parentPage; 13193 privateMembers.MODULES = privateMembers.DEFAULT_MODULES; 13194 13195 if (typeof settings.modules === 'object') { 13196 $.extend(true, privateMembers.MODULES, settings.modules); 13197 } 13198 13199 // var messageBus = $(ANF); 13200 13201 return {
13202 'init': function quickviewImagesInit() { 13203 coreSectionHelpers.init({ 13204 'modules': privateMembers.MODULES, 13205 'scope': privateMembers.GLOBAL_SCOPE 13206 }); 13207 } 13208 }; 13209 }; 13210}); 13211 13212redesign.define('productPages/quickviewMain',[ 13213 'productSections/product/quickviewModules', 13214 'productSections/product/quickviewImages', 13215 'productSections/product/productPageInfo', 13216 'productSections/product/productPageReviews' 13217], function QuickviewMain(quickviewModules, quickviewImages, productPageInfo, productPageReviews) { 13218 'use strict'; 13219 13220 function main() { 13221 if (typeof ANF.product !== 'object') { 13222 ANF.product = {}; 13223 } 13224 13225 ANF.product.quickviewMain = (function quickviewMain($, ANF) { 13226 var publicMembers = { 13227 init: function init() { 13228 ANF.product.quickviewMain.start.quickviewModulesStart({ 13229 'scope': 'body', 13230 'parentPage': ANF.product.quickviewMain 13231 }); 13232 ANF.product.quickviewMain.start.quickviewImagesStart({ 13233 'scope': 'body', 13234 'parentPage': ANF.product.quickviewMain 13235 }); 13236 ANF.product.quickviewMain.start.productPageInfoStart({ 13237 'scope': 'body', 13238 'parentPage': ANF.product.quickviewMain 13239 }); 13240 ANF.product.quickviewMain.start.productPageReviewsStart({ 13241 'scope': 'body', 13242 'parentPage': ANF.product.quickviewMain 13243 }); 13244 }, 13245 start: { 13246 quickviewModulesStart: function quickviewModulesStart(settings) { 13247 ANF.product.quickviewMain.quickviewModules = quickviewModules(settings); 13248 ANF.product.quickviewMain.quickviewModules.init(); 13249 }, 13250 quickviewImagesStart: function quickviewImagesStart(settings) { 13251 ANF.product.quickviewMain.quickviewImages = quickviewImages(settings); 13252 ANF.product.quickviewMain.quickviewImages.init(); 13253 }, 13254 productPageInfoStart: function productPageInfoStart(settings) { 13255 ANF.product.quickviewMain.productPageInfo = productPageInfo(settings); 13256 ANF.product.quickviewMain.productPageInfo.init(); 13257 }, 13258 productPageReviewsStart: function productPageReviewsStart(settings) { 13259 if (ANF.params.storeAttributes.catalogV2.hasBazaarVoiceEnable) { 13260 ANF.product.quickviewMain.productPageReviews = productPageReviews(settings); 13261 ANF.product.quickviewMain.productPageReviews.init(); 13262 ANF.product.quickviewMain.productPageReviews.subscribe(); 13263 } 13264 } 13265 } 13266 }; 13267 13268 return publicMembers; 13269 })(jQuery, ANF); 13270 13271 ANF.deferredStart = ANF.deferredStart || $.deferred(); 13272 ANF.deferredStart.then(function deferredStart() { 13273 ANF.product.quickviewMain.init(); 13274 }); 13275 } 13276 13277 return main; 13278}); 13279 13280redesign.require([ 13281 'productPages/productPageMain', 13282 'productPages/quickviewMain' 13283], function ProductV2(productPageMain, quickview) { 13284 'use strict'; 13285 13286 var ANF = ANF || {}; 13287 13288 function isProductPage() { 13289 return $('body').hasClass('product-page-v2'); 13290 } 13291 13292 function product() { 13293 if (!isProductPage()) { 13294 quickview(); 13295 } else { 13296 productPageMain(); 13297 } 13298 } 13299 13300 ANF.product = product(); 13301}); 13302 13303redesign.define("src/hol/js/productV2.js", function(){}); 13304
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.