1/* MinifyError 2(11247,15-16): run-time error JS1010: Expected identifier: . 3(11247,15-16): run-time error JS1195: Expected expression: . 4(11935,15-16): run-time error JS1010: Expected identifier: [ 5 */ 6/* 7 * Author: James Bhatt 8 * Date: 2019-11-15 9 * Description: Collection of formatters for prices. 10 * Changes: 11 * 12 * Notes: 13 * 14 * Version: 0.1 15 */ 16var PriceFormatters = /** @class */ (function () { 17 function PriceFormatters(currencyPosition, currencySymbol) { 18 this.defaultCurrencySymbol = ""; 19 this.defaultCurrencyPosition = "left"; 20 this.LEFT_WITH_SPACE = "leftwithspace"; 21 this.LEFT = "left"; 22 this.RIGHT_WITH_SPACE = "rightwithspace"; 23 this.RIGHT = "right"; 24 this.defaultCurrencyPosition = currencyPosition || "left"; 25 this.defaultCurrencySymbol = currencySymbol || ""; 26 this.defaultFormatter = this.getCurrencyFormatter(currencyPosition, currencySymbol); 27 } 28 PriceFormatters.prototype.getCurrencyFormatter = function (currencyPosition, currencySymbol) { 29 if (!currencyPosition || currencyPosition == "") { 30 currencyPosition = this.defaultCurrencyPosition; 31 } 32 if (!currencySymbol || currencySymbol == "") { 33 currencySymbol = this.defaultCurrencySymbol; 34 } 35 switch (currencyPosition) { 36 case this.LEFT_WITH_SPACE: { 37 return function (value) { 38 return [currencySymbol, value.trim()].join(" "); 39 }; 40 } 41 case this.RIGHT_WITH_SPACE: { 42 return function (value) { 43 return [value.trim(), currencySymbol].join(" "); 44 }; 45 } 46 case this.LEFT: { 47 return function (value) { 48 return [currencySymbol, value.trim()].join(""); 49 }; 50 } 51 case this.RIGHT: { 52 return function (value) { 53 return [value.trim(), currencySymbol].join(""); 54 }; 55 } 56 } 57 // Default to LEFT 58 return function (value) { 59 return [currencySymbol, value.trim()].join(""); 60 }; 61 }; 62 PriceFormatters.prototype.formatPrice = function (price) { 63 return this.formatPriceWithFormatter(price, this.defaultFormatter); 64 }; 65 PriceFormatters.prototype.formatPriceWithFormatter = function (price, formatter) { 66 if (!price || price.length == 0 || !formatter) { 67 return ''; 68 } 69 return this.doFormat(price, formatter); 70 }; 71 PriceFormatters.prototype.formatPriceWithSymbol = function (price, currencyPosition, currencySymbol) { 72 if (!price || price.length == 0) { 73 return ''; 74 } 75 var formatter = this.getCurrencyFormatter(currencyPosition, currencySymbol); 76 return this.doFormat(price, formatter); 77 }; 78 PriceFormatters.prototype.removeMultiplePriceSymbols = function (priceWithCurrency, currencyPosition, symbolToRemove) { 79 //quickly bail if not a price range 80 if (priceWithCurrency.indexOf("-") == -1) { 81 return priceWithCurrency; 82 } 83 var result = priceWithCurrency; 84 var regex = new RegExp(symbolToRemove, "g"); 85 var occurrences = (priceWithCurrency.match(regex) || []).length; 86 if (occurrences > 1) { 87 if (currencyPosition.indexOf(this.RIGHT) > -1) { 88 if (currencyPosition == this.RIGHT_WITH_SPACE) { 89 symbolToRemove = " " + symbolToRemove; 90 } 91 var firstIndex = priceWithCurrency.indexOf(symbolToRemove); 92 result = priceWithCurrency.substring(0, firstIndex - 1) + priceWithCurrency.substring(firstIndex + symbolToRemove.length); 93 } 94 else if (currencyPosition.indexOf(this.LEFT) > -1) { 95 if (currencyPosition == this.LEFT_WITH_SPACE) { 96 symbolToRemove = symbolToRemove + " "; 97 } 98 var lastIndex = priceWithCurrency.lastIndexOf(symbolToRemove); 99 result = priceWithCurrency.substring(0, lastIndex) + priceWithCurrency.substring(lastIndex + symbolToRemove.length); 100 } 101 } 102 return result; 103 }; 104 PriceFormatters.prototype.doFormat = function (price, formatter) { 105 var split = price.split('-'); 106 var result = ''; 107 for (var idx = 0; idx < split.length; idx++) { 108 //add a dash if there's more than 1 item in the array 109 if (idx > 0) { 110 result += ' - '; 111 } 112 result += formatter(split[idx]); 113 } //end each 114 return result; 115 }; 116 return PriceFormatters; 117}()); 118//# sourceMappingURL=Pricing.js.map; 119var ModularPDPHelper; 120(function (ModularPDPHelper) { 121 //URL Related 122 var PROTOCOL = window.location.protocol; 123 var HOST = window.location.host;
124 var PATH_NAME = window.location.pathname; 125 var HREF = window.location.href; 126 var URL_ORIGIN = PROTOCOL + "//" + Next.Settings.Channel.SiteUrl.split("//")[1]; 127 var URL_PATH_NAME = getUrlPathName(); 128 var URL_SEGMENTS = URL_PATH_NAME.split('/'); 129 var URL_SEGMENTS_COUNT = URL_SEGMENTS.length; 130 var HASH = window.location.hash; 131 var IS_REFERRER_SPREAD = HASH.indexOf("/referrer=spread") >= 0; 132 var UrlSegments = /** @class */ (function () { 133 function UrlSegments() { 134 } 135 return UrlSegments; 136 }()); 137 ModularPDPHelper.UrlSegments = UrlSegments; 138 function isUrlInOldFormat() { 139 //Detect PDP URL in old format (Eg: /style/st211319#548308 or /g8316s12#649138) 140 //Style and shot old URLs with spread page checks 141 return (URL_PATH_NAME.indexOf("style/") >= 0 && URL_SEGMENTS_COUNT == 3 && HASH && !IS_REFERRER_SPREAD) || (URL_SEGMENTS_COUNT == 2 && HASH && !IS_REFERRER_SPREAD) 142 ? true 143 : false; 144 } 145 ModularPDPHelper.isUrlInOldFormat = isUrlInOldFormat; 146 function isUrlInNewFormat() { 147 return (URL_PATH_NAME.indexOf("style/") >= 0 && URL_SEGMENTS_COUNT == 4) || (URL_PATH_NAME.indexOf("style/") < 0 && URL_SEGMENTS_COUNT == 3) 148 ? true 149 : false; 150 } 151 ModularPDPHelper.isUrlInNewFormat = isUrlInNewFormat; 152 function getNewUrlWithoutHash(urlCheckArguments) { 153 var itemNumber = urlCheckArguments.itemNumber || getItemNumber(); 154 itemNumber = getItemNumberWithoutDash(itemNumber); 155 var urlWithoutHash = HREF.substring(0, HREF.indexOf("#")); 156 //New url 157 var newUrl = urlWithoutHash + "/" + itemNumber; 158 if (!urlCheckArguments.includeOrigin) { 159 newUrl = newUrl.replace(URL_ORIGIN, ""); 160 } 161 if (!urlCheckArguments.includeStyle) { 162 newUrl = newUrl.replace("/style", ""); 163 } 164 if (urlCheckArguments.replaceLastSegment) { 165 var url = URL_PATH_NAME.replace("/style", ""); 166 var urlPathSegments = getSegmentOptions(url); 167 urlPathSegments.segments[urlPathSegments.segmentsCount - 1] = itemNumber; 168 newUrl = urlPathSegments.segments.join('/'); 169 } 170 return newUrl; 171 } 172 ModularPDPHelper.getNewUrlWithoutHash = getNewUrlWithoutHash; 173 function getNewUrlPathName(url, itemNumber) { 174 var urlPathSegments = getSegmentOptions(url); 175 urlPathSegments.segments[urlPathSegments.segmentsCount] = itemNumber; 176 return urlPathSegments.segments.join('/'); 177 } 178 ModularPDPHelper.getNewUrlPathName = getNewUrlPathName; 179 function getNewUrlWithHash(newHash) { 180 var newUrl = "", index = 0; 181 var formattedHash = newHash; 182 if (newHash.indexOf("/") >= 0) { 183 formattedHash = newHash.substring(0, newHash.indexOf("/")); 184 } 185 //New url format 186 if (isUrlPathNameInNewFormat()) { 187 index = URL_SEGMENTS_COUNT - 1; 188 } 189 //Old url format 190 else if (isUrlPathNameInOldFormat()) { 191 index = URL_SEGMENTS_COUNT; 192 } 193 URL_SEGMENTS[index] = formattedHash.replace("#", ""); 194 newUrl = URL_SEGMENTS.join("/") + newHash; 195 return newUrl; 196 } 197 ModularPDPHelper.getNewUrlWithHash = getNewUrlWithHash; 198 function updateUrlProperties(isShortUrl, firstSegment, secondSegment) { 199 URL_SEGMENTS = []; 200 if (isShortUrl) { 201 URL_SEGMENTS[1] = firstSegment; 202 URL_SEGMENTS[2] = secondSegment; 203 PATH_NAME = "/" + firstSegment + "/" + secondSegment; 204 } 205 else { 206 URL_SEGMENTS[1] = "style"; 207 URL_SEGMENTS[2] = firstSegment; 208 URL_SEGMENTS[3] = secondSegment; 209 PATH_NAME = "/style/" + firstSegment + "/" + secondSegment; 210 } 211 URL_SEGMENTS_COUNT = URL_SEGMENTS.length; 212 URL_PATH_NAME = PATH_NAME; 213 } 214 ModularPDPHelper.updateUrlProperties = updateUrlProperties; 215 function isUrlPathNameInNewFormat(pathName, urlSegmentsCount) { 216 var pathName = pathName || URL_PATH_NAME; 217 var urlSegmentsCount = urlSegmentsCount || URL_SEGMENTS_COUNT; 218 return (pathName.indexOf("style/") >= 0 && urlSegmentsCount == 4) || (pathName.indexOf("style/") < 0 && urlSegmentsCount == 3); 219 } 220 ModularPDPHelper.isUrlPathNameInNewFormat = isUrlPathNameInNewFormat; 221 function isUrlPathNameInOldFormat(pathName, urlSegmentsCount) { 222 var pathName = pathName || URL_PATH_NAME; 223 var urlSegmentsCount = urlSegmentsCount || URL_SEGMENTS_COUNT; 224 return (pathName.indexOf("style/") >= 0 && urlSegmentsCount == 3) || (pathName.indexOf("style/") < 0 && urlSegmentsCount == 2); 225 } 226 ModularPDPHelper.isUrlPathNameInOldFormat = isUrlPathNameInOldFormat; 227 function checkAndAddSlashBeforeUrl(url) { 228 return url.indexOf('/') != 0 ? '/' + url : url; 229 } 230 ModularPDPHelper.checkAndAddSlashBeforeUrl = checkAndAddSlashBeforeUrl; 231 function getItemNumber() { 232 var itemNumber = HASH.replace("#", ""); 233 if (itemNumber.indexOf("/") >= 0) { 234 itemNumber = itemNumber.substring(0, itemNumber.indexOf("/")); 235 } 236 return itemNumber; 237 } 238 ModularPDPHelper.getItemNumber = getItemNumber; 239 function getItemNumberWithoutDash(itemNumber) { 240 return itemNumber.replace(/-/g, ""); 241 } 242 ModularPDPHelper.getItemNumberWithoutDash = getItemNumberWithoutDash; 243 function getSegmentOptions(pathName) { 244 var urlSegments = pathName.split('/'); 245 var segments = new UrlSegments(); 246 segments.segments = urlSegments; 247 segments.segmentsCount = urlSegments.length; 248 return segments; 249 } 250 ModularPDPHelper.getSegmentOptions = getSegmentOptions; 251 function getUrlPathName() { 252 var urlWithoutHash = PROTOCOL + "//" + HOST + PATH_NAME; 253 return urlWithoutHash.replace(URL_ORIGIN, ""); 254 } 255 ModularPDPHelper.getUrlPathName = getUrlPathName; 256 function transformShotJsonShortenedNames(shot) { 257 if (shot.Media !== null && shot.Media !== undefined) { 258 shot.Media = mapMediaItems(shot.Media); 259 } 260 if (shot.Styles !== null && shot.Styles !== undefined) {
261 shot.Styles.forEach(function (style) { 262 if (style.Fits !== null && style.Fits !== undefined) { 263 style.Fits.forEach(function (fit) { 264 if (fit.Items !== null && fit.Items !== undefined) { 265 fit.Items.forEach(function (item) { 266 if (item.Media !== null && item.Media !== undefined) { 267 item.Media = mapMediaItems(item.Media); 268 } 269 if (item.Options !== null && item.Options !== undefined) { 270 item.Options = mapOptions(item.Options); 271 } 272 }); 273 } 274 }); 275 } 276 }); 277 } 278 } 279 ModularPDPHelper.transformShotJsonShortenedNames = transformShotJsonShortenedNames; 280 function mapOptions(options) { 281 options = options.map(function (_a) { 282 var Number = _a.Nmb, Name = _a.Nm, StockStatus = _a.SSt, Price = _a.Prc, SplitSizes = _a.SplSz, FitSizes = _a.FitSz, MarkdownType = _a.MdT, SizeType = _a.SzTyp; 283 return ({ 284 Number: Number, 285 Name: Name, 286 StockStatus: StockStatus, 287 Price: Price, 288 SplitSizes: SplitSizes, 289 FitSizes: FitSizes, 290 MarkdownType: MarkdownType, 291 SizeType: SizeType 292 }); 293 }); 294 return options; 295 } 296 function mapMediaItems(mediaItems) { 297 mediaItems = mediaItems.map(function (_a) { 298 var name = _a.nm, level = _a.lvl, imageType = _a.imgTyp, shotType = _a.shTyp, webType = _a.wbTyp, outlet = _a.outl, pageNo = _a.pgNo, id = _a.id, alsoItemLevel = _a.alLvl; 299 return ({ 300 name: name, 301 level: level, 302 imageType: imageType, 303 shotType: shotType, 304 webType: webType, 305 outlet: outlet, 306 pageNo: pageNo, 307 id: id, 308 alsoItemLevel: alsoItemLevel 309 }); 310 }); 311 return mediaItems; 312 } 313})(ModularPDPHelper || (ModularPDPHelper = {})); 314//# sourceMappingURL=modularpdphelper.js.map; 315/*! 316 * @overview es6-promise - a tiny implementation of Promises/A+. 317 * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 318 * @license Licensed under MIT license 319 * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE 320 * @version 3.0.2 321 */ 322 323(function(){"use strict";function lib$es6$promise$utils$$objectOrFunction(x){return typeof x==="function"||typeof x==="object"&&x!==null}function lib$es6$promise$utils$$isFunction(x){return typeof x==="function"}function lib$es6$promise$utils$$isMaybeThenable(x){return typeof x==="object"&&x!==null}var lib$es6$promise$utils$$_isArray;if(!Array.isArray){lib$es6$promise$utils$$_isArray=function(x){return Object.prototype.toString.call(x)==="[object Array]"}}else{lib$es6$promise$utils$$_isArray=Array.isArray}var lib$es6$promise$utils$$isArray=lib$es6$promise$utils$$_isArray;var lib$es6$promise$asap$$len=0;var lib$es6$promise$asap$$toString={}.toString;var lib$es6$promise$asap$$vertxNext;var lib$es6$promise$asap$$customSchedulerFn;var lib$es6$promise$asap$$asap=function asap(callback,arg){lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len]=callback;lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len+1]=arg;lib$es6$promise$asap$$len+=2;if(lib$es6$promise$asap$$len===2){if(lib$es6$promise$asap$$customSchedulerFn){lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush)}else{lib$es6$promise$asap$$scheduleFlush()}}};function lib$es6$promise$asap$$setScheduler(scheduleFn){lib$es6$promise$asap$$customSchedulerFn=scheduleFn}function lib$es6$promise$asap$$setAsap(asapFn){lib$es6$promise$asap$$asap=asapFn}var lib$es6$promise$asap$$browserWindow=typeof window!=="undefined"?window:undefined;var lib$es6$promise$asap$$browserGlobal=lib$es6$promise$asap$$browserWindow||{};var lib$es6$promise$asap$$BrowserMutationObserver=lib$es6$promise$asap$$browserGlobal.MutationObserver||lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver;var lib$es6$promise$asap$$isNode=typeof process!=="undefined"&&{}.toString.call(process)==="[object process]";var lib$es6$promise$asap$$isWorker=typeof Uint8ClampedArray!=="undefined"&&typeof importScripts!=="undefined"&&typeof MessageChannel!=="undefined";function lib$es6$promise$asap$$useNextTick(){return function(){process.nextTick(lib$es6$promise$asap$$flush)}}function lib$es6$promise$asap$$useVertxTimer(){return function(){lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush)}}function lib$es6$promise$asap$$useMutationObserver(){var iterations=0;var observer=new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush);var node=document.createTextNode("");observer.observe(node,{characterData:true});return function(){node.data=iterations=++iterations%2}}function lib$es6$promise$asap$$useMessageChannel(){var channel=new MessageChannel;channel.port1.onmessage=lib$es6$promise$asap$$flush;return function(){channel.port2.postMessage(0)}}function lib$es6$promise$asap$$useSetTimeout(){return function(){setTimeout(lib$es6$promise$asap$$flush,1)}}var lib$es6$promise$asap$$queue=new Array(1e3);function lib$es6$promise$asap$$flush(){for(var i=0;i<lib$es6$promise$asap$$len;i+=2){var callback=lib$es6$promise$asap$$queue[i];var arg=lib$es6$promise$asap$$queue[i+1];callback(arg);lib$es6$promise$asap$$queue[i]=undefined;lib$es6$promise$asap$$queue[i+1]=undefined}lib$es6$promise$asap$$len=0}function lib$es6$promise$asap$$attemptVertx(){try{var r=require;var vertx=r("vertx");lib$es6$promise$asap$$vertxNext=vertx.runOnLoop||vertx.runOnContext;return lib$es6$promise$asap$$useVertxTimer()}catch(e){return lib$es6$promise$asap$$useSetTimeout()}}var lib$es6$promise$asap$$scheduleFlush;if(lib$es6$promise$asap$$isNode){lib$es6$promise$asap$$scheduleFlush=lib$es6$promise$asap$$useNextTick()}else if(lib$es6$promise$asap$$BrowserMutationObserver){lib$es6$promise$asap$$scheduleFlush=lib$es6$promise$asap$$useMutationObserver()}else if(lib$es6$promise$asap$$isWorker){lib$es6$promise$asap$$scheduleFlush=lib$es6$promise$asap$$useMessageChannel()}else if(lib$es6$promise$asap$$browserWindow===undefined&&typeof require==="function"){lib$es6$promise$asap$$scheduleFlush=lib$es6$promise$asap$$attemptVertx()}else{lib$es6$promise$asap$$scheduleFlush=lib$es6$promise$asap$$useSetTimeout()}function lib$es6$promise$$internal$$noop(){}var lib$es6$promise$$internal$$PENDING=void 0;var lib$es6$promise$$internal$$FULFILLED=1;var lib$es6$promise$$internal$$REJECTED=2;var lib$es6$promise$$internal$$GET_THEN_ERROR=new lib$es6$promise$$internal$$ErrorObject;function lib$es6$promise$$internal$$selfFulfillment(){return new TypeError("You cannot resolve a promise with itself")}function lib$es6$promise$$internal$$cannotReturnOwn(){return new TypeError("A promises callback cannot return that same promise.")}function lib$es6$promise$$internal$$getThen(promise){try{return promise.then}catch(error){lib$es6$promise$$internal$$GET_THEN_ERROR.error=error;return lib$es6$promise$$internal$$GET_THEN_ERROR}}function lib$es6$promise$$internal$$tryThen(then,value,fulfillmentHandler,rejectionHandler){try{then.c
323all(value,fulfillmentHandler,rejectionHandler)}catch(e){return e}}function lib$es6$promise$$internal$$handleForeignThenable(promise,thenable,then){lib$es6$promise$asap$$asap(function(promise){var sealed=false;var error=lib$es6$promise$$internal$$tryThen(then,thenable,function(value){if(sealed){return}sealed=true;if(thenable!==value){lib$es6$promise$$internal$$resolve(promise,value)}else{lib$es6$promise$$internal$$fulfill(promise,value)}},function(reason){if(sealed){return}sealed=true;lib$es6$promise$$internal$$reject(promise,reason)},"Settle: "+(promise._label||" unknown promise"));if(!sealed&&error){sealed=true;lib$es6$promise$$internal$$reject(promise,error)}},promise)}function lib$es6$promise$$internal$$handleOwnThenable(promise,thenable){if(thenable._state===lib$es6$promise$$internal$$FULFILLED){lib$es6$promise$$internal$$fulfill(promise,thenable._result)}else if(thenable._state===lib$es6$promise$$internal$$REJECTED){lib$es6$promise$$internal$$reject(promise,thenable._result)}else{lib$es6$promise$$internal$$subscribe(thenable,undefined,function(value){lib$es6$promise$$internal$$resolve(promise,value)},function(reason){lib$es6$promise$$internal$$reject(promise,reason)})}}function lib$es6$promise$$internal$$handleMaybeThenable(promise,maybeThenable){if(maybeThenable.constructor===promise.constructor){lib$es6$promise$$internal$$handleOwnThenable(promise,maybeThenable)}else{var then=lib$es6$promise$$internal$$getThen(maybeThenable);if(then===lib$es6$promise$$internal$$GET_THEN_ERROR){lib$es6$promise$$internal$$reject(promise,lib$es6$promise$$internal$$GET_THEN_ERROR.error)}else if(then===undefined){lib$es6$promise$$internal$$fulfill(promise,maybeThenable)}else if(lib$es6$promise$utils$$isFunction(then)){lib$es6$promise$$internal$$handleForeignThenable(promise,maybeThenable,then)}else{lib$es6$promise$$internal$$fulfill(promise,maybeThenable)}}}function lib$es6$promise$$internal$$resolve(promise,value){if(promise===value){lib$es6$promise$$internal$$reject(promise,lib$es6$promise$$internal$$selfFulfillment())}else if(lib$es6$promise$utils$$objectOrFunction(value)){lib$es6$promise$$internal$$handleMaybeThenable(promise,value)}else{lib$es6$promise$$internal$$fulfill(promise,value)}}function lib$es6$promise$$internal$$publishRejection(promise){if(promise._onerror){promise._onerror
323(promise._result)}lib$es6$promise$$internal$$publish(promise)}function lib$es6$promise$$internal$$fulfill(promise,value){if(promise._state!==lib$es6$promise$$internal$$PENDING){return}promise._result=value;promise._state=lib$es6$promise$$internal$$FULFILLED;if(promise._subscribers.length!==0){lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish,promise)}}function lib$es6$promise$$internal$$reject(promise,reason){if(promise._state!==lib$es6$promise$$internal$$PENDING){return}promise._state=lib$es6$promise$$internal$$REJECTED;promise._result=reason;lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection,promise)}function lib$es6$promise$$internal$$subscribe(parent,child,onFulfillment,onRejection){var subscribers=parent._subscribers;var length=subscribers.length;parent._onerror=null;subscribers[length]=child;subscribers[length+lib$es6$promise$$internal$$FULFILLED]=onFulfillment;subscribers[length+lib$es6$promise$$internal$$REJECTED]=onRejection;if(length===0&&parent._state){lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish,parent)}}function lib$es6$promise$$internal$$publish(promise){var subscribers=promise._subscribers;var settled=promise._state;if(subscribers.length===0){return}var child,callback,detail=promise._result;for(var i=0;i<subscribers.length;i+=3){child=subscribers[i];callback=subscribers[i+settled];if(child){lib$es6$promise$$internal$$invokeCallback(settled,child,callback,detail)}else{callback(detail)}}promise._subscribers.length=0}function lib$es6$promise$$internal$$ErrorObject(){this.error=null}var lib$es6$promise$$internal$$TRY_CATCH_ERROR=new lib$es6$promise$$internal$$ErrorObject;function lib$es6$promise$$internal$$tryCatch(callback,detail){try{return callback(detail)}catch(e){lib$es6$promise$$internal$$TRY_CATCH_ERROR.error=e;return lib$es6$promise$$internal$$TRY_CATCH_ERROR}}function lib$es6$promise$$internal$$invokeCallback(settled,promise,callback,detail){var hasCallback=lib$es6$promise$utils$$isFunction(callback),value,error,succeeded,failed;if(hasCallback){value=lib$es6$promise$$internal$$tryCatch(callback,detail);if(value===lib$es6$promise$$internal$$TRY_CATCH_ERROR){failed=true;error=value.error;value=null}else{succeeded=true}if(promise===value){lib$es6$promise$$internal$$reject(promise,lib$es6$promise$$internal$$cannotReturnOwn());return}}else{value=detail;succeeded=true}if(promise._state!==lib$es6$promise$$internal$$PENDING){}else if(hasCallback&&succeeded){lib$es6$promise$$internal$$resolve(promise,value)}else if(failed){lib$es6$promise$$internal$$reject(promise,error)}else if(settled===lib$es6$promise$$internal$$FULFILLED){lib$es6$promise$$internal$$fulfill(promise,value)}else if(settled===lib$es6$promise$$internal$$REJECTED){lib$es6$promise$$internal$$reject(promise,value)}}function lib$es6$promise$$internal$$initializePromise(promise,resolver){try{resolver(function resolvePromise(value){lib$es6$promise$$internal$$resolve(promise,value)},function rejectPromise(reason){lib$es6$promise$$internal$$reject(promise,reason)})}catch(e){lib$es6$promise$$internal$$reject(promise,e)}}function lib$es6$promise$enumerator$$Enumerator(Constructor,input){var enumerator=this;enumerator._instanceConstructor=Constructor;enumerator.promise=new Constructor(lib$es6$promise$$internal$$noop);if(enumerator._validateInput(input)){enumerator._input=input;enumerator.length=input.length;enumerator._remaining=input.length;enumerator._init();if(enumerator.length===0){lib$es6$promise$$internal$$fulfill(enumerator.promise,enumerator._result)}else{enumerator.length=enumerator.length||0;enumerator._enumerate();if(enumerator._remaining===0){lib$es6$promise$$internal$$fulfill(enumerator.promise,enumerator._result)}}}else{lib$es6$promise$$internal$$reject(enumerator.promise,enumerator._validationError())}}lib$es6$promise$enumerator$$Enumerator.prototype._validateInput=function(input){return lib$es6$promise$utils$$isArray(input)};lib$es6$promise$enumerator$$Enumerator.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")};lib$es6$promise$enumerator$$Enumerator.prototype._init=function(){this._result=new Array(this.length)};var lib$es6$promise$enumerator$$default=lib$es6$promise$enumerator$$Enumerator;lib$es6$promise$enumerator$$Enumerator.prototype._enumerate=function(){var enumerator=this;var length=enumerator.length;var promise=enumerator.promise;var input=enumerator._input;for(var i=0;promise._state===lib$es6$promise$$internal$$PENDING&&i<length;i++){enumerator._eachEntry(input[i],i)}};lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry=function(entry,i){var enumerator=this;var c=enumerator._instanceConstructor;if(lib$es6$promise$utils$$isMaybeThenable(entry)){if(entry.constructor===c&&entry._state!==lib$es6$promise$$internal$$PENDING){entry._onerror=null;enumerator._settledAt(entry._state,i,entry._result)}else{enumerator._willSettleAt(c.resolve(entry),i)}}else{enumerator._remaining--;enumerator._result[i]=entry}};lib$es6$promise$enumerator$$Enumerator.prototype._settledAt=function(state,i,value){var enumerator=this;var promise=enumerator.promise;if(promise._state===lib$es6$promise$$internal$$PENDING){enumerator._remaining--;if(state===lib$es6$promise$$internal$$REJECTED){lib$es6$promise$$internal$$reject(promise,value)}else{enumerator._result[i]=value}}if(enumerator._remaining===0){lib$es6$promise$$internal$$fulfill(promise,enumerator._result)}};lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt=function(promise,i){var enumerator=this;lib$es6$promise$$internal$$subscribe(promise,undefined,function(value){enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED,i,value)},function(reason){enumerator._settledAt(lib$es6$promise$$internal$$REJECTED,i,reason)})};function lib$es6$promise$promise$all$$all(entries){return new lib$es6$promise$enumerator$$default(this,entries).promise}var lib$es6$promise$promise$all$$default=lib$es6$promise$promise$all$$all;function lib$es6$promise$promise$race$$race(entries){var Constructor=this;var promise=new Constructor(lib$es6$promise$$internal$$noop);if(!lib$es6$promise$utils$$isArray(entries)){lib$es6$promise$$internal$$reject(promise,new TypeError("You must pass an array to race."));return promise}var length=entries.length;function onFulfillment(value){lib$es6$promise$$internal$$resolve(promise,value)}function onRejection(reason){lib$es6$promise$$internal$$reject(promise,reason)}for(var i=0;promise._state===lib$es6$promise$$internal$$PENDING&&i<length;i++){lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]),undefined,onFulfillment,onRejection)}return promise}var lib$es6$promise$promise$race$$default=lib$es6$promise$promise$race$$race;function lib$es6$promise$promise$resolve$$resolve(object){var Constructor=this;if(object&&typeof object==="object"&&object.constructor===Constructor){return object}var promise=new Constructor(lib$es6$promise$$internal$$noop);lib$es6$promise$$internal$$resolve(promise,object);return promise}var lib$es6$promise$promise$resolve$$default=lib$es6$promise$promise$resolve$$resolve;function lib$es6$promise$promise$reject$$reject(reason){var Constructor=this;var promise=new Constructor(lib$es6$promise$$internal$$noop);lib$es6$promise$$internal$$reject(promise,reason);return promise}var lib$es6$promise$promise$reject$$default=lib$es6$promise$promise$reject$$reject;var lib$es6$promise$promise$$counter=0;function lib$es6$promise$promise$$needsResolver(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function lib$es6$promise$promise$$needsNew(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object c
323onstructor cannot be called as a function.")}var lib$es6$promise$promise$$default=lib$es6$promise$promise$$Promise;function lib$es6$promise$promise$$Promise(resolver){this._id=lib$es6$promise$promise$$counter++;this._state=undefined;this._result=undefined;this._subscribers=[];if(lib$es6$promise$$internal$$noop!==resolver){if(!lib$es6$promise$utils$$isFunction(resolver)){lib$es6$promise$promise$$needsResolver()}if(!(this instanceof lib$es6$promise$promise$$Promise)){lib$es6$promise$promise$$needsNew()}lib$es6$promise$$internal$$initializePromise(this,resolver)}}lib$es6$promise$promise$$Promise.all=lib$es6$promise$promise$all$$default;lib$es6$promise$promise$$Promise.race=lib$es6$promise$promise$race$$default;lib$es6$promise$promise$$Promise.resolve=lib$es6$promise$promise$resolve$$default;lib$es6$promise$promise$$Promise.reject=lib$es6$promise$promise$reject$$default;lib$es6$promise$promise$$Promise._setScheduler=lib$es6$promise$asap$$setScheduler;lib$es6$promise$promise$$Promise._setAsap=lib$es6$promise$asap$$setAsap;lib$es6$promise$promise$$Promise._asap=lib$es6$promise$asap$$asap;lib$es6$promise$promise$$Promise.prototype={constructor:lib$es6$promise$promise$$Promise,then:function(onFulfillment,onRejection){var parent=this;var state=parent._state;if(state===lib$es6$promise$$internal$$FULFILLED&&!onFulfillment||state===lib$es6$promise$$internal$$REJECTED&&!onRejection){return this}var child=new this.constructor(lib$es6$promise$$internal$$noop);var result=parent._result;if(state){var callback=arguments[state-1];lib$es6$promise$asap$$asap(function(){lib$es6$promise$$internal$$invokeCallback(state,child,callback,result)})}else{lib$es6$promise$$internal$$subscribe(parent,child,onFulfillment,onRejection)}return child},"catch":function(onRejection){return this.then(null,onRejection)}};function lib$es6$promise$polyfill$$polyfill(){var local;if(typeof global!=="undefined"){local=global}else if(typeof self!=="undefined"){local=self}else{try{local=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}}var P=local.Promise;if(P&&Object.prototype.toString.call(P.resolve())==="[object Promise]"&&!P.cast){return}local.Promise=lib$es6$promise$promise$$default}var lib$es6$promise$polyfill$$default=lib$es6$promise$polyfill$$polyfill;var lib$es6$promise$umd$$ES6Promise={Promise:lib$es6$promise$promise$$default,polyfill:lib$es6$promise$polyfill$$default};if(typeof define==="function"&&define["amd"]){define(function(){return lib$es6$promise$umd$$ES6Promise})}else if(typeof module!=="undefined"&&module["exports"]){module["exports"]=lib$es6$promise$umd$$ES6Promise}else if(typeof this!=="undefined"){this["ES6Promise"]=lib$es6$promise$umd$$ES6Promise}lib$es6$promise$polyfill$$default()}).call(this);; 324 325/* USAGE: 326 $(document).on("featureSettingsReady", function () { 327 if (Next.FeatureManager.FeatureSettings.RecentlyViewed.IsEnabled) { 328 // feature setting dependent logic here.. 329 } 330 }); 331*/ 332 333var FeatureSettingsLoaded = false; 334 335$(document).ready(function () { 336 // set up namespacing 337 if (typeof window.Next === "undefined") window.Next = {}; 338 if (typeof window.Next.FeatureManager === "undefined") window.Next.FeatureManager = {}; 339 340 Next.FeatureManager.FeatureSettings = (function () { 341 var host = (window.location.protocol == "http:" ? Next.Settings.Channel.SiteUrl : Next.Settings.Channel.SecureSiteUrl) + '/'; 342 $.ajax({
343 url: host + "FeatureSettings/GetClientFeatures", 344 type: "GET", 345 dataType: "json", 346 success: function (data) { 347 Next.FeatureManager.FeatureSettings = data; 348 }, 349 error: function (jqXhr, textStatus, errorThrown) { 350 if (window.console && console.log) { 351 console.log(errorThrown); 352 } 353 }, 354 complete: function () { 355 FeatureSettingsLoaded = true; 356 $(document).trigger("featureSettingsReady"); 357 } 358 }); 359 360 var defaultSettings = { 361 RecentlyViewed: { 362 IsEnabled: true, 363 DisplayCounter: false, 364 DisplayInFooter: true, 365 DisplayInPage: true, 366 DisplayInSecureFooter: false, 367 DisplayViewAllLink: false, 368 EnableAdditions: true, 369 InPageDisabledPageTypes: [ 370 // ENABLED: evoucher, homepage, shoppingbag, shot, sofashot 371 "brands", 372 "branddirectory", 373 "classic", 374 "classicstorefront", 375 "collection", 376 "complete", 377 "delivery", 378 "department", 379 "footer", 380 "item", 381 "login", 382 "meganav", 383 "myaccount", 384 "pageframe", 385 "payment", 386 "primary", 387 "quickshop", 388 "returns", 389 "search", 390 "singleitem", 391 "spread", 392 "spreadonly", 393 "spread_carousel", 394 "storefront", 395 "story", 396 "subcollection", 397 "undefined" 398 ], 399 ToolbarDisabledPageTypes: [] 400 }, 401 Search: { 402 BaseAddress: "http://localhost:62161", 403 SuggestionsEnabled: true, 404 RecentSearchesSaved: 5 405 } 406 }; 407 408 return defaultSettings; 409 })(); 410}); 411 412 413 414; 415// --------------------------------------------------------------------------------- 416// Personalisation JS Library 417// --------------------------------------------------------------------------------- 418/// <reference path="typings/jquery/jquery.d.ts" /> 419// --------------------------------------------------------------------------------- 420// Personalisation Module 421// --------------------------------------------------------------------------------- 422var Personalisation; 423(function (Personalisation) { 424 //#region Constants, Variables & Properties ------------------------------- 425 // Constants (psuedo) 426 var PERSONALISATION_DATASTORAGE_KEYNAME = "PersonalisationEntity"; 427 // Local variables 428 var _personalisationEntity = null; 429 // Public variables 430 Personalisation.isReady = false; 431 Personalisation.isLocalStorageSupported = false; 432 //#endregion 433 //#region Initialisation -------------------------------------------------- 434 function initState() { 435 // iOS browsing in private mode can show localstorage as available but will throw exceptions when calling get/set item data 436 Personalisation.isLocalStorageSupported = false; 437 // Replace console.log with an empty function if browser has disabled or not support it. 438 // Thus stopping further console.log errors. 439 window['console'] = window['console'] || {}; 440 window['console']['log'] = window['console']['log'] || function () { }; 441 try { 442 if ("localStorage" in window && window.localStorage && typeof (localStorage) !== "undefined") { 443 localStorage.setItem("__tmp_localstoragetest", '1'); 444 localStorage.getItem("__tmp_localstoragetest"); 445 localStorage.removeItem("__tmp_localstoragetest"); 446 } 447 Personalisation.isLocalStorageSupported = true; 448 } 449 catch (error) { 450 Personalisation.isLocalStorageSupported = false; 451 } 452 // InitState function will try to retrieve personalisation entity object from client first (localstorage), 453 // otherwise will attempt to get from personalisation service on server and if not returned will then 454 // create a new local entity. At this stage it will not be saved to LocalStorage or server until a data item
455 // is set (added) 456 if (_personalisationEntity == null) { 457 clientGetObject(); // Attempt to get entity from localstorage 458 if (_personalisationEntity == null) { 459 // If no personalisation data in localstorage then attempt to get from server... 460 // If data is returned then it will have updated _personalisationEntity 461 apiGetObject().then(function () { 462 // If '_personalisationEntity' still null then create blank entity with visitor id only 463 if (_personalisationEntity == null) { 464 _personalisationEntity = new PersonalisationEntity(); 465 _personalisationEntity.VisitorId = getVisitorId(); 466 } 467 Personalisation.isReady = true; 468 console.log("Personalisation client is ready"); 469 })['catch'](function () { 470 // If '_personalisationEntity' still null then create blank entity with visitor id only 471 if (_personalisationEntity == null) { 472 _personalisationEntity = new PersonalisationEntity(); 473 _personalisationEntity.VisitorId = getVisitorId(); 474 } 475 Personalisation.isReady = true; 476 console.log("Personalisation client is ready"); 477 }); 478 return; // Return so that we process no further and leave to promise callbacks 479 } 480 } 481 Personalisation.isReady = true; 482 console.log("Personalisation client is ready"); 483 } 484 Personalisation.initState = initState; 485 //#endregion 486 //#region Public Functions ------------------------------------------------ 487 function itemGet(key) { 488 if (_personalisationEntity == null) { 489 clientGetObject(); // Attempt to get entity from localstorage 490 } 491 if (_personalisationEntity != null) { 492 // Check expiry value 493 if (_personalisationEntity.Expiry != null) { 494 if (_personalisationEntity.Expiry < new Date()) { 495 // Expire (remove) personalisation entity (client side only) 496 verifyClientObject(); 497 return null; 498 } 499 } 500 // Return item value 501 if (_personalisationEntity.Data[key] != null) { 502 return _personalisationEntity.Data[key]; 503 } 504 else { 505 return null; 506 } 507 } 508 else { 509 return null; 510 } 511 } 512 Personalisation.itemGet = itemGet; 513 function itemSet(key, value) { 514 if (_personalisationEntity == null) { 515 clientGetObject(); // Attempt to get entity from localstorage 516 // If no personalisation data then create entity object 517 if (_personalisationEntity == null) { 518 _personalisationEntity = new PersonalisationEntity(); 519 _personalisationEntity.VisitorId = getVisitorId(); 520 } 521 } 522 // Ensure VisitorId is populated 523 if (_personalisationEntity.VisitorId == null || _personalisationEntity.VisitorId === "") { 524 _personalisationEntity.VisitorId = getVisitorId(); 525 } 526 if (value != null) { 527 _personalisationEntity.Data[key] = value; 528 _personalisationEntity.Expiry = new Date(new Date().getTime() + PersonalisationExpiryInMinutes * 60000); 529 } 530 else { 531 delete _personalisationEntity.Data[key]; 532 } 533 clientSetObject(); 534 } 535 Personalisation.itemSet = itemSet; 536 // GET the PersonalisationEntity object (_personalisationEntity) from server for visitor 537 function apiGetObject() { 538 if (PersonalisationClientOnly === true) 539 return Promise.reject("501"); // reject promise with 501 Not Implemented? 540 var promise = new Promise(function (resolve, reject) { 541 var visitorId = getVisitorId(); 542 if (visitorId != null && visitorId != "") { 543 $.ajax({
544 method: "GET", 545 url: PersonalisationApiUrl + "/visitor/" + visitorId 546 }) 547 .done(function (data, statusText, xhr) { 548 var status = xhr.status; 549 if (status >= 200 && status < 300 || status === 304) { 550 var getPersonalisationDataResponse = data; 551 _personalisationEntity = getPersonalisationDataResponse.PersonalisationEntity; 552 // Update localStorage 553 clientSetObject(); 554 // Resolve promise 555 resolve(data); 556 } 557 else { 558 // We only see success as data being returned (i.e 200 - not 404 etc) - therefore will reject promise 559 // Reject promise 560 reject(status); 561 } 562 }) 563 .fail(function (jqXHR, textStatus, errorThrown) { 564 console.warn("Personalisation API GET call failed"); 565 // Reject promise 566 reject(jqXHR.status); 567 }); 568 } 569 else { 570 reject("No VisitorID supplied"); 571 } 572 }); 573 return promise; 574 } 575 Personalisation.apiGetObject = apiGetObject; 576 // POST the current PersonalisationEntity object (_personalisationEntity) to server for visitor 577 function apiSetObject() { 578 if (PersonalisationClientOnly === true) 579 return; 580 // Simple fire and forget call to Personalisation API 581 $.ajax({
582 method: "POST", 583 headers: { 584 'Accept': 'application/json', 585 'Content-Type': 'application/json' 586 }, 587 url: PersonalisationApiUrl + "/visitor", 588 dataType: 'json', 589 processData: false, 590 data: JSON.stringify(_personalisationEntity) 591 }); 592 } 593 //#endregion 594 //#region Private Functions ------------------------------------------------ 595 // Get the personalisation entity for current visitor from local storage. 596 // If it does not exist then an attempt is made to the Personalisation API 597 function clientGetObject() { 598 // Verify client object (inc. check expiry) 599 verifyClientObject(); 600 if (Personalisation.isLocalStorageSupported) { 601 if (localStorage.getItem(PERSONALISATION_DATASTORAGE_KEYNAME) !== null) { 602 _personalisationEntity = JSON.parse(localStorage.getItem(PERSONALISATION_DATASTORAGE_KEYNAME)); 603 } 604 } 605 else { 606 // TODO: Fallback for non-localstorage support? 607 } 608 } 609 // Save the personalisation entity for current visitor in local storage and call function to save to Personalisation API. 610 function clientSetObject() { 611 if (Personalisation.isLocalStorageSupported) { 612 // Store on client in LocalStorage... 613 localStorage.setItem(PERSONALISATION_DATASTORAGE_KEYNAME, JSON.stringify(_personalisationEntity)); 614 // Update server 615 apiSetObject(); 616 } 617 else { 618 // TODO: Fallback for non-localstorage support? 619 } 620 } 621 // Expire personalisation entity 622 function verifyClientObject() { 623 // Check if expired?... If it has then only removes from localstorage (not server). 624 if (Personalisation.isLocalStorageSupported) { 625 if (localStorage.getItem(PERSONALISATION_DATASTORAGE_KEYNAME) !== null) { 626 var perEntity = localStorage.getItem(PERSONALISATION_DATASTORAGE_KEYNAME); 627 // Check expiry value 628 if (perEntity.Expiry != null) { 629 if (_personalisationEntity.Expiry < new Date()) { 630 // Remove personalisation entity (client side only) 631 localStorage.removeItem(PERSONALISATION_DATASTORAGE_KEYNAME); 632 return null; 633 } 634 } 635 } 636 } 637 } 638 // Get VisitorId 639 function getVisitorId() { 640 // We are unable to access VisitorID direct from cookie as NextVisitor cookie has HttpOnly attribute 641 // VisitorID is made available to every page using shared layout as var 'PersonalisationVisitorID' 642 // (It is also available VisitorID from NextBasket although this is not available immediately) 643 if (typeof NextBasket != "undefined") 644 return NextBasket.VisitorID; 645 else 646 return null; 647 // TODO: Can't use the old PersonalisationVisitorID property as this would be cached on Akamai (the devil) for all users with wrong ID. 648 //return PersonalisationVisitorID; 649 } 650 Personalisation.getVisitorId = getVisitorId; 651 //#endregion 652 //#region Interfaces & Classes -------------------------------------------- 653 /// <summary> 654 /// PersonalisationEntity class. Represents the server side personalisation entity 655 /// </summary> 656 var PersonalisationEntity = /** @class */ (function () { 657 function PersonalisationEntity() { 658 this.Data = {}; 659 } 660 return PersonalisationEntity; 661 }()); 662 //#endregion 663})(Personalisation || (Personalisation = {})); 664// Initialise Personalisation module... 665Personalisation.initState(); 666//# sourceMappingURL=personalisation.js.map; 667/* eslint-disable */ 668var HContextArray = new Array(); // Define Horizontal Context Array (for detecting changes in department, and the direction in which the transition should move) 669var DynamicResources = new Array(); // Define an array to store the dynamically added resources for the current page, so they can be removed when we leave the page asynchronously 670var PreviousPageResources; 671var PageURLInput = String(window.location.pathname) + String(window.location.search); // Drop the current page url in to a variable - used to detect context changes when triggering back functionality 672var siteAssetsUrl; // This is the URL that is used to request further assests such as CSS and JS on the fly - Set by the header.xsl code 673var PageType; 674var OverrideWithSecureFooter = (OverrideWithSecureFooter == null) ? false : OverrideWithSecureFooter; 675var PageTransitionLock = false; 676var preventReplaceState = false; // This is true if we don't want to replace state when leaving this page 677var PagePopped = false; // This is true if the popstate has been triggered for this page load 678var PagePoppedData = null; // This will contain history object data if the page is popped 679 680var mouseOverHeader = false; 681var liveSpreadPageIndex; // There are conditions where the header needs to know if the mouse is still over them. This variable is used to determine that. It should be set on mouseover/mouseenter, and cleared on mouseout/mouseleave as required. 682 683var ScriptsLoadedCallback = null; // Store the ScriptsLoadedCallback as a null function. This will get updated by indiviual pages and cleared after it has been called (this saves us from the browser keeping hold of the previous pages ScriptsLoadedCallback function and causing double init issues) 684var PageVisibleCallback = null; // Store the PageVisibleCallback as a null function. This will get updated by indiviual pages and cleared after it has been called (this saves us from the browser keeping hold of the previous pages PageVisibleCallback function and causing double init issues) 685 686var ThirdPartiesEnabled = false; 687 688var GomezEnabled; 689var GomezPageTagEnabled; 690var GomezPageId; 691var gomez; 692 693var connectionSpeed = 0; 694 695var isTouchEnabled = false; 696var isMSPointerDevice = false; 697 698var MainSiteURL = window.location.host; 699 700var isErrorPage = false; 701var isSocialPLPPage = false; 702 703var rvScrollOffset = 0; //stores item scroller offset. This will be useful in Footer.js to i
703dentify document click 704 705var currentKey = null 706 707window.console = window.console || { log: function (_) { } }; 708window._AutofillCallbackHandler = window._AutofillCallbackHandler || function () { } 709 710var jqueryUpgradeEnabledOnPage = jqueryUpgradeEnabledOnPage || false; 711 712if (console && console.log) { 713 console.log('\n t\n n nnnnn eeeee xx xx ttttt\n nn n e e xx xx t\n n n eeeeeeeee xxx t\n n n e xx xx t t\n n n eeeeee xx xx ttt\n\n S Y S T E M S\n\nThanks for looking! Our systems teams develop and\nmanage the IT that supports our entire enterprise.\nAnd we\'re recruiting now.\n\nHave a look at https://careers.next.co.uk/head-office/\n\n'); 714} 715 716if ((typeof (navigator.maxTouchPoints) == 'undefined')) { 717 if ((typeof (window.ontouchstart) != 'undefined')) { 718 isTouchEnabled = true; 719 } 720 else if (navigator.msMaxTouchPoints > 0) { //IE10 on touch device supports this 721 isTouchEnabled = true; 722 isMSPointerDevice = true; 723 } 724} 725else if (navigator.maxTouchPoints > 0) { 726 isTouchEnabled = true; 727 isMSPointerDevice = true; 728} 729 730var enableTouchFunctions = false; 731 732if (isTouchEnabled) { 733 enableTouchFunctions = true; 734} 735 736if (/\bMSIE 6/.test(navigator.userAgent) && !window.opera) { 737 738 if (window.location.pathname != '/browser-update') { 739 window.location = '/browser-update'; 740 } 741} 742 743var isIpad = false; 744var isIOS = false; 745var iOS = 0; 746 747if (navigator.userAgent.indexOf('iPad') != -1) { 748 isIpad = true; 749 750 if (/OS [2-4]_\d(_\d)? like Mac OS X/i.test(navigator.userAgent)) { 751 //Treat 2 - 4 as just 4 752 iOS = 4; 753 } else if (/CPU like Mac OS X/i.test(navigator.userAgent)) { 754 iOS = 1; 755 } else { 756 //Set to 5 for all iOS 5 and above 757 iOS = 5; 758 } 759} 760 761if (navigator.userAgent.indexOf('iPad') != -1 762 || navigator.userAgent.indexOf('iPhone') != -1 763 || navigator.userAgent.indexOf('iPod') != -1) { 764 isIOS = true; 765 ConsoleLog("iOs detected"); 766} 767 768// The documentState is a JSON object that contains params that are specific to the page. 769// These will be appended to the path as querystring params when transitioning and used on page load. 770// If a variable name begins with 'hidden', it will not be used as a querystring param 771// The hashtag variable is reserved & placed at the end of the querystring 772// e.g. { spreadview: true, hiddenSearchbreadcrumb: '<div>secret</div>', hashtag: '123456x50' } Will be output as /path?speadview=true#123456x50 773// The following methods can be used to manage documentState: getDocStateParamValue(Name), addParamToDocState(Name, Value), getDocStateString(documentState) 774var documentState = {}; // Declare global to store current document state 775 776try { 777 if (typeof window.history.replaceState != "undefined") { // If we're navigating the site rather than clicking back or forward 778 window.history.replaceState({ "url": window.location.href, "title": document.title, "DocState": documentState, "scrollPoint": $(window).scrollTop() }, document.title, window.location.href); // On load, attempt to replace the last state object on the history stack with one that is populated with the relevent transition data (although in all likely hood this will just insert a new item) 779 } 780} catch (e) { 781 ConsoleLog('Unable to replace history state'); 782} 783 784/* - Global Event Handlers - */ 785 786/* - Window Focus Detection - */ 787window.onfocus = onFocus; 788window.onblur = onBlur; 789 790// Chrome detect as variable 791var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; 792// isolated Chrome hack for window focus 793$(window).on("click", function () { 794 if (is_chrome) { 795 $(window).trigger("focus"); 796 } 797}); 798 799function AddMouseEarTouchEvents() { 800 $(".MouseEar").on('touchstart', function () { 801 $(this).addClass('hover'); 802 });
803 804 $(".MouseEar").on('touchend', function () { 805 var $ear = $(this); 806 setTimeout(function () { 807 $ear.removeClass('hover'); 808 }, 250); 809 }); 810} 811 812$(document).ready(function () { 813 AddMouseEarTouchEvents(); 814 815 if (!Next.Settings.UI.PlpDesktopJumpinessFix) { 816 setPagePositioning(); 817 } 818 819 //Start timing requests - this is used in LoadHTMLContent in ajax.js 820 lastRequest = new Date().getTime(); 821 822 //Look in current content page params - if it exists, we have fully loaded a new page (not from loadHTML ajax) 823 //Update SEO and breadcrumb here 824 825 //Update breadcrumb/SEO 826 if ($(".CurrentContent #PageParams").length) { 827 828 var BCHTML = $(".CurrentContent #PageParams #BreadcrumbWrapper").html(); 829 830 if (BCHTML != null && BCHTML.length) { 831 UpdateBreadcrumbTrail(BCHTML); 832 } 833 //Remove page params from page 834 $(".CurrentContent #PageParams").remove(); 835 } 836 837 LoadGomezScripts(); 838 839 SetupFallbackFor
839ChannelisedLogos(); 840}); 841 842function onBlur() { 843 if ($("body").hasClass("Focus")) { 844 $("body").removeClass("Focus"); 845 } 846}; 847 848function onFocus() { 849 //Need to check viewport size here as well as the user may have resized window while in a different tab 850 if (!Next.Settings.UI.PlpDesktopJumpinessFix) { 851 setPagePositioning(); 852 } 853 854 if (!$("body").hasClass("Focus")) { 855 $("body").addClass("Focus"); 856 } 857}; 858 859var addSpreadToHistory = true; 860var trackSpreadClick = true; 861var previousPageGrid = false; 862var hideScrollBars = false; 863window.onpopstate = function (stackstate) { 864 865 866 if ($("body").css("overflow") == "hidden" && !hideScrollBars) { 867 $("body").css("overflow", "visible"); 868 } 869 870 $("#SuperZoomBoxDiv > a").trigger("click"); 871 872 // Fired when user clicks forward or back buttons in browser, or when window.history.back() or .forward() is used. WARNING: Chrome incorrectly fires this event handler on load too. This should be catered for by the replace state above. 873 PagePopped = true; 874 preventReplaceState = true; 875 876 if (stackstate.state == null) { // stackstate tends to only be null on initial vist page OR if we have done a full page reload, ie F5 or transitioning to another page without using AJAX. 877 if (!window.location.pathname.substring(0, PageURLInput.length) == PageURLInput + "#") { 878 LoadHTMLContent(window.location.href, window.title, "", false, 0, {}); // in either instance, we need to reload the relevant content. 879 } 880 } else { 881 882 // The stackstate will come in handy for some pages... 883 PagePoppedData = stackstate.state; 884 885 if (previousPageGrid) { 886 previousPageGrid = false; 887 } 888 889 if (stackstate.state.url != null) { 890 //If the state object has a url property 891 LoadHTMLContent(stackstate.state.url, stackstate.state.title, "", false, stackstate.state.scrollPoint, stackstate.state.DocState); // Load the content defined by the history object - as we're going back or forward in the history stack at this point, the 4th param is set to false to prevent a new history item being inserted in the stack. 892 } 893 } 894} 895 896window.onload = function (event) { // Fired on initial page load or when user hits refresh or F5. 897 $(window).trigger("focus"); 898 positionMouseEars(); // Check to see if the footer is in the correct state for the document's initial scroll position. The transition param allows the toolbar to animate in gracefully. 899} 900 901window.onscroll = function (event) { 902 903 if (!$(".ContentArea").hasClass("HideOverflow")) { 904 resizeContentArea(); 905 } 906} 907 908window.onresize = function (event) { // Fired when the user resizes their browser window 909 resizeContentArea(); 910 positionMouseEars(); 911 if (!Next.Settings.UI.PlpDesktopJumpinessFix) { 912 setPagePositioning(); 913 } 914} 915 916$(document).on("keydown", function (event) { 917 switch (event.keyCode) { 918 case 27: 919 if (typeof ModalPopup === 'object') { 920 ModalPopup.close(); 921 } 922 break; 923 } 924}); 925 926$(document).on("keyup", function (event) { 927 switch (event.keyCode) { 928 case 37: 929 if ($(".MouseEar.Left.Active a")) { 930 try { 931 $(".MouseEar.Left.Active a")[0].trigger("click"); 932 } catch (e) { 933 } 934 } 935 break; 936 case 39: 937 if ($(".MouseEar.Right.Active a")) { 938 try { 939 $(".MouseEar.Right.Active a")[0].trigger("click"); 940 } catch (e) { 941 } 942 } 943 break; 944 } 945}); 946
947/* - SEO / Social Media - */ 948function updateSeo(seoTitle, seoDescription, seoKeywords, seoRobots, seoCanonical) { 949 var defaultTitle = "Next Official Site: Shop for Clothes, Shoes, Electricals, Homeware & More"; 950 var defaultDescription = "next.co.uk - shop online for the latest fashion for women, men, children and homeware. Next day delivery and free returns available."; 951 var defaultKeywords = "Next,next.co.uk,Next Directory,Next Flowers,Next Electrics,fashion,clothes,womens,mens,childrens,home,furniture"; 952 var defaultRobots = ""; 953 954 if ((window.Next && Next.Settings)) { 955 defaultTitle = Next.Settings.Channel.SEOTitle; 956 defaultDescription = Next.Settings.Channel.SEODescription; 957 defaultKeywords = Next.Settings.Channel.SEOKeywords; 958 defaultRobots = Next.Settings.Channel.SEORobots; 959 } 960 961 var headEl = $("head"); 962 963 function createOrGetMeta(name, value) { 964 if (value != "") { 965 var el = $("meta[name='" + name + "']"); 966 if (el != null) { 967 el.attr("content", value); 968 } else { 969 headEl.append($("<meta />").attr("name", name).attr("value", value)); 970 } 971 } 972 } 973 974 function createOrGetCanonical(value) { 975 if (value != "") { 976 var el = $("link[rel='canonical']"); 977 if (el != null) { 978 el.attr("rel", "canonical").attr("href", value) 979 } else { 980 headEl.append($("<link />").attr("rel", "canonical").attr("href", value)); 981 } 982 } 983 } 984 985 seoTitle = seoTitle || defaultTitle; 986 seoDescription = seoDescription || defaultDescription; 987 seoKeywords = seoKeywords || defaultKeywords; 988 seoRobots = seoRobots || defaultRobots; 989 990 createOrGetMeta("title", seoTitle); 991 createOrGetMeta("description", seoDescription); 992 createOrGetMeta("keywords", seoKeywords); 993 createOrGetMeta("robots", seoRobots); 994 createOrGetCanonical(seoCanonical); 995 996 document.title = seoTitle; 997} 998 999/* - Dynamic Resource Management - */ 1000function InsertScript(scriptSrc) { // Dynamically inserts JS file references :) 1001 var headID = document.getElementsByTagName("head")[0]; 1002 var newScript = document.createElement('script'); 1003 newScript.type = 'text/javascript'; 1004 newScript.src = scriptSrc; 1005 headID.appendChild(newScript); 1006 DynamicResources.push({ Type: "JS", Path: scriptSrc }); 1007} 1008 1009function InsertCSS(cssSrc) { // Dynamically inserts CSS file references :) 1010 var headID = document.getElementsByTagName("head")[0]; 1011 var cssNode = document.createElement('link'); 1012 cssNode.href = cssSrc; 1013 cssNode.rel = 'stylesheet'; 1014 //cssNode.media = 'screen'; // Required? 1015 headID.appendChild(cssNode); 1016 DynamicResources.push({ Type: "CSS", Path: cssSrc }); 1017} 1018 1019function AddDynamicResource(type, path) { 1020 DynamicResources.push({ Type: type, Path: path }); 1021} 1022 1023function checkPreloadSupported() { 1024 if(!window.isPreloadSupportedChecked) { 1025 window.isPreloadSupportedChecked = true; 1026 var relList = document.createElement('link').relList; 1027 window.isPreloadSupported = !!(relList && relList.supports && relList.supports('preload')); 1028 } 1029} 1030 1031function CreateDynamicResources() { 1032 var $ = jQuery; 1033 var Animation = createAnimation(); 1034 1035 checkPreloadSupported(); 1036 1037 let showBreadcrumbs = true; 1038 1039 if (window.location.pathname == "/" || window.location.pathname.toLowerCase() == Next.Settings.Channel.MainSiteAbsolutePath.toLowerCase() || !showBreadcrumbs) { 1040 if ($(".PageHeader").hasClass("Large")) { 1041 Animation = HideBreadcrumbBar(Animation); 1042 } 1043 } else { 1044 if (!$(".PageHeader").hasClass("Large")) { 1045 Animation = ShowBreadcrumbBar(Animation); 1046 } 1047 } 1048 1049 runAnimation(Animation); 1050 1051 var headID = document.getElementsByTagName("head")[0]; 1052 var waitForCSS = false; 1053 var checkPreviousPageResources = false; 1054 //If previous page resources exist, we'll need to check it while we add new resources 1055 if (PreviousPageResources != null && PreviousPageResources.length) { checkPreviousPageResources = true; } 1056 1057 1058 for (var i = 0; i < DynamicResources.length; i++) { // Split in to 2 loops to force CSS to render first. 1059 waitForCSS = false; 1060 if (DynamicResources[i].Type == "CSS") { 1061 waitForCSS = true; 1062 var doInsert = true; 1063 1064 //Check if item already exists in memory from previous page 1065 if (checkPreviousPageResources) { 1066 var exInstance = $.grep(PreviousPageResources, function (n) { 1067 return n.Path == DynamicResources[i].Path; 1068 }); 1069 //If the item already exists, don't add it again 1070 if (exInstance != null && exInstance.length) { 1071 doInsert = false; 1072 } 1073 } 1074 1075 if (doInsert) { 1076 var cssNode = document.createElement('link'); 1077 cssNode.href = DynamicResources[i].Path; 1078 cssNode.rel = 'stylesheet'; 1079 if( window.isPreloadSupported ) { // IE11 safety check 1080 cssNode.rel = 'preload'; 1081 cssNode.setAttribute('as', 'style'); 1082 cssNode.setAttribute('onload', "this.rel='stylesheet';"); 1083 } 1084 headID.appendChild(cssNode); 1085 } 1086 } 1087 } 1088 CreateDynamicJSResources(waitForCSS) 1089} 1090 1091function CreateDynamicJSResources(waitForCSS) { 1092 1093 if (waitForCSS == false 1094 || $(".CSSLoadDetector").css("background-color") == "rgb(255, 255, 255)" 1095 || $(".CSSLoadDetector").css("background-color") == "#ffffff" 1096 || $(".CSSLoadDetector").css("background-color") == "#fff" 1097 || $(".CSSLoadDetector").css("background-color") == "white") { 1098 1099 loadScripts(DynamicResources, 0); 1100 1101 $(".CurrentContent").removeClass("FirstLoad"); 1102 } else { 1103 window.setTimeout("CreateDynamicJSResources(true)", 200);
1104 } 1105} 1106 1107//Loads scripts in sequence - in order to guarantee scripts have loaded before calling dependencies in other files 1108function loadScripts(DynamicResources, index) { 1109 1110 if (DynamicResources[index] == null || DynamicResources.length == 0) { 1111 //Completed all script loading 1112 CallDocumentLoaded(); 1113 return false; 1114 } 1115 1116 //If current resource is CSS, move onto next 1117 if (DynamicResources[index].Type != "JS") { 1118 1119 loadScripts(DynamicResources, index + 1); 1120 return false; 1121 } 1122 1123 //If item already exists in memory 1124 if (PreviousPageResources != null && PreviousPageResources.length) { 1125 var exInstance = $.grep(PreviousPageResources, function (n) { 1126 return n.Path == DynamicResources[index].Path; 1127 }); 1128 //If the item already exists, don't add it again 1129 if (exInstance != null && exInstance.length) { 1130 1131 loadScripts(DynamicResources, index + 1); 1132 return false; 1133 } 1134 } 1135 1136 //Create new script element 1137 var script = document.createElement("script") 1138 script.type = "text/javascript"; 1139 script.defer = true; 1140 1141 //Add script load handlers - once current script has loaded, move to next. May seem slower than async loading multiple scripts but ensures we don't call non exisitent functions 1142 if (script.readyState) { //IE 1143 script.onreadystatechange = function () { 1144 if (script.readyState == "loaded" || 1145 script.readyState == "complete") { 1146 script.onreadystatechange = null; 1147 1148 loadScripts(DynamicResources, index + 1); 1149 } 1150 }; 1151 } else { //Others 1152 script.onload = function () { 1153 1154 loadScripts(DynamicResources, index + 1); 1155 }; 1156 } 1157 1158 script.src = DynamicResources[index].Path; 1159 document.getElementsByTagName("head")[0].appendChild(script); 1160} 1161 1162//Call this once all scripts have loaded 1163function CallDocumentLoaded(ignoreScriptCallBack) { 1164 1165 //Implement this function if we need to fire scripts as soon as they are loaded 1166 //This may be during a page transition - if the scripts cause performance issues during 1167 //a page transition, wait until the transition is finished i.e. implement PageVisibleCallback() instead 1168 if (!ignoreScriptCallBack) { 1169 if (typeof ScriptsLoadedCallback === 'function') { 1170 ScriptsLoadedCallback(); 1171 ScriptsLoadedCallback = null; 1172 } 1173 } 1174 1175 //If page is transitioning, wait until that finishes 1176 if (isPageTransitioning) { 1177 window.setTimeout("CallDocumentLoaded(true)", 100); 1178 return false; 1179 } 1180 1181 try { 1182 PageVisibleCallback(); 1183 PageVisibleCallback = null; 1184 } 1185 catch (ex) { 1186 //ignore - callback method not implemented 1187 } 1188 1189 AddMouseEarHoverStates(); 1190 1191 if (ThirdPartiesEnabled == true && AddThisEnabled == true) { 1192 updateSocialMediaButtons(window.location.pathname); 1193 } 1194 1195 $("#PageLoadBlock").remove(); 1196 if (!Next.Settings.UI.PlpDesktopJumpinessFix) { 1197 setPagePositioning(); 1198 } 1199 1200 // reset the PagePopped flag here as the page visible callback might need it and this is the latest we can reset it 1201 PagePopped = false; 1202 PagePoppedData = null; 1203 1204 var paymentCompletePageUrl = "/secure/checkout/complete"; //Payment complete page should redirect to "Next home page" 1205 if (!PageURLInput.match(paymentCompletePageUrl + "$")) { 1206 if ($.jStorage.storageAvailable()) { 1207 var lastPageVisited = $.jStorage.get("LastPageVisited"); 1208 if (typeof lastPageVisited !== 'undefined' && lastPageVisited != '') { 1209 $('ul#steps a.lastPageVisited').attr('href', lastPageVisited); 1210 } 1211 } 1212 } 1213} 1214 1215function RemoveDynamicResources() { 1216 var $ = jQuery; 1217 if ((PreviousPageResources != undefined) && (PreviousPageResources.length > 0)) { 1218 var DRObject = PreviousPageResources.shift(); 1219 1220 //Check this item isn't required by current page (i.e. added to dynamic resources) 1221 if (DynamicResources != null && DynamicResources.length) { 1222 var exInstance = $.grep(DynamicResources, function (n) { 1223 return n.Path == DRObject.Path; 1224 }); 1225 //If the item we're about to remove from DOM exists in the new DynamicResources, don't remove it 1226 if (exInstance != null && exInstance.length) { 1227 RemoveDynamicResources(); 1228 return false; 1229 } 1230 } 1231 1232 switch (DRObject.Type) { 1233 case "JS": 1234 $($('script[src="' + DRObject.Path + '"]')[0]).remove(); 1235 break; 1236 case "CSS": 1237 $($('link[href="' + DRObject.Path + '"]')[0]).remove(); 1238 break; 1239 } 1240 RemoveDynamicResources(); 1241 } else { 1242 isPageTransitioning = false; 1243 } 1244} 1245 1246/* - Animation Stack Handlers - */ 1247function createAnimation() { 1248 var AnimationStack = new Array(); 1249 return AnimationStack; 1250} 1251 1252function addToAnimation(AnimationStack, CommandText, Duration, WaitForComplete) { 1253 var StackObject 1254 if (WaitForComplete) { 1255 StackObject = { Command: CommandText, NextActionDelay: Duration + 10 }; 1256 } else { 1257 StackObject = { Command: CommandText } 1258 } 1259 AnimationStack.push(StackObject); 1260} 1261 1262function runAnimation(stack) { 1263 if (stack.length > 0) { 1264 StackObject = stack.shift(); 1265 eval(StackObject.Command); 1266 if (StackObject.NextActionDelay != null && StackObject.NextActionDelay != 0) { 1267 var timer = window.setTimeout(function () { runAnimation(stack) }, StackObject.NextActionDelay) 1268 } else { 1269 runAnimation(stack); 1270 } 1271 } 1272} 1273 1274/* - Document State Management - */ 1275function getDocStateParamValue(Name) { 1276 if (documentState[Name] !== undefined) 1277 return documentState[Name]; 1278 else 1279 return null; 1280} 1281 1282function addParamToDocState(Name, Value) { 1283 if (documentState[Name] !== undefined) { 1284 documentState[Name] = Value; 1285 } 1286 else { 1287 documentState[Name] = Value; 1288 } 1289 1290 if (window.history.replaceState) { 1291 window.history.replaceState({ "url": window.location.href, "DocState": documentState, "title": document.title, "scrollPoint": $(window).scrollTop() }, document.title, window.location.href); // On load, attempt to replace the last state object on the history stack with one that is populated with the relevent transition data (although in all likely hood this will just insert a new item) 1292 } 1293} 1294 1295function getDocStateString(DocState) { 1296 var docStateString = "" 1297 var hashTag = "" 1298 if (typeof DocState != "undefined" && DocState != null) { 1299 // If it is already a string, just append it 1300 if (typeof DocState === "string") 1301 docStateString = DocState; 1302 // If it is a JSON object, parse it 1303 else if (typeof DocState === "object") { 1304 jQuery.each(DocState, function (varName, varVal) { 1305 if (varName === 'hashtag') 1306 hashTag = '#' + varVal; 1307 // If this is not a hidden variable, then add it to the docstate str
1307ing 1308 else if (varName.indexOf('hidden') !== 0) 1309 docStateString += '&' + varName + '=' + varVal; 1310 }); 1311 } 1312 } 1313 1314 return docStateString + hashTag; 1315} 1316 1317/* - Page Transitions - */ 1318/* - Context Handlers - */ 1319function getVTransitionContext(url) { 1320 url = String(url); 1321 var newVTransitionContext 1322 if (url == "/") { 1323 newVTransitionContext = 0; 1324 } else { 1325 newVTransitionContext = url.split(/\//g).length - 1; 1326 } 1327 return newVTransitionContext; 1328} 1329 1330function getHTransitionContext(url) { 1331 url = String(url); 1332 var newHTransitionContext 1333 if (url == "/") { 1334 newHTransitionContext = -1; 1335 } else { 1336 if (url.indexOf("/") != -1) { 1337 var urlSeg = url.split(/\//g); 1338 newHTransitionContext = HContextArray.indexOf(urlSeg[1]) 1339 } else { 1340 newHTransitionContext = 1 1341 } 1342 } 1343 return newHTransitionContext; 1344} 1345 1346/* - Layout Size Handlers - */ 1347function resizeContentArea() { 1348 return false; 1349} 1350 1351function ResizeContentAreaToContentV2() { 1352 return false; 1353} 1354 1355/* - CSS Dynamic Content Handlers - */ 1356function scriptEnabledValidation() { 1357 var $ = jQuery; 1358 // If JS is enabled, we want to add a class to the body so that we can show/hide content in our CSS 1359 $('body').addClass('JSEnabled'); 1360 //Also add browser class to body tag 1361 1362 var browserName; 1363 1364 if (jqueryUpgradeEnabledOnPage) { 1365 if (typeof (navigator.userAgent) !== 'undefined') {
1366 if (navigator.userAgent.search("webkit")) { browserName = "webkit"; } 1367 if (navigator.userAgent.search("opera")) { browserName = "opera"; } 1368 //ie11 on desktop will have this broser class name 1369 if (navigator.userAgent.search("mozilla")) { browserName = "mozilla"; } 1370 if (navigator.userAgent.search("msie") || !!document.documentMode === true) { 1371 browserName = "msie" + document.documentMode; 1372 } 1373 } 1374 } else { 1375 if (typeof ($.browser) !== 'undefined') 1376 { 1377 if ($.browser.webkit) { browserName = "webkit"; } 1378 if ($.browser.opera) { browserName = "opera"; } 1379 if ($.browser.mozilla) { browserName = "mozilla"; }//ie11 on desktop will have this broser class name 1380 if ($.browser.msie) { browserName = "msie" + $.browser.version.split('.')[0]; } 1381 } 1382 } 1383 1384 if (navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0) {//this is for IE touch device 1385 //browserName = "msie" + $.browser.version.split('.')[0]; 1386 browserName = "ie11TouchDevice"; 1387 } 1388 if (!(window.ActiveXObject) && "ActiveXObject" in window) { 1389 browserName = "msie11"; 1390 } 1391 1392 $('body').addClass(browserName); 1393} 1394 1395scriptEnabledValidation(); 1396 1397/* - Page Callback Handlers - */ 1398 1399function PageTransitionComplete() { 1400 try { 1401 PageVisibleCallback(); 1402 PageVisibleCallback = null; 1403 } 1404 catch (ex) { 1405 //ignore - callback method not implemented 1406 } 1407} 1408 1409/* - Mouse Ear Logic - */ 1410function ShowMouseEarHover(params, forcePopUp) { 1411 1412 if (isIpad) { 1413 RemoveMouseEarHover(params.element); 1414 return false; 1415 } 1416 1417 var $ = jQuery; 1418 if (!params.img) { 1419 1420 RemoveMouseEarHover(params.element); 1421 return false; 1422 1423 } 1424 1425 var mouseEar = params.element; 1426 1427 //If mouse ear already has an associated pop up image, return false 1428 if ($(mouseEar).find(".MouseEarHoverPopUp").length) { 1429 return false; 1430 } 1431 1432 $(mouseEar).addClass("hasPopUp"); 1433 1434 //Create wrapper element 1435 var hoverEl = $("<div />"); 1436 $(hoverEl).addClass("MouseEarHoverPopUp"); 1437 1438 if (params.title) { 1439 $(hoverEl).append($("<h5>").text(params.title)); 1440 } 1441 1442 //Create pointer arrow element 1443 //Create pointer arrow element for older browsers which don't support psuedo elements (:before/:after) 1444 if ($("body").hasClass("msie8")) { 1445 var arrow = $("<div />"); 1446 $(arrow).addClass("MouseEarHoverArrow"); 1447 $(hoverEl).append(arrow); 1448 } 1449 1450 //Create image shadow inset wrapper 1451 var imgWrap = $("<div />"); 1452 $(imgWrap).addClass("MouseEarImg"); 1453 1454 //Create image path 1455 var imgPath = cdnURL + "/common/items/default/default/sitemaps/" + params.sitemap + params.img; 1456 //Load the image - make sure the image is loaded before animating into view 1457 var img = $("<img/>").attr('src', imgPath).load(function () { 1458 1459 $(imgWrap).append(img); 1460 1461 //Append image to wrapper element 1462 $(hoverEl).append(imgWrap); 1463 1464 //Create text node 1465 var textNode = $("<h5 />"); 1466 var elName = params.name; 1467 $(textNode).html(elName); 1468 $(hoverEl).append(textNode); 1469 1470 //Hide element 1471 $(hoverEl).css("opacity", 0); 1472 //Add it to the page 1473 $(mouseEar).append(hoverEl); 1474 1475 //Animate element into view based on which mouse ear it is related to 1476 if ($(mouseEar).hasClass("Right")) { 1477 1478 var animations = { 1479 opacity: 1 1480 }; 1481 animations[Next.Settings.CSS.right] = "-=" + 20; 1482 1483 $(hoverEl).animate(animations, 300, 'easeOutBack'); 1484 } 1485 1486 if ($(mouseEar).hasClass("Left")) { 1487 1488 var animations = { 1489 opacity: 1 1490 }; 1491 animations[Next.Settings.CSS.left] = "-=" + 20; 1492 1493 $(hoverEl).animate(animations, 300, 'easeOutBack'); 1494 } 1495 1496 }); 1497} 1498 1499//Change contents of open pop-up as user pages while mouse is still over the mouseEar 1500function UpdateMouseEarHover(params) { 1501 var $ = jQuery; 1502 var mouseEar = params.element; 1503 1504 if (!params.img) { 1505 RemoveMouseEarHover(mouseEar); 1506 return false; 1507 } 1508 1509 var popUp = $(mouseEar).find(".MouseEarHoverPopUp"); 1510 1511 if (popUp.length == 0) { 1512 ShowMouseEarHover(params, true); 1513 return false; 1514 } 1515 1516 //Create image path 1517 var imgPath = cdnURL + "/common/items/default/default/sitemaps/" + params.sitemap + params.img; 1518 1519 $(popUp).find("h5").empty(); 1520 $(popUp).find("h5").html(params.name); 1521 1522 //Load the image 1523 var img = $("<img/>").attr('src', imgPath).load(function () { 1524 $(popUp).find(".MouseEarImgShadow img").first().replaceWith(img); 1525 });
1526} 1527 1528function RemoveMouseEarHover(mouseEar) { 1529 1530 var $ = jQuery; 1531 //Animate element away from parent mouse ear based on position of mouse ear then remove from DOM 1532 if ($(mouseEar).hasClass("Right")) { 1533 1534 var animations = { 1535 opacity: 0 1536 }; 1537 animations[Next.Settings.CSS.right] = "+=" + 20; 1538 1539 $(mouseEar).find(".MouseEarHoverPopUp") 1540 .animate(animations, 300, 'easeInBack', function () { 1541 $(mouseEar).removeClass("hasPopUp"); 1542 $(this).remove(); 1543 }); 1544 } 1545 1546 if ($(mouseEar).hasClass("Left")) { 1547 1548 var animations = { 1549 opacity: 0 1550 }; 1551 animations[Next.Settings.CSS.left] = "+=" + 20; 1552 1553 $(mouseEar).find(".MouseEarHoverPopUp") 1554 .animate(animations, 300, 'easeInBack', function () { 1555 $(mouseEar).removeClass("hasPopUp"); 1556 $(this).remove(); 1557 }); 1558 } 1559} 1560 1561function ClearMouseEarStates() { 1562 1563 $("div.MouseEar").each(function () { 1564 $(this).unbind('mouseenter').unbind('mouseleave'); 1565 }); 1566} 1567 1568//Limit mouse ear position to 1280 width - anything beyoned that, the mouse ears will be fixed 1569function positionMouseEars() { 1570 var $ = jQuery; 1571 if ($(window).width() > 1280) { 1572 $("body").addClass("LimitWidth"); 1573 1574 } else { 1575 $("body").removeClass("LimitWidth"); 1576 } 1577} 1578 1579//Add hover states for spread mouse ears 1580function AddSpreadHoverStates() { 1581 1582 var $ = jQuery; 1583 1584 //Clear out any mouse ears from previous pages 1585 $(".MouseEar.Right.Proxy").remove(); 1586 $(".MouseEar.Left.Proxy").remove(); 1587 1588 var $mouseEarRight = $(".MouseEar.Right"); 1589 var $mouseEarLeft = $(".MouseEar.Left"); 1590 1591 //Make new spread mouse ears visible 1592 $mouseEarRight.addClass("visible"); 1593 $mouseEarLeft.addClass("visible"); 1594 //Unbind any existing hover event handlers 1595 $mouseEarRight.off('hover'); 1596 $mouseEarLeft.off('hover'); 1597 //If Right mouse ear has a next page image preview, build hover state 1598 if ($mouseEarRight.attr("data-img").length) { 1599 1600 var paramsRight = { 1601 element: $mouseEarRight, 1602 img: $mouseEarRight.attr("data-img"), 1603 sitemap: $mouseEarRight.attr("data-sitemap"), 1604 name: $mouseEarRight.attr("data-name"), 1605 title: "Coming Next" 1606 }; 1607 //Create mouseEar hover event handler 1608 $mouseEarRight.hover(function () { ShowMouseEarHover(paramsRight); }, function () { RemoveMouseEarHover($mouseEarRight); }); 1609 //And close pop up on click 1610 $mouseEarRight.on("click", function () { RemoveMouseEarHover($mouseEarRight); }); 1611 } 1612 else if ($mouseEarRight.hasClass("hasPopUp")) 1613 $mouseEarRight.mouseout(function () { RemoveMouseEarHover($mouseEarRight); }) 1614 1615 //Same as above for left mouse ear 1616 if ($mouseEarLeft.attr("data-img").length) { 1617 1618 var paramsLeft = { 1619 element: $mouseEarLeft, 1620 img: $mouseEarLeft.attr("data-img"), 1621 sitemap: $mouseEarLeft.attr("data-sitemap"), 1622 name: $mouseEarLeft.attr("data-name") 1623 }; 1624 1625 $mouseEarLeft.hover(function () { ShowMouseEarHover(paramsLeft); }, function () { RemoveMouseEarHover($mouseEarLeft); }); 1626 $mouseEarLeft.on("click", function () { RemoveMouseEarHover($mouseEarLeft); }); 1627 } 1628 else if ($mouseEarLeft.hasClass("hasPopUp")) 1629 $mouseEarLeft.mouseout(function () { RemoveMouseEarHover($mouseEarLeft); }) 1630 1631 //Move mouse ears (first time round) to outer wrapper to prevent animations overlapping ears 1632 if (!$mouseEarLeft.hasClass("spreadEar")) { 1633 $mouseEarLeft.addClass("spreadEar"); 1634 $(".ContentArea").prepend($mouseEarLeft); 1635 } 1636 1637 if (!$mouseEarRight.hasClass("spreadEar")) { 1638 $mouseEarRight.addClass("spreadEar"); 1639 $(".ContentArea").prepend($mouseEarRight); 1640 } 1641 1642 return false; 1643} 1644 1645//Create hover event handlers for mouse ears 1646function AddMouseEarHoverStates() { 1647 var $ = jQuery; 1648 //If we're in a Spread page, handle mouse ears separately 1649 if ($(".SpreadsPage").length) { 1650 AddSpreadHoverStates(); 1651 return false; 1652 } 1653 1654 //Clean up spread MouseEars 1655 $(".MouseEar.Left.spreadEar").remove(); 1656 $(".MouseEar.Right.spreadEar").remove(); 1657 1658 //Hide incoming/new MouseEars 1659 $(".MouseEar.Right").removeClass("visible"); 1660 $(".MouseEar.Left").removeClass("visible"); 1661 1662 var hasNewLeft = false; 1663 var hasNewRight = false; 1664 var hasLeftProxy = false; 1665 var hasRightProxy = false; 1666 1667 //Check we already have a mouse ear to handle hover/click events 1668 if ($(".ContentArea div.MouseEar.Left.Proxy").length) { 1669 hasLeftProxy = true; 1670 } 1671 1672 if ($(".ContentArea div.MouseEar.Right.Proxy").length) { 1673 hasRightProxy = true; 1674 } 1675 //Check incoming page has a left and right mouse ear 1676 if ($(".CurrentContent div.MouseEar.Left").length) { 1677 hasNewLeft = true; 1678 } else { 1679 //Incoming page has no left mouse ear, remove any exisitng Left mouseEar 1680 $(".ContentArea div.MouseEar.Left.Proxy").remove(); 1681 } 1682 1683 if ($(".CurrentContent div.MouseEar.Right").length) { 1684 hasNewRight = true; 1685 } else { 1686 $(".ContentArea div.MouseEar.Right.Proxy").remove(); 1687 } 1688 //Add hover event handlers for each mouse ear 1689 $(".CurrentContent div.MouseEar").each(function () { 1690 1691 var mouseEar = $(this); 1692 var params = { 1693 element: $(this), 1694 img: $(this).attr("data-img"), 1695 sitemap: $(this).attr("data-sitemap"), 1696 name: $(this).attr("data-name") 1697 }; 1698 1699 if ($(this).hasClass("Left") && hasLeftProxy) { 1700 1701 var leftProxy = $(".ContentArea div.MouseEar.Left.Proxy"); 1702 $(leftProxy).off('hover'); 1703 params.element = leftProxy; 1704 1705 $(leftProxy).attr("data-img", $(this).attr("data-img")); 1706 $(leftProxy).attr("data-sitemap", $(this).attr("data-sitemap")); 1707 $(leftProxy).attr("data-name", $(this).attr("data-name"));
1708 1709 $(leftProxy).hover(function () { ShowMouseEarHover(params); }, function () { RemoveMouseEarHover(leftProxy); }); 1710 1711 $(leftProxy).find("a").attr("href", $(this).find("a").attr("href")); 1712 $(leftProxy).find("a").attr("onclick", $(this).find("a").attr("onclick")); 1713 1714 if ($(this).hasClass("Active")) { 1715 $(leftProxy).removeClass("Inactive"); 1716 $(leftProxy).addClass("Active"); 1717 } 1718 1719 if ($(this).hasClass("Inactive")) { 1720 $(leftProxy).removeClass("Active"); 1721 $(leftProxy).addClass("Inactive"); 1722 } 1723 1724 1725 if ($(leftProxy).hasClass("hasPopUp")) { 1726 UpdateMouseEarHover(params); 1727 } 1728 } 1729 1730 if ($(this).hasClass("Left") && !hasLeftProxy) { 1731 $(this).hover(function () { ShowMouseEarHover(params); }, function () { RemoveMouseEarHover(mouseEar); }); 1732 $(this).addClass("Proxy"); 1733 $(".ContentArea").prepend(this); 1734 } 1735 1736 if ($(this).hasClass("Right") && hasRightProxy) { 1737 1738 var RightProxy = $(".ContentArea div.MouseEar.Right.Proxy"); 1739 $(RightProxy).off('hover'); 1740 params.element = RightProxy; 1741 $(RightProxy).attr("data-img", $(this).attr("data-img")); 1742 $(RightProxy).attr("data-sitemap", $(this).attr("data-sitemap")); 1743 $(RightProxy).attr("data-name", $(this).attr("data-name")); 1744 1745 $(RightProxy).hover(function () { ShowMouseEarHover(params); }, function () { RemoveMouseEarHover(RightProxy); }); 1746 1747 $(RightProxy).find("a").attr("href", $(this).find("a").attr("href")); 1748 $(RightProxy).find("a").attr("onclick", $(this).find("a").attr("onclick")); 1749 1750 1751 if ($(this).hasClass("Active")) { 1752 $(RightProxy).removeClass("Inactive"); 1753 $(RightProxy).addClass("Active"); 1754 } 1755 1756 if ($(this).hasClass("Inactive")) { 1757 $(RightProxy).removeClass("Active"); 1758 $(RightProxy).addClass("Inactive"); 1759 } 1760 1761 1762 if ($(RightProxy).hasClass("hasPopUp")) { 1763 UpdateMouseEarHover(params); 1764 } 1765 } 1766 1767 if ($(this).hasClass("Right") && !hasRightProxy) { 1768 $(this).hover(function () { ShowMouseEarHover(params); }, function () { RemoveMouseEarHover(mouseEar); }); 1769 $(this).addClass("Proxy"); 1770 $(".ContentArea").prepend(this); 1771 } 1772 }); 1773} 1774 1775$(function () { 1776 var $document = $(document), 1777 left = 0, 1778 scrollTimer = 0; 1779 1780 // Detect horizontal scroll start and stop. 1781 $document.on("scroll", function () { 1782 var docLeft = $document.scrollLeft(); 1783 if (left !== docLeft && !isTouchEnabled) { 1784 var self = this, args = arguments; 1785 if (!scrollTimer) { 1786 // We've not yet (re)started the timer: It's the beginning of scrolling. 1787 startHScroll.apply(self, args); 1788 } 1789 window.clearTimeout(scrollTimer); 1790 scrollTimer = window.setTimeout(function () { 1791 scrollTimer = 0; 1792 // Our timer was never stopped: We've finished scrolling. 1793 stopHScroll.apply(self, args); 1794 }, 100); 1795 left = docLeft; 1796 } 1797 }); 1798 1799 // Horizontal scroll started - Make header absolutely positioned. 1800 function startHScroll() { 1801 console.log("Scroll Start"); 1802 $(".PageHeader") 1803 // Clear out any left-positioning set by stopHScroll. 1804 .css("left", "") 1805 .each(function () { 1806 var $this = $(this), 1807 pos = $this.offset(); 1808 // Preserve our current vertical position... 1809 $this.css("top", pos.top) 1810 }) 1811 // ...before making it absolutely positioned. 1812 .css("position", "absolute"); 1813 } 1814 1815 // Horizontal scroll stopped - Make header float again. 1816 function stopHScroll() { 1817 var leftScroll = $(window).scrollLeft(); 1818 console.log("Scroll Stop"); 1819 $(".PageHeader")
1820 // Clear out any top-positioning set by startHScroll. 1821 .css("top", "") 1822 .each(function () { 1823 var $this = $(this), 1824 pos = $this.position(); 1825 // Preserve our current horizontal position, munus the scroll position... 1826 $this.css("left", pos.left - leftScroll); 1827 }) 1828 // ...before making it fixed positioned. 1829 .css("position", "fixed"); 1830 } 1831}); 1832 1833function isTouchDevice() { 1834 var deviceAgent = navigator.userAgent.toLowerCase(); 1835 1836 var isTouch = Modernizr.touch || 1837 (deviceAgent.match(/(iphone|ipod|ipad)/) || 1838 deviceAgent.match(/(android)/) || 1839 deviceAgent.match(/(iemobile)/) || 1840 deviceAgent.match(/iphone/i) || 1841 deviceAgent.match(/ipad/i) || 1842 deviceAgent.match(/ipod/i) || 1843 deviceAgent.match(/blackberry/i) || 1844 deviceAgent.match(/bada/i)); 1845 1846 return isTouch; 1847} 1848 1849function setPagePositioning() { 1850 var $ = jQuery; 1851 1852 if (isTouchEnabled) { 1853 // New header is always fixed 1854 $(".PageHeader").css({ 1855 "position": "fixed", 1856 "width": "100%", 1857 "min-width": "1000px" 1858 }); 1859 1860 1861 $('body').addClass("isTouchDevice"); 1862 } 1863 else { 1864 $(".PageHeader").css({ 1865 "position": "fixed", 1866 "width": "100%", 1867 "min-width": "1000px" 1868 }); 1869 } 1870} 1871 1872/* - Generic Session Variable Methods - */ 1873var ClientTempSessionVariables = {}; // This is a local store of the Session Variables that will save us from going to the Server each time we want to get a value 1874var UseLocalStorage = true; // Config switch to use either Local Storage on the browser or use the Server side Session Storage Objects 1875 1876function UpdateSessionVariable(variableName, newValue, forceServerPost, successCallback, errorCallback) { 1877 1878 if (UseLocalStorage && !forceServerPost) { 1879 $.jStorage.set(variableName, newValue); 1880 1881 if (successCallback) 1882 successCallback(); 1883 } 1884 else { 1885 if ((forceServerPost !== null && forceServerPost !== undefined && forceServerPost) || ClientTempSessionVariables === null || ClientTempSessionVariables === undefined || ClientTempSessionVariables[variableName] === null || ClientTempSessionVariables[variableName] === undefined || (ClientTempSessionVariables[variableName] !== newValue)) { 1886 // Build the URL used to set Session Variables 1887 var reqUrl = '' 1888 if (typeof baseURL != 'undefined') 1889 reqUrl = baseURL; 1890 reqUrl += "/session/AddVariable"; 1891 1892 // if a json object is passed as newValue then convert to string and post to the session controller 1893 if (typeof newValue == "object") {newValue = JSON.stringify(newValue)} 1894 1895 // Send the request 1896 $.ajax({
1897 url: reqUrl, 1898 data: { name: variableName, value: newValue }, 1899 type: "POST", 1900 cache: false, 1901 async: true, 1902 timeout: 10000, 1903 dataType: "json", 1904 xhrFields: { withCredentials: true }, 1905 crossDomain: true, 1906 success: function (data, textStatus, jqXHR) { 1907 checkAndRedirectForAjaxError(jqXHR, false, 'Global.js: UpdateSessionVariable'); 1908 // Update the temporary client-side session variables 1909 ClientTempSessionVariables = data 1910 1911 if (successCallback) 1912 successCallback(data, textStatus, jqXHR); 1913 }, 1914 error: function (jqXHR, textStatus, errorThrown) { 1915 if (errorCallback) 1916 errorCallback(jqXHR, textStatus, errorThrown); 1917 } 1918 }); 1919 } 1920 } 1921} 1922 1923function GetSessionVariable(variableName, forceServerGet, successCallback, errorCallback) { 1924 if (UseLocalStorage) { 1925 if (successCallback) 1926 successCallback($.jStorage.get(variableName)); 1927 else 1928 return $.jStorage.get(variableName); 1929 } 1930 else { 1931 // If we need to force a refresh or the variable doesn't exist in the jQuery temp ClientTempSessionVariables store, then refresh it 1932 if (forceServerGet || ClientTempSessionVariables === null || ClientTempSessionVariables === undefined || ClientTempSessionVariables[variableName] === null || ClientTempSessionVariables[variableName] === undefined) { 1933 var reqUrl = '' 1934 if (typeof baseURL != 'undefined') 1935 reqUrl = baseURL; 1936 reqUrl += "/session/GetVariables"; 1937 1938 $.ajax({
1939 url: reqUrl, 1940 type: "POST", 1941 cache: false, 1942 async: true, 1943 timeout: 5000, 1944 dataType: "json", 1945 xhrFields: { withCredentials: true }, 1946 crossDomain: true, 1947 success: function (data, textStatus, jqXHR) { 1948 checkAndRedirectForAjaxError(jqXHR, false, 'Global.js: GetSessionVariable'); 1949 ClientTempSessionVariables = data; 1950 // Return the value if it exists 1951 if (ClientTempSessionVariables !== null && ClientTempSessionVariables !== undefined && ClientTempSessionVariables[variableName] !== undefined) { // Allow this to return a null value as it is valid 1952 if (successCallback) 1953 successCallback(ClientTempSessionVariables[variableName], data, textStatus, jqXHR); 1954 } 1955 else { 1956 if (errorCallback) 1957 errorCallback(jqXHR, textStatus); 1958 } 1959 }, 1960 error: function (jqXHR, textStatus, errorThrown) { 1961 if (errorCallback) 1962 errorCallback(jqXHR, textStatus, errorThrown); 1963 } 1964 }); 1965 } 1966 else { 1967 // Return the value if it exists 1968 if (successCallback) 1969 successCallback(ClientTempSessionVariables[variableName]); 1970 } 1971 } 1972} 1973 1974/* - Continue Shopping Methods - */ 1975function UpdateContinueShopping(shoppableUrl) { 1976 var newValue; 1977 1978 if (typeof shoppableUrl != "undefined") 1979 newValue = shoppableUrl; 1980 else 1981 newValue = window.location.href; 1982 1983 // Check the current value and update it if the values are different 1984 GetSessionVariable( 1985 "ContinueShopping", 1986 false, 1987 function (UrlReturned) { // SUCCESS CALLBACK 1988 var allowUpdate = true; 1989 1990 // Don't bother re-adding it if it was the last one to be added 1991 if (UrlReturned != null && UrlReturned != undefined && UrlReturned == newValue) { 1992 allowUpdate = false; 1993 } 1994 1995 if (allowUpdate) { 1996 UpdateContinueShoppingAndShoppingHistory(newValue); 1997 } 1998 }, 1999 function () { // ERROR CALLBACK 2000 // Just update it anyway on error (there is an issue with the Get, so Setting it is safer than ignoring it) 2001 UpdateContinueShoppingAndShoppingHistory(newValue); 2002 } 2003 ); 2004} 2005 2006function UpdateContinueShoppingAndShoppingHistory(newValue) { 2007 UpdateSessionVariable('ContinueShopping', newValue); 2008 2009 // If we are using LocalStorage, then we need to manually update the ShoppingHistory too 2010 // Note this is replicating server-side logic that is in place in the non-LocalStorage version of this method 2011 if (UseLocalStorage) { 2012 GetSessionVariable( 2013 "ShoppingHistory", 2014 false, 2015 function (returnedHistory) { 2016 var tempHistory = []; 2017 if (returnedHistory !== null) { 2018 tempHistory = returnedHistory; 2019 } 2020 tempHistory.push(newValue); 2021 2022 // We only want to keep a small history, so get rid of the first item 2023 if (tempHistory.length > 2) 2024 tempHistory.shift(); 2025 2026 UpdateSessionVariable("ShoppingHistory", tempHistory); 2027 } 2028 ); 2029 } 2030 2031} 2032 2033function ContinueShopping(boolSuppressTransition) { 2034 2035 // Try to get hold of the Continue Shopping Session Variable, if it doesn't exist, then take the user to the homepage 2036 2037 var suppressTransition = true; 2038 if (boolSuppressTransition != undefined) 2039 suppressTransition = boolSuppressTransition; 2040 2041 // Default to the Base Url if it is available, otherwise default it to the root 2042 var urlToLoad = '/'; 2043 if (typeof siteBaseUrl != 'undefined') { 2044 urlToLoad = siteBaseUrl; 2045 } 2046 2047 GetSessionVariable( 2048 "ContinueShopping", 2049 false, 2050 function (UrlReturned) { // SUCCESS CALLBACK 2051 2052 if (UrlReturned != null && UrlReturned != undefined) 2053 urlToLoad = UrlReturned; 2054 LoadContinueShoppingUrl(urlToLoad, suppressTransition) 2055 }, 2056 function () { // ERROR CALLBACK 2057 LoadContinueShoppingUrl(urlToLoad, suppressTransition) 2058 } 2059 ); 2060} 2061 2062function LoadContinueShoppingUrl(urlToLoad, suppressTransition) { 2063 // Check whether the path is going to load up in HTTPS and force it to load in HTTP 2064 if (urlToLoad.indexOf('http:') < 0) { 2065 // First check whether this is an absolute path in HTTPS 2066 if (urlToLoad.indexOf('https:') == 0) { 2067 if (typeof siteBaseUrl != 'undefined') { 2068 var fullUrlToLoad = new UrlHelper(urlToLoad); 2069 urlToLoad = siteBaseUrl + fullUrlToLoad.Path() + fullUrlToLoad.Query() + fullUrlToLoad.Hash(); 2070 } 2071 else 2072 urlToLoad = 'http' + urlToLoad.substring(5); 2073 } 2074 // Otherwise, it must be a relative path, so check if we are in HTTPS and change it accordingly 2075 else if (window.location.protocol === 'https:') { 2076 if (typeof siteBaseUrl != 'undefined') 2077 urlToLoad = siteBaseUrl + urlToLoad; 2078 else { 2079 var fullUrlToLoad = new UrlHelper(urlToLoad); 2080 fullUrlToLoad.Protocol('http:'); 2081 urlToLoad = fullUrlToLoad.FullUrl(); 2082 } 2083 } 2084 } 2085 2086 if (NextBasket 2087 && NextBasket.Data
2088 && NextBasket.Data.FromLabelWebsite) { 2089 urlToLoad = "/transfer/out/label"; 2090 } 2091 2092 if (suppressTransition) 2093 window.location = urlToLoad; 2094 else 2095 LoadHTMLContent(urlToLoad); 2096} 2097 2098function ShopMore() { 2099 ContinueShopping(true); 2100 return false; 2101} 2102 2103 2104/* - General Helpers - */ 2105function UrlHelper(urlToProcess) { 2106 this._protocol = '', 2107 this._host = '', 2108 this._port = '', 2109 this._path = '', 2110 this._query = '', 2111 this._hash = ''; 2112 this._rawUrl = ''; 2113 2114 if (urlToProcess != undefined) { 2115 var urlLeftToProcess = urlToProcess; 2116 var indexTemp = 0; 2117 2118 this._rawUrl = urlLeftToProcess; 2119 2120 // Protocol & Host 2121 2122 if (urlLeftToProcess.substring(0, 4) == 'http') { 2123 var slashSplit = urlLeftToProcess.split('/'); 2124 this._protocol = slashSplit[0]; 2125 this._host = slashSplit[2].split(':')[0].split('?')[0].split('#')[0]; 2126 if (slashSplit[2].split(':').length > 1) 2127 this._port = slashSplit[2].split(':')[1].split('?')[0].split('#')[0]; 2128 urlLeftToProcess = urlLeftToProcess.substring(this._protocol.length + 2 + this._host.length + 1 + this._port.length); 2129 } 2130 2131 if (urlLeftToProcess.substring(0, 1) == '/') 2132 urlLeftToProcess = urlLeftToProcess.substring(1); 2133 2134 // Path 2135 2136 indexTemp = urlLeftToProcess.indexOf('?'); 2137 if (indexTemp > 0) { 2138 this._path = urlLeftToProcess.substring(0, indexTemp); 2139 urlLeftToProcess = urlLeftToProcess.substring(indexTemp); 2140 } 2141 else { 2142 indexTemp = urlLeftToProcess.indexOf('#'); 2143 if (indexTemp > 0) { 2144 this._path = urlLeftToProcess.substring(0, indexTemp); 2145 urlLeftToProcess = urlLeftToProcess.substring(indexTemp); 2146 } 2147 else { 2148 this._path = urlLeftToProcess; 2149 urlLeftToProcess = ''; 2150 } 2151 } 2152 2153 if (urlLeftToProcess.substring(0, 1) == '?') 2154 urlLeftToProcess = urlLeftToProcess.substring(1); 2155 2156 // Query & Hash 2157 2158 indexTemp = urlLeftToProcess.indexOf('#'); 2159 if (indexTemp > 0) { 2160 this._query = urlLeftToProcess.substring(0, indexTemp); 2161 urlLeftToProcess = urlLeftToProcess.substring(indexTemp); 2162 if (urlLeftToProcess.substring(0, 1) == '#') 2163 this._hash = urlLeftToProcess.substring(1); 2164 urlLeftToProcess = ''; 2165 } 2166 else { 2167 this._query = urlLeftToProcess; 2168 urlLeftToProcess = ''; 2169 } 2170 2171 2172 } 2173 2174 this.Protocol = function (newval) { 2175 if (newval == undefined) { 2176 if (this._protocol == '') 2177 return window.location.protocol + '//'; 2178 else 2179 return this._protocol + '//'; 2180 } 2181 else { 2182 this._protocol = newval; 2183 } 2184 } 2185 2186 this.Host = function (newval) { 2187 if (newval == undefined) { 2188 if (this._host == '') 2189 return window.location.hostname; 2190 else 2191 return this._host; 2192 } 2193 else { 2194 this._host = newval; 2195 } 2196 } 2197 2198 this.Port = function (newval) { 2199 if (newval == undefined) { 2200 if (this._port == '') { 2201 if (window.location.port == '') 2202 return ''; 2203 else 2204 return ':' + window.location.port; 2205 } 2206 else 2207 return ':' + this._port; 2208 } 2209 else { 2210 this._port = newval; 2211 } 2212 } 2213 2214 this.Path = function (newval) { 2215 if (newval == undefined) { 2216 if (this._path == '') 2217 return ''; 2218 else 2219 return '/' + this._path; 2220 } 2221 else { 2222 this._path = newval; 2223 } 2224 } 2225 2226 this.Query = function (newval) { 2227 if (newval == undefined) { 2228 if (this._query == '') 2229 return ''; 2230 else 2231 return '?' + this._query; 2232 } 2233 else { 2234 this._query = newval; 2235 } 2236 } 2237 2238 this.Hash = function (newval) { 2239 if (newval == undefined) { 2240 if (this._hash == '') 2241 return ''; 2242 else 2243 return '#' + this._hash; 2244 } 2245 else { 2246 this._hash = newval; 2247 } 2248 } 2249 2250 this.FullUrl = function () { 2251 return this.Protocol() + this.Host() + this.Port() + this.Path() + this.Query() + this.Hash(); 2252 } 2253 2254 this.ServerUrl = function () { 2255 return this.Protocol() + this.Host() + this.Port() + this.Path() + this.Query(); 2256 } 2257 2258 this.AddQueryParam = function (name, value) { 2259 this._query += '&' + name + '=' + value; 2260 } 2261} 2262 2263function GetQueryStringValue(name) { 2264 name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
2265 var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), 2266 results = regex.exec(location.search); 2267 return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 2268} 2269 2270function ConsoleLog(obj) { 2271 if (window.console && console.log) { 2272 console.log(obj); 2273 } 2274} 2275 2276function ScrollToPoint(scrollPoint) { 2277 if (scrollPoint != 'undefined' && scrollPoint != null && (parseInt(scrollPoint) > 100)) 2278 $('html, body').animate({ scrollTop: scrollPoint }, 400); 2279} 2280 2281// Check's if an 500 error has occurred during an AJAX call and if so to track it and redirect the user to a friendly error page 2282function checkAndRedirectForAjaxError(jqXHR, initGaAccount, label) { 2283 if (jqXHR.getResponseHeader('X-Error500Initiated') == 'True') { 2284 if (initGaAccount == true) { 2285 GoogleAnalytics(GAAccount); 2286 } 2287 TrackGAEvent('500 AJAX - Internal Server Error', jqXHR.getResponseHeader('X-Error500Message'), label); 2288 2289 var RedirectError = false; 2290 2291 if (RedirectError) { 2292 // Redirect to the error page if we are not on an error page 2293 if (window.location.href.indexOf('error') === -1 || isErrorPage == false) { 2294 window.location = '/error'; 2295 } 2296 } 2297 else { 2298 2299 var errorDom = $(jqXHR.responseText); 2300 $('.CurrentContent').html(errorDom.find('.CurrentContent').html()).addClass('dynamicLoadError'); 2301 2302 // Execute the JavaScript in the the Error Page response 2303 errorDom.filter('script').each(function () { 2304 if (this.className != "monetate-tag") { 2305 $.globalEval(this.text || this.textContent || this.innerHTML || ''); 2306 } 2307 }); 2308 2309 resizeContentArea(); 2310 } 2311 } 2312} 2313
2314function SetupFallbackForChannelisedLegalWarningLogos() { 2315 $(".channelisedLegalWarningLogo").on('error', function () { 2316 2317 if (Next.Settings.Channel.CountryCode === "GB") { 2318 $(this).closest('p').remove(); 2319 } 2320 else { 2321 var nextSpacerRow = $(this).closest('tr').next('tr.lwTableSpacingRow'); 2322 var prevSpacerRow = $(this).closest('tr').prev('tr.lwTableSpacingRow'); 2323 var lwTable = $(this).closest('table'); 2324 var lwTableRowCount = lwTable.find('tr').length; 2325 2326 if (lwTableRowCount === 1) { 2327 lwTable.remove(); 2328 } else { 2329 if (nextSpacerRow.html()) { 2330 nextSpacerRow.remove(); 2331 } else if (prevSpacerRow.html()) { 2332 prevSpacerRow.remove(); 2333 } 2334 2335 $(this).closest('tr').remove(); 2336 } 2337 } 2338 $(this).unbind('error'); 2339 }); 2340 2341 2342 if (jqueryUpgradeEnabledOnPage) { 2343 $(".channelisedLegalWarningLogo").on("load", function () { 2344 $(".legalWarningLogos").show(); 2345 }); 2346 2347 } else { 2348 $(".channelisedLegalWarningLogo").load(function () { 2349 $(".legalWarningLogos").show(); 2350 }); 2351 2352 } 2353} 2354 2355function AddLegalWarningLogoImage(styleCopy, url, imageName, imageDesc) { 2356 2357 var $legalWarningLogos = styleCopy.find("#legalWarningLogos"); 2358 2359 // check to see if the image has already been added... 2360 var $img = $legalWarningLogos.find("img[src$='" + url + "'][class='channelisedLegalWarningLogo']"); 2361 2362 if ($img.length === 0) { 2363 $legalWarningLogos.append("<p><img class=\"channelisedLegalWarningLogo\" src='" + url + "' alt='" + imageName + "' />" + imageDesc + "</p>"); 2364 } 2365} 2366
2367function SetupFallbackForChannelisedLogos() { 2368 $(".channelisedLogo").on('error', function () { 2369 $(this).closest('tr').remove(); 2370 $(this).unbind('error'); 2371 }); 2372 2373 $(".channelisedLogo").on("load", function () { 2374 $(".logos").show(); 2375 }); 2376} 2377 2378function AddLogoImage(styleCopy, URL, imageName, imageDesc) { 2379 2380 $logos = styleCopy.find("#logos"); 2381 $logosTable = $logos.find("table"); 2382 2383 // check to see if the image has already been added... 2384 $img = $logosTable.find("img[src$='" + URL + "'][class='channelisedLogo']"); 2385 2386 if ($img.length == 0) { 2387 $logosTable.append("<tr><td class='img'><img class=\"channelisedLogo\" src='" + URL + "' alt='" + imageName + "' /></td><td><div class='description'>" + imageDesc + "</div></td></tr>"); 2388 } 2389} 2390 2391function LoadGomezScripts() { 2392 if (GomezEnabled) { 2393 // Create a gomez variable that will be used once the script has loaded 2394 if (GomezPageTagEnabled && pageLoadStart && GomezPageId) { 2395 gomez = { gs: pageLoadStart, acctId: 'FDA173', pgId: GomezPageId, grpId: 'Vision' }; 2396 if (typeof (GomezGroupId2) == 'string') { 2397 gomez.grpId2 = GomezGroupId2; 2398 } 2399 } 2400 2401 // UNCOMMENT BELOW FOR GOMEZ DEBUG LOGGING 2402 /* 2403 // Log whatever detail we have 2404 ConsoleLog("Loading Gomez Tag..."); 2405 ConsoleLog(gomez); 2406 2407 // Log the time to load so far 2408 var timeToLoad = new Date().getTime(); 2409 timeToLoad = timeToLoad - pageLoadStart; 2410 ConsoleLog("Time To Load So Far: " + timeToLoad) 2411 */ 2412 2413 // Append the gomez script to the page - if we do this any earlier than the footer or doc ready, then we may not have enough params from the page to load the tag 2414 var docHeadID = document.getElementsByTagName("head")[0]; 2415 var newGomezScript = document.createElement('script'); 2416 newGomezScript.type = 'text/javascript'; 2417 newGomezScript.src = siteAssetsUrl + '/Scripts/Gomez.js?v1'; 2418 docHeadID.appendChild(newGomezScript); 2419 } 2420} 2421 2422function GoBackOrClose() { 2423 var canGoBack = false; 2424 var ua = navigator.userAgent || navigator.vendor || window.opera; 2425 2426 if (Next.Settings.UI.IsSocialPLPEnabled && ((ua.indexOf("FBAN") > -1) || (ua.indexOf("FBAV") > -1))) { 2427 if (window.location.href.indexOf('frm_sclp') > -1) { 2428 var __sclPlpUrl = window.location.href.split('#')[0] + '?frm_sclp'; 2429 location.replace(__sclPlpUrl); 2430 2431 return; 2432 } 2433 } 2434 2435 $(window).on('beforeunload', function () { 2436 canGoBack = true; 2437 }); 2438 2439 window.history.back(); 2440 2441 window.setTimeout(function () { 2442 if (!canGoBack) { window.close(); } 2443 }, 200); 2444 2445} 2446 2447/* 2448* ----------------------------- JSTORAGE ------------------------------------- 2449* Simple local storage wrapper to save data on the browser side, supporting 2450* all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+ 2451* 2452* Copyright (c) 2010 Andris Reinman, [email protected] 2453* Project homepage: www.jstorage.info 2454* 2455* Licensed under MIT-style license: 2456* 2457* Permission is hereby granted, free of charge, to any person obtaining a copy 2458* of this software and associated documentation files (the "Software"), to deal 2459* in the Software without restriction, including without limitation the rights 2460* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 2461* copies of the Software, and to permit persons to whom the Software is 2462* furnished to do so, subject to the following conditions: 2463* 2464* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2465* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2466* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 2467* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2468* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2469* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 2470* SOFTWARE. 2471*/ 2472 2473/** 2474* $.jStorage 2475* 2476* USAGE: 2477* 2478* jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then 2479* jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed. 2480* (jQuery-JSON needs to be loaded BEFORE jStorage!) 2481* 2482* Methods: 2483* 2484* -set(key, value[, options]) 2485* $.jStorage.set(key, value) -> saves a value 2486* 2487* -get(key[, default]) 2488* value = $.jStorage.get(key [, default]) -> 2489* retrieves value if key exists, or default if it doesn't 2490*
2491* -deleteKey(key) 2492* $.jStorage.deleteKey(key) -> removes a key from the storage 2493* 2494* -flush() 2495* $.jStorage.flush() -> clears the cache 2496* 2497* -storageObj() 2498* $.jStorage.storageObj() -> returns a read-ony copy of the actual storage 2499* 2500* -storageSize() 2501* $.jStorage.storageSize() -> returns the size of the storage in bytes 2502* 2503* -index() 2504* $.jStorage.index() -> returns the used keys as an array 2505* 2506* -storageAvailable() 2507* $.jStorage.storageAvailable() -> returns true if storage is available 2508* 2509* -reInit() 2510* $.jStorage.reInit() -> reloads the data from browser storage 2511* 2512* <value> can be any JSON-able value, including objects and arrays. 2513* 2514**/ 2515 2516(function ($) { 2517 if (!$ || !($.toJSON || Object.toJSON || window.JSON)) { 2518 throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!"); 2519 } 2520 2521 var 2522 /* This is the object, that holds the cached values */ 2523 _storage = {}, 2524 2525 /* Actual browser storage (localStorage or globalStorage['domain']) */ 2526 _storage_service = { jStorage: "{}" }, 2527 2528 /* DOM element for older IE versions, holds userData behavior */ 2529 _storage_elm = null, 2530 2531 /* How much space does the storage take */ 2532 _storage_size = 0, 2533 2534 /* function to encode objects to JSON strings */ 2535 json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)), 2536 2537 /* function to decode objects from JSON strings */ 2538 json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function (str) { 2539 return String(str).evalJSON(); 2540 }, 2541 2542 /* which backend is currently used */ 2543 _backend = false, 2544 2545 /* Next check for TTL */ 2546 _ttl_timeout, 2547 2548 /** 2549 * XML encoding and decoding as XML nodes can't be JSON'ized 2550 * XML nodes are encoded and decoded if the node is the value to be saved 2551 * but not if it's as a property of another object 2552 * Eg. - 2553 * $.jStorage.set("key", xmlNode); // IS OK 2554 * $.jStorage.set("key", {xml: xmlNode}); // NOT OK 2555 */ 2556 _XMLService = { 2557 2558 /** 2559 * Validates a XML node to be XML 2560 * based on jQuery.isXML function 2561 */ 2562 isXML: function (elm) { 2563 var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement; 2564 return documentElement ? documentElement.nodeName !== "HTML" : false; 2565 }, 2566 2567 /** 2568 * Encodes a XML node to string 2569 * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/ 2570 */ 2571 encode: function (xmlNode) { 2572 if (!this.isXML(xmlNode)) { 2573 return false; 2574 } 2575 try { // Mozilla, Webkit, Opera 2576 return new XMLSerializer().serializeToString(xmlNode); 2577 } catch (E1) { 2578 try { // IE 2579 return xmlNode.xml; 2580 } catch (E2) { } 2581 } 2582 return false; 2583 }, 2584 2585 /** 2586 * Decodes a XML node from string 2587 * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/ 2588 */ 2589 decode: function (xmlString) { 2590 var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) || 2591 (window.ActiveXObject && function (_xmlString) { 2592 var xml_doc = new ActiveXObject('Microsoft.XMLDOM'); 2593 xml_doc.async = 'false'; 2594 xml_doc.loadXML(_xmlString); 2595 return xml_doc; 2596 }), 2597 resultXML; 2598 if (!dom_parser) { 2599 return false; 2600 } 2601 resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml'); 2602 return this.isXML(resultXML) ? resultXML : false; 2603 } 2604 }; 2605
2606 ////////////////////////// PRIVATE METHODS //////////////////////// 2607 2608 /** 2609 * Initialization function. Detects if the browser supports DOM Storage 2610 * or userData behavior and behaves accordingly. 2611 * @returns undefined 2612 */ 2613 function _init() { 2614 /* Check if browser supports localStorage */ 2615 var localStorageReallyWorks = false; 2616 if ("localStorage" in window) { 2617 try { 2618 window.localStorage.setItem('_tmptest', 'tmpval'); 2619 localStorageReallyWorks = true; 2620 window.localStorage.removeItem('_tmptest'); 2621 } catch (BogusQuotaExceededErrorOnIos5) { 2622 // Thanks be to iOS5 Private Browsing mode which throws 2623 // QUOTA_EXCEEDED_ERRROR DOM Exception 22. 2624 } 2625 } 2626 if (localStorageReallyWorks) { 2627 try { 2628 if (window.localStorage) { 2629 _storage_service = window.localStorage; 2630 _backend = "localStorage"; 2631 } 2632 } catch (E3) { /* Firefox fails when touching localStorage and cookies are disabled */ } 2633 } 2634 /* Check if browser supports globalStorage */ 2635 else if ("globalStorage" in window) { 2636 try { 2637 if (window.globalStorage) { 2638 _storage_service = window.globalStorage[window.location.hostname]; 2639 _backend = "globalStorage"; 2640 } 2641 } catch (E4) { /* Firefox fails when touching localStorage and cookies are disabled */ } 2642 } 2643 /* Check if browser supports userData behavior */ 2644 else { 2645 _storage_elm = document.createElement('link'); 2646 if (_storage_elm.addBehavior) { 2647 2648 /* Use a DOM element to act as userData storage */ 2649 _storage_elm.style.behavior = 'url(#default#userData)'; 2650 2651 /* userData element needs to be inserted into the DOM! */ 2652 document.getElementsByTagName('head')[0].appendChild(_storage_elm); 2653 2654 _storage_elm.load("jStorage"); 2655 var data = "{}"; 2656 try { 2657 data = _storage_elm.getAttribute("jStorage"); 2658 } catch (E5) { } 2659 _storage_service.jStorage = data; 2660 _backend = "userDataBehavior"; 2661 } else { 2662 _storage_elm = null; 2663 return; 2664 } 2665 } 2666 2667 _load_storage(); 2668 2669 // remove dead keys 2670 _handleTTL(); 2671 } 2672 2673 /** 2674 * Loads the data from the storage based on the supported mechanism 2675 * @returns undefined 2676 */ 2677 function _load_storage() { 2678 /* if jStorage string is retrieved, then decode it */ 2679 if (_storage_service.jStorage) { 2680 try { 2681 _storage = json_decode(String(_storage_service.jStorage)); 2682 } catch (E6) { _storage_service.jStorage = "{}"; } 2683 } else { 2684 _storage_service.jStorage = "{}"; 2685 } 2686 _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; 2687 } 2688 2689 /** 2690 * This functions provides the "save" mechanism to store the jStorage object 2691 * @returns undefined 2692 */ 2693 function _save() { 2694 try { 2695 _storage_service.jStorage = json_encode(_storage); 2696 // If userData is used as the storage engine, additional 2697 if (_storage_elm) { 2698 _storage_elm.setAttribute("jStorage", _storage_service.jStorage); 2699 _storage_elm.save("jStorage"); 2700 } 2701 _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; 2702 } catch (E7) { /* probably cache is full, nothing is saved this way*/ } 2703 } 2704 2705 /** 2706 * Function checks if a key is set and is string or numberic 2707 */ 2708 function _checkKey(key) { 2709 if (!key || (typeof key != "string" && typeof key != "number")) { 2710 throw new TypeError('Key name must be string or numeric'); 2711 } 2712 if (key == "__jstorage_meta") { 2713 throw new TypeError('Reserved key name'); 2714 } 2715 return true; 2716 } 2717 2718 /** 2719 * Removes expired keys 2720 */ 2721 function _handleTTL() { 2722 var curtime, i, TTL, nextExpire = Infinity, changed = false; 2723 2724 clearTimeout(_ttl_timeout); 2725 2726 if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object") { 2727 // nothing to do here 2728 return; 2729 } 2730 2731 curtime = +new Date(); 2732 TTL = _storage.__jstorage_meta.TTL; 2733 for (i in TTL) { 2734 if (TTL.hasOwnProperty(i)) { 2735 if (TTL[i] <= curtime) { 2736 delete TTL[i]; 2737 delete _storage[i]; 2738 changed = true; 2739 } else if (TTL[i] < nextExpire) { 2740 nextExpire = TTL[i]; 2741 } 2742 } 2743 } 2744 2745 // set next check 2746 if (nextExpire != Infinity) { 2747 _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime); 2748 } 2749 2750 // save changes 2751 if (changed) { 2752 _save(); 2753 } 2754 } 2755 2756
2757 ////////////////////////// PUBLIC INTERFACE ///////////////////////// 2758 2759 $.jStorage = { 2760 /* Version number */ 2761 version: "0.1.7.0", 2762 2763 /** 2764 * Sets a key's value. 2765 * 2766 * @param {String} key - Key to set. If this value is not set or not 2767 * a string an exception is raised. 2768 * @param {Mixed} value - Value to set. This can be any value that is JSON 2769 * compatible (Numbers, Strings, Objects etc.). 2770 * @param {Object} [options] - possible options to use 2771 * @param {Number} [options.TTL] - optional TTL value 2772 * @returns the used value 2773 */ 2774 set: function (key, value, options) { 2775 _checkKey(key); 2776 2777 options = options || {}; 2778 2779 if (_XMLService.isXML(value)) { 2780 value = { _is_xml: true, xml: _XMLService.encode(value) }; 2781 } else if (typeof value == "function") { 2782 value = null; // functions can't be saved! 2783 } else if (value && typeof value == "object") { 2784 // clone the object before saving to _storage tree 2785 value = json_decode(json_encode(value)); 2786 } 2787 _storage[key] = value; 2788 2789 if (!isNaN(options.TTL)) { 2790 this.setTTL(key, options.TTL); 2791 // also handles saving 2792 } else { 2793 _save(); 2794 } 2795 return value; 2796 }, 2797 2798 /** 2799 * Looks up a key in cache 2800 * 2801 * @param {String} key - Key to look up. 2802 * @param {mixed} def - Default value to return, if key didn't exist. 2803 * @returns the key value, default value or <null> 2804 */ 2805 get: function (key, def) { 2806 _checkKey(key); 2807 if (key in _storage) { 2808 if (_storage[key] && typeof _storage[key] == "object" && 2809 _storage[key]._is_xml && 2810 _storage[key]._is_xml) { 2811 return _XMLService.decode(_storage[key].xml); 2812 } else { 2813 return _storage[key]; 2814 } 2815 } 2816 return typeof (def) == 'undefined' ? null : def; 2817 }, 2818 2819 /** 2820 * Deletes a key from cache. 2821 * 2822 * @param {String} key - Key to delete. 2823 * @returns true if key existed or false if it didn't 2824 */ 2825 deleteKey: function (key) { 2826 _checkKey(key); 2827 if (key in _storage) { 2828 delete _storage[key]; 2829 // remove from TTL list 2830 if (_storage.__jstorage_meta && 2831 typeof _storage.__jstorage_meta.TTL == "object" && 2832 key in _storage.__jstorage_meta.TTL) { 2833 delete _storage.__jstorage_meta.TTL[key]; 2834 } 2835 _save(); 2836 return true; 2837 } 2838 return false; 2839 }, 2840 2841 /** 2842 * Sets a TTL for a key, or remove it if ttl value is 0 or below 2843 * 2844 * @param {String} key - key to set the TTL for 2845 * @param {Number} ttl - TTL timeout in milliseconds 2846 * @returns true if key existed or false if it didn't 2847 */ 2848 setTTL: function (key, ttl) { 2849 var curtime = +new Date(); 2850 _checkKey(key); 2851 ttl = Number(ttl) || 0; 2852 if (key in _storage) { 2853 2854 if (!_storage.__jstorage_meta) { 2855 _storage.__jstorage_meta = {}; 2856 } 2857 if (!_storage.__jstorage_meta.TTL) { 2858 _storage.__jstorage_meta.TTL = {}; 2859 } 2860 2861 // Set TTL value for the key 2862 if (ttl > 0) { 2863 _storage.__jstorage_meta.TTL[key] = curtime + ttl; 2864 } else { 2865 delete _storage.__jstorage_meta.TTL[key]; 2866 } 2867 2868 _save(); 2869 2870 _handleTTL(); 2871 return true; 2872 } 2873 return false; 2874 }, 2875 2876 /** 2877 * Deletes everything in cache. 2878 * 2879 * @return true 2880 */ 2881 flush: function () { 2882 _storage = {}; 2883 _save(); 2884 return true; 2885 }, 2886 2887 /** 2888 * Returns a read-only copy of _storage 2889 * 2890 * @returns Object 2891 */ 2892 storageObj: function () { 2893 function F() { } 2894 F.prototype = _storage; 2895 return new F(); 2896 }, 2897 2898 /** 2899 * Returns an index of all used keys as an array
2900 * ['key1', 'key2',..'keyN'] 2901 * 2902 * @returns Array 2903 */ 2904 index: function () { 2905 var index = [], i; 2906 for (i in _storage) { 2907 if (_storage.hasOwnProperty(i) && i != "__jstorage_meta") { 2908 index.push(i); 2909 } 2910 } 2911 return index; 2912 }, 2913 2914 /** 2915 * How much space in bytes does the storage take? 2916 * 2917 * @returns Number 2918 */ 2919 storageSize: function () { 2920 return _storage_size; 2921 }, 2922 2923 /** 2924 * Which backend is currently in use? 2925 * 2926 * @returns String 2927 */ 2928 currentBackend: function () { 2929 return _backend; 2930 }, 2931 2932 /** 2933 * Test if storage is available 2934 * 2935 * @returns Boolean 2936 */ 2937 storageAvailable: function () { 2938 return !!_backend; 2939 }, 2940 2941 /** 2942 * Reloads the data from browser storage 2943 * 2944 * @returns undefined 2945 */ 2946 reInit: function () { 2947 var new_storage_elm, data; 2948 if (_storage_elm && _storage_elm.addBehavior) { 2949 new_storage_elm = document.createElement('link'); 2950 2951 _storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm); 2952 _storage_elm = new_storage_elm; 2953 2954 /* Use a DOM element to act as userData storage */ 2955 _storage_elm.style.behavior = 'url(#default#userData)'; 2956 2957 /* userData element needs to be inserted into the DOM! */ 2958 document.getElementsByTagName('head')[0].appendChild(_storage_elm); 2959 2960 _storage_elm.load("jStorage"); 2961 data = "{}"; 2962 try { 2963 data = _storage_elm.getAttribute("jStorage"); 2964 } catch (E5) { } 2965 _storage_service.jStorage = data; 2966 _backend = "userDataBehavior"; 2967 } 2968 2969 _load_storage(); 2970 } 2971 }; 2972 2973 // Initialize jStorage 2974 _init(); 2975 2976})(window.$ || window.jQuery); 2977 2978 2979// ------- Helper ------------- 2980var Helpers = { 2981 RoundToHalf: function (value) { 2982 //<summary>Converts decimal to nearest half value (e.g 4.3=4.5, 5.1=5)</summary> 2983 2984 var converted = parseFloat(value); // Make sure we have a number 2985 var decimal = (converted - parseInt(converted, 10)); 2986 //Set to Math.floor to match value returned by search provider 2987 decimal = Math.floor(decimal * 10); 2988 if (decimal == 5) { return (parseInt(converted, 10) + 0.5); } 2989 if ((decimal < 3) || (decimal > 7)) { 2990 return Math.round(converted); 2991 } else { 2992 return (parseInt(converted, 10) + 0.5); 2993 } 2994 }, 2995 FormatDate: function (JavascriptDateTime) { 2996 2997 var dateString = JavascriptDateTime.substring(0, 10); 2998 2999 var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
2999November", "December"] 3000 3001 var dateArr = dateString.split('-'); 3002 var year = dateArr[0]; 3003 3004 var monthString = dateArr[1]; 3005 var monthInt = parseInt(monthString, 10); 3006 var monthIndex = monthInt - 1; 3007 3008 var month = monthNames[monthIndex]; 3009 var day = dateArr[2]; 3010 3011 var dateJSON = 3012 { 3013 "day": day, 3014 "month": month, 3015 "year": year 3016 } 3017 3018 return dateJSON; 3019 } 3020} 3021 3022// ------- Helper ------------- 3023var Helpers = { 3024 RoundToHalf: function (value) { 3025 //<summary>Converts decimal to nearest half value (e.g 4.3=4.5, 5.1=5)</summary> 3026 3027 var converted = parseFloat(value); // Make sure we have a number 3028 var decimal = (converted - parseInt(converted, 10)); 3029 3030 //Set to Math.floor to match value returned by search provider 3031 decimal = Math.floor(decimal * 10); 3032 if (decimal == 5) { return (parseInt(converted, 10) + 0.5); } 3033 if ((decimal < 3) || (decimal > 7)) { 3034 return Math.round(converted); 3035 } else { 3036 return (parseInt(converted, 10) + 0.5); 3037 } 3038 }, 3039 FormatDate: function (JavascriptDateTime) { 3040 3041 var dateString = JavascriptDateTime.substring(0, 10); 3042 3043 var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
3043November", "December"] 3044 3045 var dateArr = dateString.split('-'); 3046 var year = dateArr[0]; 3047 3048 var monthString = dateArr[1]; 3049 var monthInt = parseInt(monthString, 10); 3050 var monthIndex = monthInt - 1; 3051 3052 var month = monthNames[monthIndex]; 3053 var day = dateArr[2]; 3054 3055 var dateJSON = 3056 { 3057 "day": day, 3058 "month": month, 3059 "year": year 3060 } 3061 3062 return dateJSON; 3063 3064 }, 3065 FormatCurrencyAndPrice: function (currencySymbol, currencyPosition, price) { 3066 switch (currencyPosition) { 3067 case 'left': 3068 return currencySymbol + price; 3069 case 'right': 3070 return price + currencySymbol; 3071 case 'leftwithspace': 3072 return currencySymbol + ' ' + price; 3073 case 'rightwithspace': 3074 return price + ' ' + currencySymbol; 3075 default: 3076 return currencySymbol + price; 3077 } 3078 } 3079} 3080 3081var GlobalURLHelper = { 3082 SetRedirectUrl: function (url) { 3083 3084 if (url.length < 1) { 3085 return url; 3086 } 3087 3088 var MainSiteAbsolutePath = Next.Settings.Channel.MainSiteAbsolutePath; 3089 3090 //check first character of url 3091 var firstUrlChar = url.charAt(0) 3092 3093 if (firstUrlChar !== '/') { 3094 url = '/' + url; 3095 } 3096 3097 if (MainSiteAbsolutePath == '/') { 3098 return url; 3099 3100 } else 3101 return url = MainSiteAbsolutePath + url; 3102 } 3103} 3104 3105var Button = { 3106 Vars: { 3107 Classes: { 3108 Loading: "loading" 3109 } 3110 }, 3111 UpdateUI: function ($btn, settings) { 3112 3113 var _this = this; 3114 3115 if (settings.loading) { 3116 3117 var btnWidth = $btn.width(); 3118 3119 $btn.width(btnWidth) 3120 .addClass(_this.Vars.Classes.Loading); 3121 } 3122 else if (settings.loading == false) { 3123 3124 $btn.removeClass(_this.Vars.Classes.Loading) 3125 .css("width", ""); 3126 } 3127 3128 } 3129} 3130 3131var Next = Next || {}; 3132Next.NextTrial = { 3133 Init: function (trialEnabled) { 3134 this.Vars.TrialEnabled = trialEnabled; 3135 this.Vars.TrialImgPath = "/content/co-uk/images/trial/"; 3136 this.Vars.TrialImgAltTxt = "Northern Ireland Only | FREE next day delivery to home on orders over £50*. All deliveries and returns to store are FREE. Returns collected from your home cost £1.99. * Subject to stock, and courier availability. Product exceptions apply see full Terms and Conditions."; 3137 this.Vars.TrialRefundTxt = 'All deliveries and returns to store are free. Returns collected from your home cost £2.50. Subject to stock, and courier availability. Product exceptions apply, see full <a href=\"/terms" target=\"_blank\"><u>Terms and Conditions</u></a>.'; 3138 this.Vars.TrialInitialised = true; 3139 this.Vars.TrialCookieName = 'Next-TL'; 3140 }, 3141 3142 Vars: { 3143 TrialEnabled: null, 3144 TrialImgPath: null, 3145 TrialInitialised: false 3146 }, 3147 3148 InitRecursiveCheck: function (f) { 3149 if (!this.Vars.TrialInitialised) { 3150 setTimeout(function () { 3151 f(); 3152 }, 600); 3153 return false; 3154 } 3155 else { 3156 return true; 3157 } 3158 }, 3159 3160 trackGa: function () { 3161 var _this = this; 3162 var trialId = _this.getTrialCookieKeyValue(_this.Vars.TrialCookieName, 'id'); 3163 var urlPath = window.location.pathname.toLowerCase(); 3164 3165 if (!Next.NextTrial.InitRecursiveCheck(_this.trackGa)) { 3166 return false; 3167 } 3168 3169 if (_this.getTrialCookie(_this.Vars.TrialCookieName) != null) { 3170 if (urlPath == "/") { 3171 TrackGAEvent('Trial', trialId, 'NI Homepage', 0); 3172 } else if (urlPath == "/shoppingbag") { 3173 TrackGAEvent('Trial', trialId, 'NI Shoppingbag', 0); 3174 } else if (urlPath == "/quickshop") { 3175 TrackGAEvent('Trial', trialId, 'NI Quickshop', 0); 3176 } else if (urlPath == "/delivery/") { 3177 TrackGAEvent('Trial', trialId, 'NI Delivery', 0); 3178 } 3179 } 3180 }, 3181 3182 3183 getTrialCookie: function (name) { 3184 var nameEQ = name + "="; 3185 var ca = document.cookie.split(';'); 3186 for (var i = 0; i < ca.length; i++) { 3187 var c = ca[i];
3188 while (c.charAt(0) == ' ') c = c.substring(1, c.length); 3189 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); 3190 } 3191 return null; 3192 }, 3193 3194 getTrialCookieKeyValue: function (cookieName, keyName) { 3195 var _this = this; 3196 var nextTrialCookie = _this.getTrialCookie(cookieName); 3197 if (nextTrialCookie != null) { 3198 var splitVars = nextTrialCookie.split("&"); 3199 for (var i = 0; i < splitVars.length; i++) { 3200 var splitKvp = splitVars[i].split("="); 3201 if (splitKvp != null && splitKvp[0] == keyName) 3202 return splitKvp[1]; 3203 } 3204 } 3205 return null; 3206 }, 3207 3208 getTrialHomepage: function () { 3209 3210 var _this = this; 3211 if (window.location.pathname != "/") { 3212 return false; 3213 } 3214 3215 if (!Next.NextTrial.InitRecursiveCheck(_this.getTrialHomepage)) { 3216 return false; 3217 } 3218 3219 if (_this.Vars.TrialEnabled == 'true') { 3220 if (_this.getTrialCookie(_this.Vars.TrialCookieName) != null) { 3221 var trialId = _this.getTrialCookieKeyValue('Next-TL', 'id'); 3222 var trialStoreFrontUrl = window.location.href + trialId; 3223 3224 if (trialId != null) { 3225 $.ajax({
3226 url: trialStoreFrontUrl, 3227 timeout: 10000, 3228 dataType: 'html', 3229 type: 'GET', 3230 error: function () { }, 3231 success: function (htmlResponse, textStatus, jqXHR) { 3232 checkAndRedirectForAjaxError(jqXHR, false, 'global.js: function getTrialHomepage'); 3233 var htmlDataResponse = $(htmlResponse); 3234 $('.Storefront .Background').replaceWith(htmlDataResponse.find('.Storefront .Background')); 3235 _this.trackGa(); 3236 }, 3237 complete: function (jqXHR, errorThrown) { 3238 if (errorThrown == 'timeout') { 3239 3240 } 3241 } 3242 }) 3243 } 3244 } 3245 } 3246 }, 3247 3248 getTrialShopBanner: function () { 3249 var _this = this; 3250 3251 if (!Next.NextTrial.InitRecursiveCheck(_this.getTrialShopBanner)) { 3252 return false; 3253 } 3254 3255 var trialImgPath = _this.Vars.TrialImgPath; 3256 var trialImgAltTxt = _this.Vars.TrialImgAltTxt; 3257 var shoppingBagImgPath; 3258 3259 var oBanner = $('.ContentArea .trialShopBanner'); 3260 if (_this.Vars.TrialEnabled == 'true') { 3261 if (_this.getTrialCookie('Next-TL') != null) { 3262 3263 var trialId = _this.getTrialCookieKeyValue('Next-TL', 'id'); 3264 if (trialId != null) { 3265 if (oBanner.length) { 3266 shoppingBagImgPath = MainSiteURL + trialImgPath + trialId + '/shop.jpg'; 3267 oBanner.html("<img src='" + shoppingBagImgPath + "' alt='" + trialImgAltTxt + "' />"); 3268 _this.trackGa(); 3269 } 3270 } 3271 } 3272 } 3273 }, 3274 3275 getTrialCheckoutBanner: function () { 3276 var _this = this; 3277 var MainSiteURLSecure = window.location.protocol + "//" + window.location.host; 3278 3279 if (!Next.NextTrial.InitRecursiveCheck(_this.getTrialCheckoutBanner)) { 3280 return false; 3281 } 3282 3283 var trialImgPath = _this.Vars.TrialImgPath; 3284 var trialImgAltTxt = _this.Vars.TrialImgAltTxt; 3285 var checkoutImgPath; 3286 3287 var oBanner = $('#deliveryForm .trialCheckoutBanner'); 3288 if (_this.Vars.TrialEnabled == 'true') { 3289 if (_this.getTrialCookie('Next-TL') != null) { 3290 3291 var trialId = _this.getTrialCookieKeyValue('Next-TL', 'id'); 3292 if (trialId != null) { 3293 if (oBanner.length) { 3294 checkoutImgPath = MainSiteURLSecure + trialImgPath + trialId + '/checkout.jpg'; 3295 oBanner.html("<img src='" + checkoutImgPath + "' alt='" + trialImgAltTxt + "' />"); 3296 _this.trackGa(); 3297 } 3298 } 3299 } 3300 } 3301 }, 3302 3303 getTrialCheckoutRefundTxt: function () { 3304 var _this = this; 3305 3306 if (!Next.NextTrial.InitRecursiveCheck(_this.getTrialCheckoutRefundTxt)) { 3307 return false; 3308 } 3309 3310 var trialImgPath = _this.Vars.TrialImgPath; 3311 var trialImgAltTxt = _this.Vars.TrialImgAltTxt; 3312 var trialRefundTxt = _this.Vars.TrialRefundTxt; 3313 3314 var oRefundTxt = $('.dBag .trialCheckoutRefundTxt'); 3315 if (_this.Vars.TrialEnabled == 'true') { 3316 if (_this.getTrialCookie('Next-TL') != null) { 3317 3318 var trialId = _this.getTrialCookieKeyValue('Next-TL', 'id'); 3319 if (trialId != null) { 3320 if (oRefundTxt.length) { 3321 oRefundTxt.html(trialRefundTxt); 3322 } 3323 } 3324 } 3325 } 3326 } 3327} 3328 3329$(function () { 3330 var urlPath = window.location.pathname.toLowerCase(); 3331 if (typeof NextTrialEnabled !== "undefined") { 3332 Next.NextTrial.Init(NextTrialEnabled); 3333 } 3334}); 3335 3336function isValidDomain(domain) { 3337 return domain.split(".").length > 2; 3338} 3339 3340function getValidDomain() { 3341 var siteUrl = Next.Settings.Channel.SiteUrl; 3342 3343 var domains = ["next"]; 3344 var strippedDownUrl = ""; 3345 3346 for (var i = 0; i < domains.length; ++i) { 3347 var domainVal = domains[i]; 3348 var regex = new RegExp("^.*" + domainVal + "")
3349 strippedDownUrl = (siteUrl.replace(regex, domainVal)).replace(/(?=\/)(.*)/, ''); 3350 3351 if (isValidDomain(strippedDownUrl)) { 3352 return strippedDownUrl; 3353 } 3354 } 3355 3356 return ""; 3357} 3358 3359function generateAlternativeCookiePolicy(site, link, showFull) { 3360 3361 var returnString = ""; 3362 var cookiePolicyClass = !showFull ? "altCookiePolicySmall" : ""; 3363 3364 var isUK = Next.Settings.Channel.CountryCode === 'GB'; 3365 var isChildsPlay = site === "Childsplay"; 3366 var isFromNext = window.location.href.toLowerCase().indexOf('?fromnext') > 0; 3367 var directToCP = isChildsPlay && !isFromNext ? " cp-direct" : ""; 3368 3369 returnString += "<div id='alternativeCookiePolicy' class='col-sm-10 " + cookiePolicyClass + directToCP + "'>"; 3370 3371 if (showFull) { 3372 var cookiePolicyHeaderText; 3373 var sectionImg1Class, sectionImg2Class, sectionImg3Class, sectionImg4Class; 3374 var section1Text, section2Text, section3Text, section4Text; 3375 var section1Hide, section2Hide; 3376 3377 sectionImg1Class = "sharedBag"; 3378 sectionImg2Class = "nextUnlimited"; 3379 sectionImg3Class = "spreadCosts"; 3380 sectionImg4Class = "backArrow"; 3381 3382 section1Hide = false; 3383 section2Hide = false; 3384 3385 switch (site) { 3386 case "Childsplay": 3387 sectionImg1Class = "luxuryBrands"; 3388 sectionImg2Class = "sharedBag"; 3389 cookiePolicyHeaderText = "Childsplay Clothing"; 3390 section1Text = "Luxury Kids Brands from Moncler to Versace"; 3391 section2Text = "Shared Shopping Bag across Next and Childsplay"; 3392 section3Text = "Pay Using your Next Pay Account*"; 3393 section4Text = "Easily Navigate Back to Next"; 3394 section2Hide = !Next.Settings.Channel.StateConvergence.IsEnabled; 3395 break; 3396 } 3397 3398 var sectionHiddenFormatClass = section1Hide || section2Hide ? "sectionHiddenFormat " : ""; 3399 3400 returnString += "<div class='altCookiePolicyHeader'>"; 3401 if (!isChildsPlay) { 3402 returnString += "<span class='altCookiePolicyHeaderText'>Welcome to <span class='altCookiePolicyHeaderLogo'></span>" + cookiePolicyHeaderText + "</span>"; 3403 } else { 3404 returnString += "<span class='altCookiePolicyHeaderText'>Welcome to " + cookiePolicyHeaderText + "</span>"; 3405 } 3406 returnString += "<span class='altCookiePolicyClose'></span>"; 3407 returnString += "</div>"; 3408 3409 returnString += "<div id='altCookiePolicySections'>"; 3410 if (!section1Hide) { 3411 returnString += "<div class='" + sectionHiddenFormatClass + "altCookiePolicySection'><span class='altCookiePolicySectionImage " + sectionImg1Class + "'></span><span class='altCookiePolicySectionText'>" + section1Text + "</span></div>"; 3412 } 3413 if (!section2Hide) { 3414 returnString += "<div class='" + sectionHiddenFormatClass + "altCookiePolicySection'><span class='altCookiePolicySectionImage " + sectionImg2Class + "'></span><span class='altCookiePolicySectionText'>" + section2Text + "</span></div>"; 3415 } 3416 3417 returnString += "<div class='" + sectionHiddenFormatClass + "altCookiePolicySection'><span class='altCookiePolicySectionImage " + sectionImg3Class + "'></span><span class='altCookiePolicySectionText'>" + section3Text + "</span></div>"; 3418 returnString += "<div class='" + sectionHiddenFormatClass + "altCookiePolicySection'><span class='altCookiePolicySectionImage " + sectionImg4Class + "'></span><span class='altCookiePolicySectionText'>" + section4Text + "</span></div>"; 3419 3420 returnString += "</div>"; 3421 } 3422 3423 returnString += "<div id='altCookiePolicyLegal'" + (isChildsPlay && isFromNext ? " class='from-next'" : "") + ">"; 3424 3425 if (!isChildsPlay || (isChildsPlay && isFromNext && isUK)) { 3426 returnString += "<span class='altCookieNextPayTerms'>*29.9% APR Representative Variable. Credit subject to status.</span>"; 3427 } 3428 3429 returnString += " We use cookies to provide you with the best possible experience. By continuing to use our site, you agree to our use of cookies. <a href='" + link + "' target='_blank'>Find out more</a> about managing your cookie settings.<span class='altCookiePolicyClose'></span></div>" 3430 3431 3432 returnString += "</div>"; 3433 3434 return returnString; 3435} 3436 3437$(document).ready(function () { 3438 //Moved before userConsentManagementEnabled check. 3439 if (!Next.Settings.Channel.PlatModGoogleAnalyticsSDK) { 3440 $('#platform_modernisation_header').on('click', 3441 'a,\ 3442 button,\ 3443 [data-testid="header-small-screen-search"]>[role="link"],\ 3444 [data-testid="country-selector-select-menu"] li', 3445 function () { 3446 if (currentKey) { 3447 currentKey = null 3448 return 3449 } 3450 3451 var $this = $(this) 3452 var testId = $this.data("testid") 3453 if (testId && 3454 (testId == "header-search-bar-clear-text-button" || 3455 testId == "header-search-bar-button")) { 3456 var variant2 = $this.closest('[data-testid$="screen-search"]').find('[data-testid="header-search-bar-text-input"]').val(); 3457 TrackPlatModLinkID($this, null, variant2) 3458 return 3459 } 3460 3461 TrackPlatModLinkID($this); 3462 } 3463 ); 3464 3465 $('#platform_modernisation_header').on('keypress', '.header-adaptive-search form [data-testid="header-search-bar-text-input"]', function (e) { 3466 if (e.which == 13) { 3467 currentKey = 13 3468 var $this = $(this) 3469 TrackPlatModLinkID($this, null, $this.val(), $this.data('ga-enter-event')); 3470 } 3471 }); 3472 } 3473 3474 if (!Next.Settings.Channel.EnableModernisedConsentCookie) { 3475 var enabled = "true"; // Toggle for UK channels only
3476 if (Next.Settings.Channel.CountryCode !== 'GB') eraseCookie('NextConsentCookie');// This deletes the UK Consent Cookie if an International customer has it. 3477 var siteUrl = Next.Settings.Channel.SiteUrl; 3478 var nextConsentCookie = (Next.Settings.Channel.CountryCode === 'GB') ? readCookie('NextConsentCookie') : readCookie('NextDirectConsentCookie'); 3479 var aLink = (Next.Settings.Channel.CountryCode === 'GB') ? siteUrl + "/privacy" : siteUrl + "/privacypolicy"; 3480 var ukChannels = Next.Settings.Channel.ChannelId === "NEXT_GB" || Next.Settings.Channel.ChannelId === "NXTA_GB" || Next.Settings.Channel.ChannelId === "NXTB_GB" || Next.Settings.Channel.ChannelId === "NXTD_GB"; 3481 var fromNextCookieName = Next.Settings.Cookie.BackToNextCookieName; 3482 var isFromNext = readCookie(fromNextCookieName) === "True"; 3483 var isChildsPlay = Next.Settings.Channel.ChannelTheme === "Childsplay"; 3484 var userConsentManagementEnabled = Next.Settings.Channel.UserConsentManagement.Enabled; 3485 3486 if (userConsentManagementEnabled === true) { 3487 return; 3488 } 3489 else if ((enabled == "true" && ukChannels) || Next.Settings.UI.CookiePolicyPopup) { 3490 if (nextConsentCookie == null) { 3491 var cookiePopUp = ""; 3492 if (Next.Settings.Channel.EnableAltCookiePolicy) { 3493 if (isFromNext || isChildsPlay) {
3494 cookiePopUp = generateAlternativeCookiePolicy(Next.Settings.Channel.ChannelTheme, aLink, true); 3495 } else { 3496 cookiePopUp = generateAlternativeCookiePolicy(Next.Settings.Channel.ChannelTheme, aLink, false); 3497 } 3498 3499 } else { 3500 cookiePopUp = '<div class="CookieConsent hide-in-app-mode"><p style="font-weight:600">' + nxt.jstranslations.cookiepolicy.header + '</p><div class="CookieConsentCross"></div><p>' + nxt.jstranslations.cookiepolicy.messagebody1 + ' <br/> <a id="aFindOutMore" href="' + aLink + '" target="_blank" style="text-decoration:underline">' + nxt.jstranslations.cookiepolicy.messagebody2 + '</a> ' + nxt.jstranslations.cookiepolicy.messagebody3 + '</p ></div > '; 3501 } 3502 $("body").append(cookiePopUp); 3503 3504 var d = new Date(); 3505 d.setTime(d.getTime() + (365 * 24 * 60 * 60 * 1000)); //1 years expiry time 3506 var expires = "expires=" + d.toUTCString(); 3507 var domain = getValidDomain(); // strip URL down to just next domain e.g. https://account.next.de/de -> .next.de 3508 // If domain computed is not correct just use the configured domain 3509 if (!isValidDomain(domain)) { 3510 domain = Next.Settings.Channel.CookieDomainName; 3511 } 3512 var cookieData = (Next.Settings.Channel.CountryCode === 'GB') ? "NextConsentCookie=" : "NextDirectConsentCookie="; 3513 cookieData += "AllowCookiesFromNext=True;path=/;" + expires + ";domain=" + domain; 3514 document.cookie = cookieData; 3515 } 3516 } 3517 3518 $('.CookieConsent').on("click", function (event) { 3519 if (event.target.toString() != aLink) { 3520 3521 $('.CookieConsent').hide(); 3522 } 3523 }); 3524 $('.altCookiePolicyClose').on("click", function (e) { 3525 $("#alternativeCookiePolicy").hide(); 3526 }) 3527 } 3528}); 3529; 3530//define a global namespace for nxt - this will be used in all js file requiring translation. 3531var nxt = window.nxt || {}; 3532 3533(function ($, nx) { 3534 3535 /* 3536 * formats a JS string using string interpolation similar to string.Format in .Net 3537 * Usage: 3538 * var param1 = 3; 3539 * var param2 = "shopping bag"; 3540 * var message = nxt.stringFormat("You have {0} items in your {1}", param1, param2); 3541 */ 3542 nx.stringFormat = function (string) { 3543 var args = arguments; 3544 return string.replace(/{(\d+)}/g, function (match, index) { 3545 var pos = parseInt(index) + 1; 3546 return args[pos] < 0 ? args[pos] || Math : args[pos]; 3547 }); 3548 }; 3549 3550 3551 3552 //initialise 3553 nxt.jstranslations = nxt.jsData.next.jscontent; 3554 3555})(window.jQuery, nxt);; 3556var FontLoader = { 3557 Init: function () { 3558 3559 var _this = this; 3560 3561 WebFont.load({ 3562 timeout: 2000, 3563 custom: { 3564 families: ['azo-sans'] 3565 }, 3566 fontactive: function () { 3567 3568 _this.FontLoadComplete(); 3569 3570 }, 3571 fontinactive: function () { 3572 3573 _this.FontLoadComplete(); 3574 3575 } 3576 }); 3577 3578 }, 3579 FontLoadComplete: function () { 3580 3581 if (typeof setNavItemWidths === "function") { 3582 setNavItemWidths(); 3583 } 3584 3585 } 3586} 3587 3588$(function () { 3589 FontLoader.Init(); 3590});; 3591var Next = Next || {}; 3592Next.UI = Next.UI || {}; 3593Next.UI.CustomDropDown = { 3594 Init: function () { 3595 3596 if (Next.Settings.UI.CustomDropdownEnabled) { 3597 3598 $("select[data-customdropdown='true']:visible").each(function(i, v) { 3599 3600 var $this = $(this); 3601 if (!$this.data("dropkick")) { 3602 $this.dropkick(); 3603 } 3604 3605 }); 3606 } 3607 3608 } 3609} 3610 3611$(function () { 3612 Next.UI.CustomDropDown.Init(); 3613});; 3614 3615/* - Functions -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ 3616/* - Site Chrome / Layout ------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ 3617/* - Prototype Code ----------------------------------------------------------------------------------------------------------------------------------------------------------------*/ 3618 3619var xhr = null; 3620var isPageTransitioning = null; 3621var isSpreadTransitioning = null; 3622var isDragging = false; 3623var globalPageReferrer = document.referrer; 3624var globalPageLocation = document.location.href; 3625var lastRequest = null; 3626 3627function toggleFixedProductViewer() { // Check to see if the footer is in the correct state for the document's scroll position. 3628 if ($("ProductViewer")) { 3629 var PVB = $(document).scrollTop() + $(".ProductViewer").height() + 130; // (VPB = View Port Bottom) Calculates the position of the bottom of the users browser viewport on the document 3630 var FTB = $(".FooterPromotions").position().top; // (FTB = Footer Toolbar Bottom) Calculates the position of the bottom of the footer toolbar, when not fixed and in document flow 3631 if (PVB < FTB) { // If the View Port Bottom Position is less than the normal position of the bottom of the footer toolbar when it's in the document flow (ie the toolbar would be out of view) then 3632 if (parseInt($(".PageHeader").height()) == 48 && $(document).scrollTop() >= 32) { 3633 pinProductViewer() // Pin the Footer Toolbar to the bottom of the browser chrome 3634 } else { 3635 releaseProductViewer(); 3636 } 3637 } else { // Otherwise (ie the toolbar would be fully in view 3638 releaseProductViewer("Bottom"); // Unpin the toolbar from the browser chrome, and drop it back in to the document flow. 3639 } 3640 } 3641} 3642 3643function pinProductViewer() { 3644 /* 3645 if ($(".ProductViewer").hasClass("Top")) { 3646 $(".ProductViewer").removeClass("Top"); 3647 } 3648 */ 3649 if ($(".ProductViewer").hasClass("Bottom")) { 3650 $(".ProductViewer").removeClass("Bottom"); 3651 } 3652 if (!$(".ProductViewer").hasClass("Fixed")) { 3653 $(".ProductViewer").addClass("Fixed"); 3654 } else { 3655 $(".ProductViewer").removeAttr("style"); 3656 } 3657} 3658 3659function releaseProductViewer(className) { 3660 /* 3661 if ($(".ProductViewer").hasClass("Top")) { 3662 $(".ProductViewer").removeClass("Top"); 3663 } 3664 */ 3665 if ($(".ProductViewer").hasClass("Fixed")) { 3666 $(".ProductViewer").removeClass("Fixed"); 3667 } 3668 3669 if (className == "Bottom" && !$(".ProductViewer").hasClass("Bottom")) { 3670 $(".ProductViewer").addClass("Bottom"); 3671 } 3672 $(".ProductViewer").removeAttr("style"); 3673} 3674 3675/* - Page / Data Handlers ----------------------------------------------------------------------------------------------------------------------------------------------------------*/ 3676 3677function LoadHTMLFragment(Path, Parameters, TargetElement) { 3678 // alert("Remove This Call!"); 3679 $.get(Path, Parameters, function (data) { document.getElementById(TargetElement).innerHTML = data; }); 3680} 3681 3682// Method: LoadHTMLContent 3683// Description: This is the main method called by pages when a link is clicked 3684// It will check whether the link is the current page, whether another page is already loading and also whether the browser can use the HTML5 History Object (Pushstate) 3685// If it passes these checks then it will load the respective data and kick off a transition 3686// Params: 3687// Path: [REQUIRED] String - The URL of the content to be loaded - e.g. '/women' 3688// Title: [OPTIONAL] String - The title of the conent to be loaded (used for history object) - e.g. 'Next.co.uk - Women' 3689// Transition: [OPTIONAL] String - This will override the default transition UP/DOWN/LEFT/RIGHT - e.g. 'RIGHT' 3690// AddToHistory: [OPTIONAL] Boolean - This will determine whether the history stack needs to be updated - e.g. true 3691// ScrollPoint: [OPTIONAL] Integer - This will scroll the document to the specified point after loading - e.g. 0 3692// DocState: [OPTIONAL] JSON - This is an object that contains querystring params that are specific to the page. These will be appended to the path and used on page load. If a variable name begins with 'hidden', it will not be appended to the path and the hashtag variable is reserved & placed at the end of the querystring - e.g. { spreadview: true, hiddenSearchbreadcrumb: '<div>secret</div>', hashtag: '123456x50' } 3693// FormData: [OPTIONAL] JSON - If form data (page params) needs to be sent with the $.get request, it can be specified here - e.g. { w: 'dress' } 3694// CompletionCallback: [OPTIONAL] Function - Once the transition is complete, this method will be called - e.g. function() { ConsoleLog('Page has loaded...'); } 3695 3696function LoadHTMLContent(Path, Title, Transition, AddToHistory, ScrollPoint, DocState, FormData, CompletionCallback) { 3697 3698 if (typeof mobileDragged !== 'undefined' || typeof isDragStopping !== 'undefined') { 3699 if (mobileDragged || isDragStopping) { 3700 return false; 3701 } 3702 } 3703 3704 3705 var $ = jQuery; 3706 var allowTransition = true; 3707 3708 // Check whether the parent of the DOM element that called this has a class "Inactive" 3709 // This is mainly for Mouse Ears, where the user is on the last Spread in a Department and they select the Right Mouse Ear to load up a malformed sread page 3710 if ((this.event != undefined) && this.event.srcElement && this.event.srcElement.parentNode && this.event.srcElement.parentNode.parentNode) { 3711 var srcWrapper = this.event.srcElement.parentNode.parentNode; 3712 if ($(srcWrapper).hasClass("Inactive")) { 3713 return false; 3714 } 3715 } 3716 3717 //This ensures, no matter what happens, we don't lock the UI for more than 2 seconds 3718 //In theory this wont be needed but acts as a fallback should something go wrong 3719 var newRquest = new Date().getTime(); 3720 if (lastRequest != null) { 3721 if ((newRquest - lastRequest) > 2000) { 3722 isPageTransitioning = false; 3723 } 3724 } 3725 lastRequest = newRquest; 3726 3727 //$("section.BreadcrumbNavigation div.Content a.ContinueShopping").remove(); // this will remove 'continue shopping' link from all other pages - Raj 3728 3729 //Check if we're loading an external URL - external URLs should be loaded in a new window 3730 if (Path.indexOf(location.hostname) == -1 && Path.indexOf("http://") == 0) { 3731 window.open(Path, "_blank"); 3732 return false; 3733 } 3734 3735 //If page is transitioning, ignore request 3736 if (isPageTransitioning || isDragging) { 3737 return false; 3738 } 3739 3740 //This is for browsers that support pushState - if the user loads a page by manipulating the url or clicks on a standard link
3740with no onclick event 3741 //the browser will fully load the requested page. If the user then hits the back button the loadHTML content will fire at the same time as a full refresh, 3742 //effectively requesting the same content twice. This stops a transition happening on the same page AFTER the content has loaded. 3743 //Previously, this only comapred the Path element, but it has been modified to compare the full url as it was causing issues on the search page when consecutive searches were made 3744 var newUrl = new UrlHelper(Path); 3745 var currUrl = new UrlHelper(PageURLInput); 3746 3747 if (newUrl.ServerUrl() == currUrl.ServerUrl()) { 3748 isPageTransitioning = false; 3749 return false; 3750 } 3751 3752 $("section.BreadcrumbNavigation div.Content a.ContinueShopping").remove(); // this will remove 'continue shopping' link from all other pages - Raj 3753 3754 // If we are on an error page, then we should not transition to the next page as the DOM may be in a bad state 3755 if (window.location.pathname.indexOf('error') > -1) { 3756 allowTransition = false; 3757 } 3758 3759 // Temporary solution to force lipsy homepage to be loaded without transitions - this is required until the storefronts are moved into the MVC architechure 3760 if ((newUrl.Path() == '/lipsy') || (newUrl.Path() == '/lipsy/')) { 3761 allowTransition = false; 3762 } 3763 3764 // Check if the DocState was passed in and make it blank if it wasn't 3765 if (typeof DocState === "undefined") { 3766 DocState = {}; 3767 } 3768 3769 //Check we're not passing in the full URL the same as current location 3770 if (location.href == Path && !PagePopped) { 3771 isPageTransitioning = false; 3772 return false; 3773 } 3774 3775 globalPageReferrer = globalPageLocation; 3776 globalPageLocation = Path; 3777 3778 // Disable all transitions except spread to grid and grid to spread. 3779 if (Transition != 'SPREADTOGRID' && Transition != 'GRIDTOSPREAD') { 3780 allowTransition = false; 3781 } 3782 3783 /* ASP pages do not contain ContentArea, so transitions cannot be used for them. */ 3784 if (allowTransition && typeof window.history.pushState != "undefined" && document.getElementById("ContentArea") != null) { 3785 3786 //Clean up path - remove http://domain 3787 var siteURL = window.location.protocol + "//" + window.location.host; 3788 Path = Path.replace(siteURL, ''); 3789 3790 //Kill hero carousel from home page if it still exists 3791 if (typeof cInterval != "undefined") { 3792 try { 3793 clearInterval(cInterval); 3794 cInterval = null; 3795 } catch (ex) { } 3796 } 3797 3798 //Kill any current async page requests. 3799 //The only time this can happen is if the user has clicked a link again before the previous xhr request has completed 3800 //As soon as the xhr succeeds we will be transitioning the page so this wont apply. 3801 //This just allows the user the click something else if the request is taking too long 3802 if (xhr != null) { 3803 xhr.abort(); 3804 xhr = null; 3805 } 3806 3807 //Store outgoing page's resource array (jscript and css) so we can remove them from header when transition is complete 3808 PreviousPageResources = DynamicResources; 3809 DynamicResources = new Array(); 3810 var backTransition; 3811 var forwardTransition = Transition; 3812 var currentPageURL = window.location.pathname; 3813 3814 var addToHistory = true; 3815 if (AddToHistory == false) { 3816 addToHistory = false; 3817 currentPageURL = PageURLInput; 3818 //currentPageURL = Path; 3819 //ConsoleLog(PageURLInput); 3820 } 3821 3822 // Figure out what transition we need to use 3823 var currentHContext = getHTransitionContext(currentPageURL); 3824 3825 var currentVContext = getVTransitionContext(currentPageURL); 3826 var newHContext = getHTransitionContext(Path); 3827 var newVContext = getVTransitionContext(Path); 3828 var productPageURLPattern = /^\/[xgXG](l)?(L)?\d{2}\d+[sS]\d+$/gi; 3829 3830 var productPageTransition = false; 3831 if (currentPageURL.search(productPageURLPattern) != -1 || Path.search(productPageURLPattern) != -1) { 3832 productPageTransition = true; 3833 } 3834 3835 if (Transition == null || Transition == "") { 3836 if (Path == "/") { // Homepage Handler 3837 forwardTransition = "DOWN"; // Moving to the homepage is always a DOWN 3838 } else { 3839 if (currentHContext == -1) { // Currently on a root level page (ie homepage), all transitions are UP 3840 if (currentPageURL.search(productPageURLPattern) != -1) { 3841 forwardTransition = "DOWN"; 3842 } else { 3843 if (newVContext < currentVContext) { 3844 forwardTransition = "DOWN"; 3845 } else if (newVContext > currentVContext) { 3846 forwardTransition = "UP"; 3847 } else { 3848 forwardTransition = "LEFT"; 3849 } 3850 //If next page is a product page, regardless of where we are, animate up 3851 if (Path.search(productPageURLPattern) != -1) { 3852 forwardTransition = "UP"; 3853 } 3854 } 3855 } else if (currentHContext > newHContext) { // Changing Department RIGHT 3856 if (Path.search(productPageURLPattern) != -1) { 3857 forwardTransition = "UP"; 3858 } else { 3859 forwardTransition = "RIGHT"; 3860 } 3861 } else if (currentHContext < newHContext) { // Changing Department LEFT 3862 forwardTransition = "LEFT"; 3863 } else if (currentHContext == newHContext) { // Within the same Department 3864 if (currentVContext > newVContext) { // Current Vertical Context is higher in Context Tree than New Vertical Context 3865 forwardTransition = "DOWN"; 3866 } else if (currentVContext < newVContext) { // Current Vertical Context is lower in Context Tree than New Vertical Context 3867 forwardTransition = "UP"; 3868 } else if (currentVContext == newVContext) { // Current Vertical Context is same as New Vertical Context (Maybe a different horizontal context) 3869 //forwardTransition = "UP"; 3870 return false; // At the same vertical Context Level - Should be horizontal transition? 3871 } 3872 } 3873 } 3874 } 3875 3876 //Shopping bag 3877 if (Path.toLowerCase() == "/shoppingbag") { 3878 forwardTransition = "UP"; 3879 } 3880 if (currentPageURL.toLowerCase() == "/shoppingbag") { 3881 forwardTransition = "DOWN"; 3882 } 3883 3884 // Build up the request and get the content from the server 3885 if (Path != currentPageURL || addToHistory == false) { 3886 // We are about to build up the request path, but we need to get rid of the hash temporarily so we can append some querystring params 3887 // If we don't do this then we will end up with a malformed request and this causes issues when we go from a redirected product page with hashtag to a spread view and back again 3888 var hashSplit = Path.split('#'); 3889 var reqPath = hashSplit[0];
3890 3891 // Add contentonly=true so that we are not pulling back the header and footer 3892 if (reqPath.toLowerCase().indexOf("?") >= 0) 3893 reqPath += '&'; 3894 else 3895 reqPath += '?'; 3896 reqPath += "contentonly=true"; 3897 3898 // Build up a docstate string that we can append to the Url 3899 var docStateString = getDocStateString(DocState); 3900 3901 // Add the docstate string 3902 if (docStateString !== '') { 3903 if (docStateString.charAt(0) === '?') 3904 reqPath += docStateString.replace('?', '&'); 3905 else if (docStateString.charAt(0) === '&' || docStateString.charAt(0) === '#') 3906 reqPath += docStateString; 3907 else 3908 reqPath += '&' + docStateString; 3909 } 3910 3911 // If the original path already had a hash on it, add it back on 3912 if (hashSplit.length > 1) { 3913 // If the getDocStateString brought back a hashtag, then replace it with the original 3914 if (reqPath.indexOf('#') > -1) { 3915 reqPath = reqPath.substring(0, reqPath.indexOf('#')); 3916 } 3917 reqPath += '#' + hashSplit[1]; 3918 } 3919 3920 // Get the form data defined if it isn't 3921 var formData = "" 3922 if (typeof FormData != "undefined" && FormData != null) { 3923 formData = FormData; 3924 } 3925 3926 // Send the request 3927 xhr = $.ajax({ 3928 type: "GET", 3929 url: reqPath, 3930 timeout: 5000, 3931 data: formData, 3932 success: function (data, textStatus, jqXHR) { // 200 (and non cross domain 301 redirects) 3933 checkAndRedirectForAjaxError(jqXHR, false, 'AJAXv1.js: LoadHTMLContent'); 3934 if (jqXHR.getResponseHeader('X-RequestUrl') != null) { 3935 // We cannot detect whether there was a redirect (e.g. 301) as the browser handles this and gets the redirect content before bubbling anything back up to the JavaScript 3936 // So lets check if our custom header with the requested url on the actual server matches the url that we sent in the initial request 3937 try { 3938 var serverRequestUrl = jqXHR.getResponseHeader('X-RequestUrl'); 3939 var clientUrl = new UrlHelper(reqPath); 3940 var serverUrl = new UrlHelper(serverRequestUrl); 3941 if (clientUrl.ServerUrl() !== serverUrl.ServerUrl()) { 3942 // If they are different, reload the page with the original URL so that we are redirected with a hashtag 3943 window.location = Path; 3944 return false; 3945 } 3946 } 3947 catch (exp) { 3948 // Let it carry on and transition 3949 } 3950 } 3951 3952 TransitionHandler("success", data, forwardTransition, Path, addToHistory, ScrollPoint, DocState, CompletionCallback); 3953 }, 3954 error: function (data) { // 404 etc (and cross domain 301 redirects which are handled in the TransitionHandler) 3955 TransitionHandler("error", data, forwardTransition, Path, addToHistory, ScrollPoint, DocState, CompletionCallback); 3956 } 3957 }); 3958 } 3959 return false; 3960 } else { 3961 //Browser doesn't support pushstate, load page the 'traditional' way. 3962 isPageTransitioning = false; 3963 3964 var isMsie = jqueryUpgradeEnabledOnPage ? navigator.userAgent.toLowerCase().indexOf('msie') > -1 : $.browser.msie; 3965 3966 if (isMsie && Path.substr(0, 4) != ('http') && Path.charAt(0) != '/') { 3967 Path = "/" + Path; 3968 } 3969 3970 //Check if there's a hash param in the docstate 3971 if (DocState['hashtag'] !== undefined && DocState['hashtag'] !== null && DocState['hashtag'] != "") { 3972 window.location = Path + '#' + DocState['hashtag']; 3973 } else { 3974 window.location = Path; 3975 var _pattern = /[#][0-9][_][0-9]/; 3976 if (Next.Settings.UI.HorizontalFiltersEnabled && bcSearchBy != undefined && (bcSearchBy == "category" || bcSearchBy == "keyword") && (_pattern.test(Path))) { 3977 window.location.reload(); 3978 } 3979 } 3980 return false; 3981 } 3982} 3983 3984function TransitionHandler(type, data, forwardTransition, Path, addToHistory, ScrollPoint, DocState, CompletionCallback) { 3985 var $ = jQuery; 3986 //Could change animation interval here 3987 //The default is 13 - giving a smooth, approx 75 fps 3988 //But - this might kill some browsers. By increasing the interval between 'frames' we decrease the fps 3989 //potentially improving performance but a less smooth transition.
3990 //Setting the interval to 40 gives an approximate fps of 24 - shouldn't really go any lower than that. 3991 //jQuery.fx.interval = 20; 3992 3993 //No need to track users aborting the rerquest, 3994 //Just cancel 3995 if (data.statusText == "abort") { 3996 isPageTransitioning = false; 3997 xhr = null; 3998 return false; 3999 } 4000 4001 //Set the transition lock before we bind anything to the page as scripts contained in the incoming data 4002 //may need to fire after the transtion finishes - this prevents those scripts firing until we want them to 4003 isPageTransitioning = true; 4004 var Title 4005 Path = Path.toLowerCase(); 4006 4007 switch (type) { 4008 case "success": 4009 TrackGAPageView(Path); 4010 $(".NextContent").html(data); 4011 xhr = null; 4012 break; 4013 case "error": 4014 var StatusCode = "" + data.status 4015 var Details = ""; 4016 4017 if (data.statusText == "timeout") { 4018 StatusCode = "408"; 4019 } 4020 4021 switch (StatusCode) { 4022 case "404": 4023 StatusCode += " - File Not Found" 4024 Details = "Referrer: " + window.location.href; 4025 break; 4026 case "408": 4027 TrackGAEvent(StatusCode + " - " + data.statusText, Path, Details, 0); 4028 xhr.abort(); 4029 xhr = null; 4030 isPageTransitioning = false; 4031 return false; 4032 break; 4033 case "500": 4034 Details = data.responseText 4035 break; 4036 } 4037 4038 TrackGAEvent(StatusCode + " - " + data.statusText, Path, Details, 0); 4039 4040 if (data.responseText != "") { 4041 $(".NextContent").html(data.responseText); 4042 } else { 4043 // If we hit any cross domain redirect issues or the request is malformed, then we need to reload the page 4044 // We catch this instance with the status code 0. 4045 // The browser will make the initial request, get a 301, try to get the redirect url and hit a permission denyed if it is cross domain, then it will return a 0 instead of a 301 4046 if (data.status === 0) 4047 window.location = Path; 4048 return false; 4049 } 4050 break; 4051 } 4052 4053 4054 4055 //Check if we are currently on a spread page and the user has navigated to a spread in the same dept 4056 //If so, we need to trigger the spread animation instead 4057 if ($(".CurrentContent .SpreadsPage").length && $(".NextContent .SpreadsPage").length) { 4058 4059 var nextSpread = $(".NextContent .Spread"); 4060 var nextDept = $(".NextContent .SpreadsPage #hdnDeptName").val(); 4061 var curDept = $(".CurrentContent .SpreadsPage #hdnDeptName").val(); 4062 var nextPage = $(".NextContent .SpreadsPage .ImageLayer").attr("data-page"); 4063 var nextBook = $(".NextContent .SpreadsPage .ImageLayer").attr("data-book"); 4064 var nextOccurrence = $(".NextContent .SpreadsPage .ImageLayer").attr("data-occurrence"); 4065 4066 $(".NextContent").html(""); 4067 //If we're in the same dept 4068 if (nextDept == curDept) { 4069 //Target the item in the current carousel we need to click to load incoming spread 4070 var targetCarouselLI = $(".CurrentContent .SpreadsPage li[data-page='" + nextPage + "'][data-book='" + nextBook + "'][data-occurrence='" + nextOccurrence + "']"); 4071 $(targetCarouselLI).find("a").first().click(); 4072 //Scroll carousel to display selected carousel item 4073 scrollThumbNailBar($(targetCarouselLI).offset().left, targetCarouselLI, true); 4074 4075 } else { 4076 //Else - we're navigating between spread pages of different departments 4077 //// //Load the new spread carousel 4078 //// LoadSpreadCarousel(nextDept); 4079 //Update attributes on current spread - this allows us to target the correct carousel item once the carousel has loaded 4080 $(".ImageLayer").attr("data-page", nextPage); 4081 $(".ImageLayer").attr("data-book", nextBook);
4082 $(".ImageLayer").attr("data-occurrence", nextOccurrence); 4083 $(".ImageLayer").attr("data-reloadContent", true); 4084 $(".CurrentContent .SpreadsPage #hdnDeptName").val(nextDept); 4085 initCarousel(nextDept); 4086 //// initPage(); 4087 4088 } 4089 //Return false here as we've passed transitioning over to the spread transition handler (via a carousel click). 4090 return false; 4091 } 4092 4093 //Update breadcrumb/SEO 4094 if ($(".NextContent #PageParams").length) { 4095 var BCHTML = $(".NextContent #PageParams #BreadcrumbWrapper").html(); 4096 if (BCHTML.length) { 4097 UpdateBreadcrumbTrail(BCHTML); 4098 } 4099 4100 var seoTitle = $(".NextContent #PageParams #seoTitle").val(); 4101 var seoDescription = $(".NextContent #PageParams #seoDescription").val(); 4102 var seoKeywords = $(".NextContent #PageParams #seoKeywords").val(); 4103 var seoRobots = $(".NextContent #PageParams #seoRobots").val(); 4104 var seoCanonical = $(".NextContent #PageParams #seoCanonical").val(); 4105 4106 updateSeo(seoTitle, seoDescription, seoKeywords, seoRobots, seoCanonical); 4107 4108 //Remove page params from page 4109 $(".NextContent #PageParams").remove(); 4110 4111 } 4112 4113 if (addToHistory && !preventReplaceState) { // If we're navigating the site rather than clicking back or forward 4114 window.history.replaceState({ "url": window.location.href, "DocState": documentState, "title": document.title, "scrollPoint": $(window).scrollTop() }, document.title, window.location.href); // On load, attempt to replace the last state object on the history stack with one that is populated with the relevent transition data (although in all likely hood this will just insert a new item) 4115 } 4116 4117 var Animation = createAnimation() 4118 4119 var siteURL = window.location.protocol + "//" + window.location.host; 4120 var relPath = Path.replace(siteURL, ''); 4121 4122 if (relPath == "/") { 4123 $(".PageHeader").addClass("home"); 4124 if ($(".PageHeader").hasClass("Large")) { 4125 addToAnimation(Animation, '$("footer.ToolBar").animate({bottom: -50}, 1000);', 0, false); 4126 addToAnimation(Animation, '$("#SpreadsGridTitle").fadeOut(350);', 0, false); 4127 addToAnimation(Animation, '$(".PageHeader .BreadcrumbNavigation").fadeOut(350);', 350, true); 4128 //addToAnimation(Animation, '$(".HeaderSpacer").animate({ "height": "-=' + $(".BreadcrumbNavigation").height() + 'px" }, 250);', 250, true); 4129 addToAnimation(Animation, '$(".PageHeader").removeClass("Large")', 0, false); 4130 //addToAnimation(Animation, '$(".HeaderSpacer").removeClass("Large")', 0, false); 4131 //addToAnimation(Animation, '$(".HeaderSpacer").removeAttr("style")', 0, false); 4132 } 4133 } else { 4134 $(".PageHeader").removeClass("home"); 4135 if (!$(".PageHeader").hasClass("Large")) { 4136 addToAnimation(Animation, '$("footer.ToolBar").animate({bottom: 0}, 1000);', 0, false); 4137 addToAnimation(Animation, '$(".PageHeader").addClass("Large")', 0, false); 4138 //addToAnimation(Animation, '$(".HeaderSpacer").animate({ "height": "+=' + $(".BreadcrumbNavigation").height() + 'px" }, 250);', 250, true); 4139 //addToAnimation(Animation, '$(".HeaderSpacer").addClass("Large")', 0, false); 4140 //addToAnimation(Animation, '$(".HeaderSpacer").removeAttr("style")', 0, false); 4141 addToAnimation(Animation, '$(".PageHeader .BreadcrumbNavigation").fadeIn(650);', 150, true); 4142 } 4143 } 4144 4145 4146 4147 addToAnimation(Animation, 'PageTransition' + forwardTransition + '(' + ScrollPoint + ')', 0, false); 4148 if (typeof CompletionCallback != "undefined") 4149 addToAnimation(Animation, CompletionCallback, 0, true); 4150 4151 4152 runAnimation(Animation); 4153 4154 //If hash value was passed, add this to path 4155 if (DocState['hashtag'] !== undefined && DocState['hashtag'] !== null) { 4156 Path += '#' + DocState['hashtag']; 4157 } 4158 4159 //eval("PageTransition" + forwardTransition + "(" + ScrollPoint + ")");
4160 if (addToHistory) { 4161 var historyObject 4162 //if (Transition == null || Transition == "") { 4163 historyObject = { "url": Path, "title": Title, "DocState": DocState, "scrollPoint": ScrollPoint } 4164 //} else { 4165 // historyObject = { "url": Path, "title": Title, "transition": Transition } 4166 //} 4167 window.history.pushState(historyObject, Title, Path); 4168 } 4169 //document.title = Title; 4170 PageURLInput = Path; 4171 preventReplaceState = false; 4172 4173} 4174 4175function LoadSpread(Path, Title, Transition, AddToHistory, ScrollPoint, DocState) { 4176 4177 var $ = jQuery; 4178 4179 // Check whether the parent of the DOM element that called this has a class "Inactive" 4180 // This is mainly for Mouse Ears, where the user is on the last Spread in a Department and they select the Right Mouse Ear to load up a malformed sread page 4181 if ((this.event != undefined) && this.event.srcElement && this.event.srcElement.parentNode && this.event.srcElement.parentNode.parentNode) { 4182 var srcWrapper = this.event.srcElement.parentNode.parentNode; 4183 if ($(srcWrapper).hasClass("Inactive")) { 4184 return false; 4185 } 4186 } 4187 4188 // If there is a hash at the end of the Url, strip it off 4189 if (Path.indexOf('#', Path.length - 1) !== -1) { 4190 Path = Path.substring(0, Path.length - 1) 4191 } 4192 4193 //Check we're not passing in the full URL the same as current location 4194 if (location.href == Path && !PagePopped) { 4195 return false; 4196 } 4197 4198 // This prevents the spread from being loaded twice when Chrome incorrectly fires onpopstate on page load. 4199 var newUrl = new UrlHelper(Path); 4200 var currUrl = new UrlHelper(PageURLInput); 4201 if (newUrl.ServerUrl() == currUrl.ServerUrl()) { 4202 return false; 4203 } 4204 4205 if (typeof window.history.pushState != "undefined") { 4206 4207 //Prevents spread locking completely 4208 var newRquest = new Date().getTime(); 4209 if (lastRequest != null) { 4210 if ((newRquest - lastRequest) > 1500) { 4211 isSpreadTransitioning = false; 4212 } 4213 } 4214 lastRequest = newRquest; 4215 4216 4217 //Prevent using building up a stack of clicks/transtions 4218 if (isSpreadTransitioning) { 4219 return false; 4220 } 4221 //Set isSpreadTransitioning to true - indicates we're in a spread transition 4222 isSpreadTransitioning = true; 4223 4224 var currentPageURL = window.location.pathname; 4225 var siteURL = window.location.protocol + "//" + window.location.host; 4226 4227 Path = Path.replace(siteURL, ''); 4228 4229 4230 4231 if (PageTransitionLock == false) { 4232 PageTransitionLock = true; 4233 var backTransition; 4234 var forwardTransition = Transition; 4235 4236 var addToHistory = true; 4237 if (AddToHistory == false) { 4238 addToHistory = false; 4239 currentPageURL = PageURLInput; 4240 } 4241 4242 // Check if the DocState was passed in and make it blank if it wasn't 4243 if (typeof DocState === "undefined") { 4244 DocState = {}; 4245 } 4246 4247 if (Path != currentPageURL || addToHistory == false) { 4248 // Add contentonly=true so that we are not pulling back the header and footer 4249 var reqPath = Path; 4250 if (reqPath.toLowerCase().indexOf("?") >= 0) 4251 reqPath += '&'; 4252 else 4253 reqPath += '?'; 4254 reqPath += "contentonly=true&spreadonly=true"; 4255
4256 // Build up a docstate string that we can append to the Url 4257 var docStateString = getDocStateString(DocState); 4258 4259 // Add the docstate string 4260 if (docStateString !== '') { 4261 if (docStateString.charAt(0) === '?') 4262 reqPath += docStateString.replace('?', '&'); 4263 else if (docStateString.charAt(0) === '&' || docStateString.charAt(0) === '#') 4264 reqPath += docStateString; 4265 else 4266 reqPath += '&' + docStateString; 4267 } 4268 4269 var request = $.get(reqPath); 4270 request.success(function (data) { 4271 4272 Path = Path.toLowerCase(); 4273 TrackGAPageView(Path); 4274 4275 $(".NextSpread").html(data); 4276 4277 //Update breadcrumb/SEO 4278 if ($(".NextSpread #PageParams").length) { 4279 var BCHTML = $(".NextSpread #PageParams #BreadcrumbWrapper").html(); 4280 if (BCHTML.length) { 4281 UpdateBreadcrumbTrail(BCHTML); 4282 } 4283 4284 4285 var seoTitle = $(".NextSpread #PageParams #seoTitle").val(); 4286 var seoDescription = $(".NextSpread #PageParams #seoDescription").val(); 4287 var seoKeywords = $(".NextSpread #PageParams #seoKeywords").val(); 4288 var seoRobots = $(".NextSpread #PageParams #seoRobots").val(); 4289 var seoCanonical = $(".NextSpread #PageParams #seoCanonical").val(); 4290 4291 updateSeo(seoTitle, seoDescription, seoKeywords, seoRobots, seoCanonical); 4292 4293 4294 //Remove page params from page 4295 $(".NextSpread #PageParams").remove(); 4296 4297 } 4298 4299 if (addToHistory) { // If we're navigating the site rather than clicking back or forward 4300 window.history.replaceState({ "url": window.location.href, "spreadView": true, "DocState": documentState, "title": document.title, "scrollPoint": $(window).scrollTop() }, document.title, window.location.href); // On load, attempt to replace the last state object on the history stack with one that is populated with the relevent transition data (although in all likely hood this will just insert a new item) 4301 //ConsoleLog(documentState); 4302 } 4303 4304 if (Title == '') 4305 Title = seoTitle; 4306 4307 SpreadTransition(forwardTransition === "LEFT"); 4308 4309 if (addToHistory) { 4310 var historyObject 4311 if (Transition == null || Transition == "") { 4312 historyObject = { "url": Path, "title": Title, "spreadView": true, "DocState": DocState, "scrollPoint": ScrollPoint } 4313 } else { 4314 historyObject = { "url": Path, "title": Title, "transition": Transition, "spreadView": true, "DocState": DocState, "scrollPoint": ScrollPoint } 4315 } 4316 window.history.pushState(historyObject, Title, Path); 4317 } 4318 document.title = Title; 4319 PageURLInput = Path; 4320 4321 UpdateContinueShopping(Path) 4322 }); 4323 request.error(function (data) { // 404, 500 Response => To Error Page 4324 isSpreadTransitioning = false; 4325 PageTransitionLock = false; 4326 TransitionHandler("error", data, forwardTransition, Path, addToHistory, ScrollPoint, DocState); 4327 }); 4328 } else { 4329 isSpreadTransitioning = false; 4330 PageTransitionLock = false; 4331 } 4332 } 4333 return false; 4334 } else { 4335 4336 var siteURL = window.location.protocol + "//" + window.location.host; 4337 4338 Path = Path.replace(siteURL, ''); 4339 4340 window.location.pathname = Path; 4341 } 4342} 4343 4344 4345/* - Transition Handlers / Definitions ---------------------------------------------------------------------------------------------------------------------------------------------*/ 4346 4347 4348/* Available easings ---------------------------------------------------- 4349linea, swing, jswing, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, 4350easeOutCubic, easeInOutCubic, easeInQuart, easeOutQuart, easeInOutQuart, 4351easeInQuint, easeOutQuint, easeInOutQuint, easeInSine, easeOutSine, easeInOutSine, 4352easeInExpo, easeOutExpo, easeInOutExpo, easeInCirc, easeOutCirc, easeInOutCirc, 4353easeInElastic, easeOutElastic, easeInOutElastic, easeInBack, easeOutBack, easeInOutBack, 4354easeInBounce, easeOutBounce, easeInOutBounce 4355*/ 4356 4357 4358var pageTransitionSpeed = 800; 4359var leftRightTransitionSpeed = 900; 4360var pageTransitionEasing = 'easeOutCirc'; 4361var pageTransitionEasingIn = 'easeInQuint'; 4362var fadeContent = false; 4363 4364//TODO - Need to decide whether fading next content in is required 4365//In chrome it can give the appearance of a smoother transition, firefox maybe not 4366//if ($.browser.mozilla == true) { 4367// fadeContent = false; 4368//
4369//} 4370 4371//Set page up correctly for any transition 4372function PreparePageForTransition(scrollToTop) { 4373 var $ = jQuery; 4374 4375 $(window).scrollTop(0); 4376 4377 if (fadeContent) { 4378 $(".NextContent").css("position", "absolute").css("opacity", "0").css("display", "block"); 4379 } else { 4380 $(".NextContent").css("position", "absolute").css("display", "block"); 4381 } 4382 $("#ContentArea").removeAttr("style").removeClass("HideOverflow"); 4383 4384 $(".ToolBar").css("position", "fixed"); 4385 4386 var newContentHeight = $(".NextContent").height(); 4387 var ViewportHeight = $(window).height(); 4388 4389 $(".NextContent").height(ViewportHeight); 4390 4391} 4392 4393function PageTransitionGRIDTOSPREAD(scrollPoint) { 4394 var $ = jQuery; 4395 var imageLoadCalled = false; 4396 PreparePageForTransition(); 4397 4398 // Load page and image so we can get the image position/size 4399 $(".NextContent").css("top", 0).css("left", 0).css("opacity", 0); 4400 4401 var $spreadImg = $(".NextContent .ImageLayer img"); 4402 var onSpreadImageLoad = function () { 4403 if (Next.Settings.Channel.IsInnovationDesktopSpreadQuickView) { 4404 if (imageLoadCalled) { 4405 return false; 4406 } 4407 imageLoadCalled = true; 4408 } 4409 var imgOffset = $spreadImg.offset(); 4410 var spreadDimensions = { 4411 top: imgOffset.top, 4412 left: imgOffset.left, 4413 width: $spreadImg.width(), 4414 height: $spreadImg.height() 4415 }; 4416 4417 var animateElement = $spreadImg.clone(); 4418 $spreadImg.css("opacity", 0); 4419 4420 var $gridImg = $("a.selectedGrid img"); 4421 var gridImgOffset = $gridImg.offset(); 4422 4423 $(".CurrentContent").animate({ opacity: 0 }, 700); 4424 4425 $(animateElement).css("position", "absolute") 4426 .css("top", gridImgOffset.top) 4427 .css("left", gridImgOffset.left) 4428 .width($gridImg.width()) 4429 .height($gridImg.height()) 4430 .css("border", "none").css("margin", "").css("z-index", "1000"); 4431 4432 $(".NextContent").after(animateElement); 4433 4434 $(animateElement).animate(spreadDimensions, 800, "easeOutExpo"); 4435 4436 setTimeout(function () { 4437 $(".NextContent").animate({ opacity: 1 }, 600, pageTransitionEasing, function () { 4438 $(".NextContent .ImageLayer").find("img").css("opacity", 1); 4439 $(animateElement).remove() 4440 PageTransitionTidy(); 4441 }); 4442 }, 20) 4443 }; 4444 $spreadImg.load(function () { 4445 onSpreadImageLoad(); 4446 }); 4447 //Cached images and the âloadâ event issue--http://mikefowler.me/journal/2014/04/22/cached-images-load-event 4448 if (Next.Settings.Channel.IsInnovationDesktopSpreadQuickView && $spreadImg.length > 0 && isIOS) { 4449 var spreadImageElement = $spreadImg[0]; 4450 if (spreadImageElement.complete) { 4451 onSpreadImageLoad(); 4452 } 4453 } 4454} 4455 4456function PageTransitionSPREADTOGRID(scrollPoint) { 4457 var $ = jQuery; 4458 PreparePageForTransition(); 4459 $(".NextContent").css("top", 0).css("left", 0).css("opacity", 0); 4460 $(".CurrentContent .NextSpread").remove(); 4461 var srcPage = $(".CurrentContent .CurrentSpread .ImageLayer").attr("data-page"); 4462 var $spreadImage = $(".CurrentContent .CurrentSpread .ImageLayer img"); 4463 4464 var srcImg = $spreadImage.clone(); 4465 var $targetImg = $(".NextContent img[data-page='" +srcPage + "']"); 4466 4467 var targetParent = $targetImg.parent(); 4468 $(targetParent).addClass("StorySelected"); 4469 4470 var srcOffset = $spreadImage.offset(); 4471 4472 srcImg.css("position", "absolute") 4473 srcImg.offset(srcOffset); 4474 srcImg.height($spreadImage.height()); 4475 srcImg.css("z-index", 1); 4476 $(".NextContent").after(srcImg); 4477 4478 $spreadImage.animate({ opacity: 0 }, 200, function () { 4479 var $img = $(".StorySelected img"); 4480 var imgOffset; 4481 if ($img.length > 0) { 4482 imgOffset = $img.offset(); 4483 $(srcImg).animate({ 4484 top: imgOffset.top, 4485 left: imgOffset.left, 4486 width: $img.width(), 4487 height: $img.height() 4488 }, 600, "easeOutExpo"); 4489 } 4490 4491 $(".Controller").hide(); 4492 4493 $(".NextContent").animate({ opacity: 1 }, 600, "easeInExpo", function () { 4494 PageTransitionTidy(); 4495 var $storySel = $(".StorySelected"); 4496 $(srcImg).remove(); 4497 // Only scroll if necessary to bring image into view. 4498 if ($storySel.length > 0) { 4499 $("body,html").animate({ scrollTop: $storySel.offset().top + 250 - $(window).height() }, 200); 4500 } 4501 }); 4502 });
4503} 4504 4505function PageTransitionGRIDLEFT(scrollPoint) { 4506 var $ = jQuery; 4507 PreparePageForTransition(); 4508 var ViewportHeight = $(window).height(); 4509 $(".NextContent").css("top", 0).css("left", 0).css("opacity", 1).css("background-color", "transparent"); 4510 $(".NextContent .SpreadsGridTitle").css("opacity", 0); 4511 4512 $(".NextContent .SpreadGridSection").height(ViewportHeight).css("background-color", "#fff"); ; 4513 4514 $(".NextContent .SpreadGridSection").offset({ left: $(".NextContent .SpreadGridSection").offset().left + 970 }); 4515 $(".NextContent .SpreadGridSection").animate({ 4516 left: "-=" + 970 4517 }, 900, "easeOutExpo", function () { 4518 $(".NextContent .SpreadGridSection").css("height", ""); 4519 PageTransitionTidy(); 4520 4521 }); 4522 4523 // $(".CurrentContent .SpreadGridSection").animate({ 4524 // left: "-=" + 970 4525 // }, 700, "easeOutExpo"); 4526 4527 $(".NextContent .SpreadsGridTitle").animate({ opacity: 1 }, 700); 4528 $(".CurrentContent .SpreadsGridTitle").animate({ opacity: 0 }, 700); 4529 4530 4531} 4532 4533 4534function PageTransitionGRIDRIGHT(scrollPoint) { 4535 var $ = jQuery; 4536 PreparePageForTransition(); 4537 var ViewportHeight = $(window).height(); 4538 $(".NextContent").css("top", 0).css("left", 0).css("opacity", 1).css("background-color", "transparent").css("z-index", 100); 4539 $(".NextContent .SpreadsGridTitle").css("opacity", 0); 4540 $(".NextContent .SpreadGridSection").offset({ left: $(".NextContent .SpreadGridSection").offset().left }); 4541 4542 4543 4544 $(".CurrentContent .SpreadGridSection").css("background-color", "#fff").css("z-index", 101).css("height", ViewportHe
4544ight); 4545 4546 // $(".NextContent .SpreadGridSection").animate({ 4547 // left: "+=" + 970 4548 // }, 700, "easeOutExpo"); 4549 4550 $(".CurrentContent .SpreadGridSection").animate({ 4551 left: "+=" + 970 4552 }, 900, pageTransitionEasingIn, function () { 4553 PageTransitionTidy(); 4554 ScrollToPoint(scrollPoint); 4555 }); 4556 4557 $(".NextContent .SpreadsGridTitle").animate({ opacity: 1 }, 700); 4558 $(".CurrentContent .SpreadsGridTitle").animate({ opacity: 0 }, 700); 4559 4560} 4561 4562function PageTransitionUP(scrollPoint) { 4563 var $ = jQuery; 4564 PreparePageForTransition(); 4565 var ViewportHeight = $(window).height(); 4566 var HeaderHeight = $(".PageHeader").height(); 4567 $(".NextContent").css("top", ViewportHeight).css("left", "0px"); 4568 var animateJSON; 4569 4570 if (fadeContent) { 4571 animateJSON = { 4572 top: "-=" + ViewportHeight, 4573 opacity: 1 4574 }; 4575 } else { 4576 animateJSON = { 4577 top: "-=" + ViewportHeight 4578 }; 4579 4580 } 4581 4582 $(".NextContent").animate(animateJSON, pageTransitionSpeed, pageTransitionEasing, function () { 4583 PageTransitionTidy(); 4584 ScrollToPoint(scrollPoint); 4585 }); 4586} 4587 4588function PageTransitionDOWN(scrollPoint) { 4589 var $ = jQuery; 4590 PreparePageForTransition(); 4591 var ViewportHeight = $(window).height(); 4592 var HeaderHeight = $(".PageHeader").height();
4593 var ContentViewportHeight = ViewportHeight - HeaderHeight; 4594 var newContentHeight = $(".NextContent").height(); 4595 var newContentPositon = 0 - newContentHeight; 4596 4597 $(".CurrentContent").css("z-index", "1000") 4598 .css("position", "absolute") 4599 .css("overflow", "hidden") 4600 .height(ViewportHeight); 4601 4602 $(".NextContent").css("top", "0px") 4603 .css("left", "0px") 4604 .css("opacity", "1"); 4605 var animateJSON; 4606 4607 if (fadeContent) { 4608 animateJSON = { 4609 top: "+=" + ViewportHeight, 4610 opacity: 0 4611 }; 4612 } else { 4613 animateJSON = { 4614 top: "+=" + ViewportHeight 4615 }; 4616 } 4617 4618 $(".CurrentContent").animate(animateJSON, pageTransitionSpeed, pageTransitionEasingIn, function () { 4619 PageTransitionTidy(); 4620 ScrollToPoint(scrollPoint); 4621 }); 4622} 4623 4624//Page transition for going BACK - content transtions to the right, user clicked on LEFT mouse ear 4625function PageTransitionRIGHT(scrollPoint) { 4626 var $ = jQuery; 4627 PinMouseEars(); 4628 PreparePageForTransition(); 4629 4630 4631 4632 var tempClip = $("<div />"); 4633 $(tempClip).addClass("tempClip"); 4634 $(tempClip).height($(window).height()); 4635 $(tempClip).append($(".NextContent")); 4636 $(tempClip).append($(".CurrentContent")); 4637 $("#ContentArea").append(tempClip); 4638 4639 $(".NextContent .TitleBar").first().addClass("newTitle"); 4640 $(".CurrentContent .TitleBar").first().addClass("newTitle"); 4641 4642 if ($(".NextContent .TitleBar").length) { 4643 $(".NextContent .TitleBar").parent().css("margin-top", 48); 4644 } 4645 4646 if ($(".CurrentContent .TitleBar").length) { 4647 $(".CurrentContent .TitleBar").parent().css("margin-top", 48); 4648 } 4649 4650 var ViewportHeight = $(window).height(); 4651 var ViewportWidth = $(window).width(); 4652 var HeaderHeight = $(".PageHeader").height();
4653 var ContentViewportHeight = ViewportHeight - HeaderHeight; 4654 var newContentHeight = $(".NextContent").height(); 4655 var newContentWidth = $(".NextContent").width(); 4656 var newContentPositon = 0 - 976; 4657 $(".NextContent").css("left", 0).css("width", 976).css("top", "0px").css("z-index", 100); 4658 $(".CurrentContent").css("left", 0).css("width", 976).css("top", "0px").css("position", "absolute").css("z-index", 101).css("height", ViewportHeight); 4659 var animateJSON; 4660 if (fadeContent) { 4661 animateJSON = { 4662 left: "+=" + 976, 4663 opacity: 1 4664 }; 4665 } else { 4666 animateJSON = { 4667 left: "+=" + 976 4668 }; 4669 } 4670 4671 //$(".NextContent").animate(animateJSON, pageTransitionSpeed, pageTransitionEasing); 4672 4673 $(".CurrentContent").animate(animateJSON, leftRightTransitionSpeed, 'easeInCubic', function () { 4674 var cnt = $(".tempClip").contents() 4675 $(".tempClip").replaceWith(cnt); 4676 4677 PageTransitionTidy(); 4678 ScrollToPoint(scrollPoint); 4679 4680 }); 4681 4682 $(".NextContent .TitleBar").first().animate({ opacity: 1 }, 300); 4683 4684} 4685 4686function PageTransitionLEFT(scrollPoint) { 4687 var $ = jQuery; 4688 PinMouseEars(); 4689 4690 PreparePageForTransition(); 4691 4692 var tempClip = $("<div />"); 4693 4694 $(tempClip).addClass("tempClip"); 4695 $(tempClip).height($(window).height()); 4696 $(tempClip).append($(".NextContent")); 4697 $(tempClip).append($(".CurrentContent")); 4698 $("#ContentArea").append(tempClip); 4699 $(".NextContent .TitleBar").first().addClass("newTitle"); 4700 $(".CurrentContent .TitleBar").first().addClass("newTitle"); 4701 4702 if ($(".NextContent .TitleBar").length) { 4703 $(".NextContent .TitleBar").parent().css("margin-top", 48); 4704 } 4705 4706 if ($(".CurrentContent .TitleBar").length) { 4707 $(".CurrentContent .TitleBar").parent().css("margin-top", 48); 4708 } 4709 4710 4711 $("body").css("overflow-x", "hidden"); 4712 var ViewportHeight = $(window).height(); 4713 var ViewportWidth = $(window).width(); 4714 var HeaderHeight = $(".PageHeader").height();
4715 var ContentViewportHeight = ViewportHeight - HeaderHeight; 4716 var newContentHeight = $(".NextContent").height(); 4717 var newContentWidth = $(".NextContent").width(); 4718 var newContentPositon = ViewportWidth; 4719 $(".NextContent").css("left", 976).css("width", 976).css("top", "0px").css("height", ViewportHeight); 4720 $(".CurrentContent").css("left", 0).css("width", 976).css("top", "0px").css("position", "absolute"); 4721 4722 4723 4724 var animateJSON; 4725 if (fadeContent) { 4726 animateJSON = { 4727 left: "-=" + 976, 4728 opacity: 1 4729 }; 4730 } else { 4731 animateJSON = { 4732 left: "-=" + 976 4733 }; 4734 } 4735 4736 $(".NextContent").animate(animateJSON, leftRightTransitionSpeed, pageTransitionEasing, function () { 4737 var cnt = $(".tempClip").contents() 4738 $(".tempClip").replaceWith(cnt); 4739 4740 PageTransitionTidy(); 4741 ScrollToPoint(scrollPoint); 4742 }); 4743 4744 //$(".CurrentContent").animate(animateJSON, pageTransitionSpeed, pageTransitionEasing); 4745 4746 $(".NextContent .TitleBar").first().animate({ opacity: 1 }, 300); 4747 4748} 4749 4750 4751 4752function PinMouseEars() { 4753 4754 return false; 4755 var oldContent = $(".CurrentContent"); 4756 var newContent = $(".NextContent"); 4757 4758 var oldMouseEarLeft = $(".CurrentContent").find(".MouseEar.Left"); 4759 var oldMouseEarRight = $(".CurrentContent").find(".MouseEar.Right"); 4760 4761 var newMouseEarLeft = $(".NextContent").find(".MouseEar.Left"); 4762 var newMouseEarRight = $(".NextContent").find(".MouseEar.Right"); 4763 4764 var omeLPOS = $(oldMouseEarLeft).offset(); 4765 var omeRPOS = $(oldMouseEarRight).offset(); 4766 4767 $(oldMouseEarLeft).css("position", "fixed").css("left", "0px").css("top", omeLPOS.top); 4768 $(oldMouseEarRight).css("position", "fixed").css("right", "0px").css("top", omeRPOS.top); 4769 4770 $(newMouseEarLeft).css("position", "fixed").css("left", "0px").css("top", omeLPOS.top).css("opacity", "0"); 4771 $(newMouseEarRight).css("position", "fixed").css("right", "0px").css("top", omeRPOS.top).css("opacity", "0"); 4772 4773 $(oldMouseEarLeft).animate({ opacity: 0 }, 700); 4774 $(oldMouseEarRight).animate({ opacity: 0 }, 700); 4775 4776 $(newMouseEarLeft).animate({ opacity: 1 }, 700); 4777 $(newMouseEarRight).animate({ opacity: 1 }, 700); 4778 4779 4780} 4781 4782/////////////Spread transitions 4783// Copied into htmlSpreadCanvas.js 4784function SpreadTransition(left) { 4785 var $ = jQuery; 4786 4787 // Is spread expanded? 4788 var $exp = $(".SpreadsPage .Expanded:visible"); 4789 if ($exp.length) { 4790 4791 doAnimation(function () { 4792 var src = $(".ImageLayer img").attr("src"); 4793 var $img = $exp.find("img"); 4794 $img.attr("src", src); 4795 }); 4796 4797 } else { 4798 4799 // Transition Data 4800 var ContentViewportWidth = $(".SpreadCarousel").width(); 4801 var currentSpreadOffset = $(".CurrentSpread").offset(); 4802 4803 var targetOffset; 4804 if (left) 4805 targetOffset = "-=" + currentSpreadOffset.left + ContentViewportWidth; 4806 else 4807 targetOffset = "+=" + ContentViewportWidth; 4808 4809 $(".NextSpread").offset({ left: currentSpreadOffset.left }).css("z-index", 100); 4810 $(".CurrentSpread").css("z-index", 101); 4811 $(".NextSpread .ImageLayer img").load(function () { 4812 $(".CurrentSpread").animate({ left: targetOffset }, 700, 'easeInSine', doAnimation); 4813 }); 4814 4815 } 4816 4817 function doAnimation(callback) { 4818 var Animation = createAnimation(); 4819 Animation = SpreadTransitionUpdateNavigationAids(Animation) 4820 Animation = SpreadTransitionClearDown(Animation); 4821 runAnimation(Animation, callback); 4822 isSpreadTransitioning = false; 4823 PageTransitionLock = false; 4824 } 4825} 4826 4827//function SpreadTransitionLEFT() { 4828// SpreadTransition(true); 4829//} 4830 4831//function SpreadTransitionRIGHT() { 4832// SpreadTransition(false); 4833//} 4834 4835function SpreadTransitionUpdateNavigationAids(Animation) { 4836 var $ = jQuery; 4837 addToAnimation(Animation, 'ClearMouseEarStates();', 0, true); 4838 4839 if ($(".BreadcrumbTrail").html() != "") { 4840 // addToAnimation(Animation, '$(".Breadcrumbs").html($(".NextSpread .BreadcrumbTrail .Breadcrumbs").html())', 0, false); 4841 } 4842 4843 if ($(".NextSpread .NextPageURL").val() == "") { 4844 addToAnimation(Animation, '$(".MouseEar.Right").addClass("Inactive").removeClass("Active");', 0, false); 4845 addToAnimation(Animation, '$(".MouseEar.Right a")[0].href = "#";', 0, false); 4846 } else { 4847 addToAnimation(Animation, '$(".MouseEar.Right").addClass("Active").removeClass("Inactive");', 0, false); 4848 addToAnimation(Animation, '$(".MouseEar.Right a")[0].href = $(".NextSpread .NextPageURL").val();', 0, false); // 4849 } 4850 4851 if ($(".NextSpread .PreviousPageURL").val() == "") { 4852 addToAnimation(Animation, '$(".MouseEar.Left").addClass("Inactive").removeClass("Active");', 0, false); 4853 addToAnimation(Animation, '$(".MouseEar.Left a")[0].href = "#";', 0, false); 4854 } else { 4855 addToAnimation(Animation, '$(".MouseEar.Left").addClass("Active").removeClass("Inactive");', 0, false); 4856 addToAnimation(Animation, '$(".MouseEar.Left a")[0].href = $(".NextSpread .PreviousPageURL").val();', 0, false); 4857 4858 } 4859 4860 if ($(".NextSpread .NextPageIMG").val() == "") { 4861 addToAnimation(Animation, '$(".MouseEar.Right").attr("data-img","");', 0, false); 4862 } else { 4863 addToAnimation(Animation, '$(".MouseEar.Right").attr("data-img","' + $(".NextSpread .NextPageIMG").val() + '");', 0, false); 4864 } 4865 4866 if ($(".NextSpread .PreviousPageIMG").val() == "") { 4867 addToAnimation(Animation, '$(".MouseEar.Left").attr("data-img","");', 0, false); 4868 } else { 4869 addToAnimation(Animation, '$(".MouseEar.Left").attr("data-img","' + $(".NextSpread .PreviousPageIMG").val() + '");', 0, false); 4870 } 4871 4872 if ($(".NextSpread .NextPageName").val() == "") { 4873 addToAnimation(Animation, '$(".MouseEar.Right").attr("data-name","");', 0, false); 4874 } else { 4875 addToAnimation(Animation, '$(".MouseEar.Right").attr("data-name","' + $(".NextSpread .NextPageName").val() + '");', 0, false); 4876 } 4877 4878 if ($(".NextSpread .PreviousPageName").val() == "") { 4879 addToAnimation(Animation, '$(".MouseEar.Left").attr("data-name","");', 0, false); 4880 } else { 4881 addToAnimation(Animation, '$(".MouseEar.Left").attr("data-name","' + $(".NextSpread .PreviousPageName").val() + '");', 0, false); 4882 } 4883 4884 4885 return Animation; 4886} 4887 4888function SpreadTransitionClearDown(Animation, UpdateMeta) { 4889 ConsoleLog("SpreadTransitionClearDown"); 4890 var $ = jQuery; 4891 // Tidy Up Function For Page Transitions - makes sure everything is reset correctly for next time. 4892 addToAnimation(Animation, '$(".CurrentSpread").removeClass("CurrentSpread").addClass("Held");', 0, false); // Change Class Of CurrentContent To Held - It will ultimately become NextContent 4893 addToAnimation(Animation, '$(".NextSpread").removeClass("NextSpread").addClass("CurrentSpread");', 0, false); // Reassign the classes on the NextContent container to reflect it's new position (CurrentContent) 4894 addToAnimation(Animation, '$(".Held").removeClass("Held").addClass("NextSpread");', 0, true); // Reassign the classes on the Held container to reflect it's new position (NextContent) 4895 addToAnimation(Animation, '$(".NextSpread").html("");', 0, false); 4896 4897 addToAnimation(Animation, '$(".NextSpread").css("-webkit-transform", ""); ', 0, false); 4898 addToAnimation(Animation, '$(".CurrentSpread").css("-webkit-transform", ""); ', 0, false); 4899 addToAnimation(Animation, '$(".NextSpread").css("transform", ""); ', 0, false); 4900 addToAnimation(Animation, '$(".CurrentSpread").css("transform", ""); ', 0, false); 4901 addToAnimation(Animation, '$(".CurrentSpread").removeClass("animate"); ', 0, false); 4902 addToAnimation(Animation, '$(".NextSpread").removeClass("animate"); ', 0, false); 4903 4904 4905 addToAnimation(Animation, '$(".NextSpread").removeAttr("style");', 0, false); // Remove dynamically added css styles. 4906 addToAnimation(Animation, '$(".CurrentSpread").removeAttr("style");', 0, false); // Remove dynamically added css styles. 4907 //addToAnimation(Animation, 'PageTransitionLock = false;', 0, true); 4908 4909 addToAnimation(Animation, 'AddMouseEarHoverStates();', 0, true); 4910 4911 if (UpdateMeta) { 4912 addToAnimation(Animation, 'makeSpreadDraggable(false, true);', 0, true); 4913 } else { 4914 addToAnimation(Animation, 'makeSpreadDraggable();', 0, true); 4915 } 4916 4917 addToAnimation(Animation, 'selectCurrentSpreadThumbnail();', 0, true); 4918 addToAnimation(Animation, 'setGridViewUrl();', 0, true); 4919 4920 4921 4922 return Animation; 4923} 4924 4925 4926function PageTransitionTidy() { 4927 var $ = jQuery; 4928 $("body").css("overflow-x", ""); 4929 4930 var oldContent = $(".CurrentContent"); 4931 var newContent = $(".NextContent"); 4932 4933 $(oldContent).html("").removeClass("CurrentContent") 4934 .addClass("NextContent") 4935 .css("position", "absolute") 4936 .css("display", "none") 4937 .css("opacity", "") 4938 .css("width", "") 4939 .css("overflow", "") 4940 .css("z-index", "") 4941 .css("height", ""); 4942 4943 $(newContent).addClass("CurrentContent") 4944 .removeClass("NextContent") 4945 .css("position", "") 4946 .css("opacity", "") 4947 .css("width", "") 4948 .css("top", "") 4949 .css("left", "") 4950 .css("height", "") 4951 .css("background-color", "#fff"); 4952 4953 $(".CurrentContent .TitleBar").parent().css("margin-top", 0); 4954 4955 $(".ProductImagery").css("position", "").css("margin-top", ""); 4956 $("#ContentArea").removeAttr("style").removeClass("HideOverflow").addClass("LargeHeader"); 4957 4958 AddMouseEarTouchEvents(); 4959 4960 RemoveDynamicResources(); 4961} 4962 4963function PageTransitionClearDown(Animation, scrollPoint) { 4964 var $ = jQuery; 4965 // Tidy Up Function For Page Transitions - makes sure everything is reset correctly for next time. 4966 4967 //Potential to do something with ContentArea here to make the transition to the scroll point better? 4968 addToAnimation(Animation, '$(".CurrentContent").removeClass("CurrentContent").addClass("Held");', 0, false); // Change Class Of CurrentContent To Held - It will ultimately become NextContent 4969 addToAnimation(Animation, '$(".NextContent").removeClass("NextContent").addClass("CurrentContent").addClass("Backgroun
4969dWhite");', 0, false); // Reassign the classes on the NextContent container to reflect it's new position (CurrentContent) 4970 addToAnimation(Animation, '$(".Held").removeClass("Held").addClass("NextContent");', 0, true); // Reassign the classes on the Held container to reflect it's new position (NextContent) 4971 addToAnimation(Animation, '$(".NextContent").html("");', 0, false); // Remove Content From NextContent Container 4972 addToAnimation(Animation, '$(".NextContent").removeAttr("style");', 0, false); // Remove dynamically added css styles. 4973 addToAnimation(Animation, '$(".CurrentContent").removeAttr("style");', 0, false); // Remove dynamically added css styles. 4974 //addToAnimation(Animation, 'ResizeContentAreaToContent(' + scrollPoint + ')', 0, false); 4975 4976 addToAnimation(Animation, 'Resize(".ContentArea", "Y", $(".CurrentContent").height() + "px", 500, "")', 500, true); // The content bottom is not visible, so we cna shorten the transition by just snapping it in to place. 4977 // For Toolbar 4978 //addToAnimation(Animation, 'toggleFixedFooterToolbar("transition");', 200, true); // ...and switch the footer toolbar to fixed mode. 4979 4980 addToAnimation(Animation, 'RemoveDynamicResources();', 0, false); 4981 //addToAnimation(Animation, 'PageTransitionComplete();', 0, true); 4982 4983 // addToAnimation(Animation, '$(".CurrentContent").removeClass("Backgroun
4983dWhite");', 100, true); 4984 4985 4986 return Animation; 4987} 4988 4989function ResizeContentAreaToContent(scrollPoint) { 4990 return false; 4991 var scrolltimer = 500; 4992 var CCHeight = $(".CurrentContent").height(); 4993 var CAHeight = $(".ContentArea").height(); 4994 var CAMinHeight = parseInt($(".ContentArea").css('min-height')); 4995 var CAHeightDiff 4996 var timer 4997 4998 if (CCHeight >= CAHeight) { 4999 if (CAMinHeight > CAHeight) { 5000 CAHeight = CAMinHeight; 5001 } 5002 CAHeightDiff = CCHeight - CAHeight 5003 ContentResizeComplete("+", CAHeightDiff, scrolltimer, scrollPoint); 5004 } else { 5005 CAHeightDiff = CAHeight - CCHeight 5006 var newContentHeight = $(".PageHeader").height() + CCHeight + $(".FooterPromotions").height(); 5007 if (newContentHeight < $(window).height()) { 5008 unPinFooterToolbar('transition'); 5009 timer = window.setTimeout(function () { ContentResizeComplete("-", CAHeightDiff, scrolltimer, scrollPoint); }, 300) // Wait for animation to complete, then continue 5010 } else { 5011 ContentResizeComplete("-", CAHeightDiff, scrolltimer, scrollPoint); 5012 } 5013 } 5014} 5015 5016function ContentResizeComplete(animationDirection, animationDistance, animationDuration, scrollPoint) { 5017 5018 //$(".ContentArea").animate({ "height": animationDirection + "=" + animationDistance + "px" }, animationDuration); 5019 timer = window.setTimeout(function () { ResetScrollPoint(scrollPoint) }, animationDuration + 10) // Wait for animation to complete, then continue 5020} 5021 5022function ResetScrollPoint(scrollPoint) { 5023 $(".CurrentContent").scrollTop(1); // Match content's scroll point to the window scrollpoint and... 5024 $(window).scrollTop(scrollPoint); 5025 // Reset the window's scroll point to 0 - this ensures that the new page's content starts at the top, rather than at the scroll point from the previous page! 5026} 5027 5028function ToggleExpander(target, ExpanderID) { 5029 ToggleExpanderResize(target, "Expanded"); 5030 5031 if (!$(target).hasClass("Expanded")) { 5032 5033 if (!$(target).is("#CategoryViewport")) { 5034 5035 if ($(ExpanderID + "Img")) { 5036 $(ExpanderID + "Img").attr("src", $(ExpanderID + "Img").attr("src").toLowerCase().replace("down", "up")); 5037 } 5038 if ($(ExpanderID + "Text")) { 5039 $(ExpanderID + "Text").text($(ExpanderID + "Text").text().replace("More", "Less")); 5040 } 5041 5042 } 5043 5044 if ($(target).hasClass("ExpansionFinished")) { 5045 5046 if ($(ExpanderID + "Img")) { 5047 $(ExpanderID + "Img").attr("src", $(ExpanderID + "Img").attr("src").toLowerCase().replace("down", "up")); 5048 } 5049 if ($(ExpanderID + "Text")) { 5050 $(ExpanderID + "Text").text($(ExpanderID + "Text").text().replace("More", "Less")); 5051 } 5052 5053 $(target).removeClass("ExpansionFinished"); 5054 } 5055 5056 addParamToDocState(target, 'Expanded'); 5057 } else { 5058 5059 if ($(ExpanderID + "Img")) { 5060 $(ExpanderID + "Img").attr("src", $(ExpanderID + "Img").attr("src").toLowerCase().replace("up", "down")); 5061 } 5062 if ($(ExpanderID + "Text")) { 5063 $(ExpanderID + "Text").text($(ExpanderID + "Text").text().replace("Less", "More")); 5064 } 5065 addParamToDocState(target, ''); 5066 } 5067} 5068 5069function ToggleExpanderResize(target, toggleClass) { 5070 var value 5071 5072 5073 if (!$(target).hasClass(toggleClass)) { 5074 5075 if ($(target).is("#CategoryViewport")) { 5076 var maxHeight = $(target).children().height(); 5077 var unitHeight = $(target).find("article").first().height(); 5078 var unitMargin = parseInt($(target).find("article").first().css("margin-top")) * 2; 5079 var curentHeight = $(target).height(); 5080 var baseAdjust = parseInt($(target).css("min-height")) - (unitHeight + unitMargin); 5081 var hiddenUnitsLeft = parseInt((maxHeight - (curentHeight - baseAdjust)) / (unitHeight + unitMargin)); 5082 5083 if (unitHeight * 2 > curentHeight && unitHeight * 2 <= maxHeight) { 5084 5085 //add two more rows 5086 value = curentHeight + ((unitHeight + unitMargin) * 2); 5087 } 5088 else { 5089 5090 //add more rows based on where we are in the visible and hidden grid 5091 5092 if (hiddenUnitsLeft > 3) { 5093 hiddenUnitsLeft = 3; 5094 } 5095 5096 // calcualte the new height to show 5097 value = curentHeight + ((unitHeight + unitMargin) * hiddenUnitsLeft); 5098 } 5099 5100 // chack to see if we are at the end of the collection 5101 if (value > maxHeight) { 5102 $(target).addClass("ExpansionFinished"); 5103 } 5104 5105 if (value < maxHeight) { 5106 toggleClass = ""; 5107 } 5108 5109 } 5110 else { 5111 value = $(target).children(1).height(); 5112 } 5113 } else { 5114 value = parseInt($(target).css("min-height")); 5115 } 5116 5117 ResizeExpander(target, value, 500, toggleClass); 5118} 5119 5120function ResizeExpander(target, value, duration, toggleClass) { 5121 var Animation = createAnimation(); 5122 var newHeight = $(".ContentArea").height() - $(target).height() + value; 5123 5124 addToAnimation(Animation, '$("' + target + '").animate({"height": "' + value + '"}, ' + duration + ')', 0, false); 5125 addToAnimation(Animation, '$(".ContentArea").animate({"height": "' + newHeight + '"}, ' + duration + ')', duration, true); 5126 5127 if (toggleClass != '') { 5128 if (!$(target).hasClass(toggleClass)) { 5129 addToAnimation(Animation, '$("' + target + '").addClass("' + toggleClass + '")', 0, false); 5130 } else { 5131 addToAnimation(Animation, '$("' + target + '").removeClass("' + toggleClass + '")', 0, false); 5132 } 5133 } 5134 5135 runAnimation(Animation); 5136} 5137 5138function ToggleResize(target, axis, toggleClass) { 5139 var newvalue 5140 var startvalue 5141 5142 if (axis == "Y") { 5143 newvalue = $("#" + target).children(1).height() 5144 startvalue = parseInt($("#" + target).css("min-height")); 5145 } 5146 if (axis == "X") { 5147 newvalue = $("#" + target).children(1).width() 5148 startvalue = parseInt($("#" + target).css("min-width")); 5149 } 5150 5151 if (!$("#" + target).hasClass(toggleClass)) { 5152 Resize("#" + target, axis, newvalue, 500, toggleClass); 5153 } else { 5154 Resize("#" + target, axis, startvalue, 500, toggleClass); 5155 } 5156} 5157 5158function Resize(target, axis, value, duration, toggleClass) { 5159 var Animation = createAnimation(); 5160 var dimension 5161 switch (axis) { 5162 case "Y": 5163 dimension = "height"; 5164 break; 5165 case "X": 5166 dimension = "width"; 5167 break; 5168 } 5169 5170 addToAnimation(Animation, '$("' + target + '").animate({"' + dimension + '": "' + value + '"}, ' + duration + ')', duration, true); 5171 5172 if (toggleClass != '') { 5173 if (!$("" + target).hasClass(toggleClass)) { 5174 addToAnimation(Animation, '$("' + target + '").addClass("' + toggleClass + '")', 0, false); 5175 } else { 5176 addToAnimation(Animation, '$("' + target + '").removeClass("' + toggleClass + '")', 0, false); 5177 } 5178 addToAnimation(Animation, '$("' + target + '").removeAttr("style")', 0, false); 5179 } 5180 5181 //addToAnimation(Animation, 'ResizeContentAreaToContent(' + $(window).scrollTop() + ')', 0, false); 5182 5183 runAnimation(Animation); 5184} 5185 5186function ToggleView(View1, View2, Trigger, DocStateParam) { 5187 var Animation = createAnimation(); 5188 var scrollTimer = 500 5189 var ToggleClass = 'Hide' 5190 5191 if ($(View1.className).hasClass(ToggleClass)) { 5192 addToAnimation(Animation, '$(".' + View2.className + '").fadeOut(' + scrollTimer + ')', scrollTimer, true); 5193 addToAnimation(Animation, '$("' + View2.className + '").addClass("FadedOut")', 0, false); 5194 addToAnimation(Animation, '$("' + View1.className + '").removeClass("' + ToggleClass + '")', 0, false); 5195 addToAnimation(Animation, '$("' + View2.className + '").addClass("' + ToggleClass + '")', 0, false); 5196 addToAnimation(Animation, '$(".' + View1.className + '").fadeIn(' + scrollTimer + ');', scrollTimer, true); 5197 addToAnimation(Animation, '$("' + View1.className + '").removeClass("FadedOut")', 0, false); 5198 addToAnimation(Animation, '$("' + View1.className + '").removeAttr("style")', 0, false); 5199 addToAnimation(Animation, '$("' + View2.className + '").removeAttr("style")', 0, false); 5200 if (Trigger.TextID && Trigger.TextID != "" && View2.trigger.text && View2.trigger.text != '') { 5201 addToAnimation(Animation, '$("#' + Trigger.TextID + '").text("' + View2.trigger.text + '")', 0, false); 5202 } 5203 5204 if (DocStateParam != '') { 5205 if (View1.DocStateValue) { 5206 addParamToDocState(DocStateParam, View1.DocStateValue) 5207 } else { 5208 addParamToDocState(DocStateParam, '') 5209 } 5210 } 5211 } else { 5212 addToAnimation(Animation, '$(".' + View1.className + '").fadeOut(' + scrollTimer + ')', scrollTimer, true); 5213 addToAnimation(Animation, '$("' + View1.className + '").addClass("FadedOut")', 0, false); 5214 addToAnimation(Animation, '$("' + View2.className + '").removeClass("Hide")', 0, false); 5215 addToAnimation(Animation, '$("' + View1.className + '").addClass("Hide")', 0, false); 5216 addToAnimation(Animation, '$(".' + View2.className + '").fadeIn(' + scrollTimer + ')', scrollTimer, true); 5217 addToAnimation(Animation, '$("' + View2.className + '").removeClass("FadedOut")', 0, false); 5218 addToAnimation(Animation, '$("' + View1.className + '").removeAttr("style")', 0, false); 5219 addToAnimation(Animation, '$("' + View2.className + '").removeAttr("style")', 0, false); 5220 if (Trigger.TextID && Trigger.TextID != "" && View1.trigger.text && View1.trigger.text != '') { 5221 addToAnimation(Animation, '$("#' + Trigger.TextID + '").text("' + View1.trigger.text + '")', 0, false); 5222 } 5223 if (DocStateParam != '') { 5224 if (View2.DocStateValue) { 5225 addParamToDocState(DocStateParam, View2.DocStateValue) 5226 } else { 5227 addParamToDocState(DocStateParam, '') 5228 } 5229 } 5230 } 5231 5232 runAnimation(Animation); 5233} 5234 5235/*function ZoomTarget(e) { 5236if ($(".ProductViewer").hasClass("ShowZoom")) { 5237} else { 5238return false; 5239} 5240}*/ 5241; 5242var StateConvergence = /** @class */ (function () { 5243 function StateConvergence() { 5244 } 5245 StateConvergence.prototype.GetStateConvergenceCookie = function () { 5246 var cookieName = Next.Settings.Channel.StateConvergence.CookieName; 5247 var cookieValue = {}; 5248 var stateConvergenceCookie = readCookie(cookieName); 5249 if (stateConvergenceCookie != null) { 5250 var splitVars = stateConvergenceCookie.split("&"); 5251 for (var i = 0; i < splitVars.length; i++) { 5252 var splitKvp = splitVars[i].split("="); 5253 if (splitKvp != null) { 5254 cookieValue[splitKvp[0]] = splitKvp[1]; 5255 } 5256 } 5257 } 5258 return cookieValue; 5259 }; 5260 StateConvergence.prototype.GetAgentId = function () { 5261 var cookieAgentIdKey = Next.Settings.Channel.StateConvergence.CookieAgentIDKey; 5262 var cookieValue = this.GetStateConvergenceCookie(); 5263 return cookieValue[cookieAgentIdKey]; 5264 }; 5265 StateConvergence.prototype.GetConvergedFlag = function () { 5266 var cookieConvergedFlagKey = Next.Settings.Channel.StateConvergence.CookieConvergedFlagKey; 5267 var cookieValue = this.GetStateConvergenceCookie(); 5268 return cookieValue[cookieConvergedFlagKey] === "True"; 5269 }; 5270 StateConvergence.prototype.StripTrailingForwardSlash = function (url) { 5271 var lastForwardSlashIndex = url.lastIndexOf("/");
5272 var lastIndexOfUrl = url.length - 1; 5273 var output; 5274 if (lastForwardSlashIndex === lastIndexOfUrl) { 5275 output = url.substring(0, lastForwardSlashIndex); 5276 } 5277 else { 5278 output = url; 5279 } 5280 return output; 5281 }; 5282 StateConvergence.prototype.FromEndpoint = function (endpoint, agentId) { 5283 var formattedEndpoint = this.StripTrailingForwardSlash(endpoint); 5284 var formattedOrigin = this.StripTrailingForwardSlash(window.location.origin); 5285 if (formattedEndpoint !== formattedOrigin) { 5286 var pixelEndpoint = endpoint + "/StateConvergence/Pixel/" + agentId; 5287 var pixelImage = new Image(); 5288 pixelImage.src = pixelEndpoint; 5289 } 5290 }; 5291 StateConvergence.prototype.Execute = function () { 5292 var convergedFlag = this.GetConvergedFlag(); 5293 if (convergedFlag) { 5294 return; 5295 } 5296 var convergedDomains = Next.Settings.Channel.StateConvergence.Domains; 5297 var agentId = this.GetAgentId(); 5298 if (agentId && convergedDomains.length) { 5299 for (var i = 0; i < convergedDomains.length; i++) { 5300 var endpoint = convergedDomains[i]; 5301 this.FromEndpoint(endpoint, agentId); 5302 } 5303 } 5304 }; 5305 return StateConvergence; 5306}()); 5307var NextStateConvergence = new StateConvergence(); 5308//# sourceMappingURL=StateConvergenceCommon.js.map; 5309// globals we reference 5310// todo supply interface for consumers, rather than letting them randomly add to our data structure? 5311var rebuildDataLayer = true; 5312var setUpGA = true; 5313var itemNumberAdded = ""; 5314var guestAccountConverted = false; 5315var platmodCommon; 5316(function (platmodCommon) { 5317 var w = window; 5318 var NextUnlimitedModel = /** @class */ (function () { 5319 function NextUnlimitedModel() { 5320 this.IsEnabled = false; 5321 this.IsExistingNUCustomer = false; 5322 this.ShowAlreadyNUCustomerBanner = false; 5323 this.ShowBelowMinimumOrderValueBanner = false; 5324 this.AmountToSatisfyMinimumOrderValue = ""; 5325 this.ShowAlreadyNUCustomerNURemovedAlert = false; 5326 this.NextUnlimitedItemNumbers = []; 5327 } 5328 NextUnlimitedModel.prototype.Update = function (data) { 5329 this.IsEnabled = data.IsEnabled; 5330 this.IsExistingNUCustomer = data.IsExistingNUCustomer; 5331 this.ShowAlreadyNUCustomerBanner = data.ShowAlreadyNUCustomerBanner; 5332 this.ShowBelowMinimumOrderValueBanner = data.ShowBelowMinimumOrderValueBanner; 5333 this.AmountToSatisfyMinimumOrderValue = data.AmountToSatisfyMinimumOrderValue; 5334 this.ShowAlreadyNUCustomerNURemovedAlert = data.ShowAlreadyNUCustomerNURemovedAlert; 5335 if (data.ItemNumbersWithoutHyphen && data.ItemNumbersWithoutHyphen.length > 0) { 5336 this.NextUnlimitedItemNumbers = data.ItemNumbersWithoutHyphen; 5337 } 5338 }; 5339 return NextUnlimitedModel; 5340 }()); 5341 var NextBasketData = /** @class */ (function () { 5342 function NextBasketData() { 5343 this.Version = 0; 5344 this.ItemCount = 0; 5345 } 5346 return NextBasketData; 5347 }()); 5348 var SessionTransferVariables = /** @class */ (function () { 5349 function SessionTransferVariables() { 5350 this.GAAccount = ''; 5351 this.GASessionId = ''; 5352 this.HomeUrl = ''; 5353 this.IgnoreGA = true; 5354 this.SearchUrl = ''; 5355 this.ShoppingBagUrl = ''; 5356 this.GAEnabledForGTM = false; 5357 this.GAAccountForGTM = ''; 5358 } 5359 return SessionTransferVariables; 5360 }()); 5361 var Basket = /** @class */ (function () { 5362 function Basket() { 5363 this.VisitorID = ''; 5364 this.SessionID = ''; 5365 this.Data = new NextBasketData(); 5366 this.NotifyChange = []; 5367 this.NotifyError = []; 5368 this.Loaded = false; 5369 this.UsingKeepAlive = false; 5370 this.KeepAlivePollTime = 60000; 5371 this.HostURL = ""; 5372 this.InboundQueue = []; 5373 this.OutboundQueue = []; 5374 this.ItemsWaiting = false; 5375 this.NotifyWaiting = false; 5376 this.SessionTransferVariables = new SessionTransferVariables();
5377 this.NextUnlimited = new NextUnlimitedModel(); 5378 this.RoamingProfileId = ""; 5379 this.BrowseOnlyMode = false; 5380 this.MobilePMeterAnimationEnabled = Next.Settings.Channel.BagIconShutterAnimation; 5381 this.TriggerMobilePMeterAnimation = false; 5382 this.DesktopPMeterAnimationEnabled = Next.Settings.Channel.BagIconShutterAnimation; 5383 this.TriggerDesktopPMeterAnimation = false; 5384 // todo pass baseUrl to ctor 5385 // Use baseURL if it exists (used by AccountPortal to post to MVC app). 5386 if (typeof baseURL != 'undefined') { 5387 this.HostURL = baseURL; 5388 } 5389 // the header is not there at this point so we don't want to do this 5390 // Load -> LoadSuccess calls UpdateBagUI anyway 5391 //this.UpdateBagUI(this); 5392 } 5393 Basket.prototype.SubscribeToEvents = function () { 5394 var _this = this; 5395 if (typeof w.subjects !== "undefined") { 5396 w.subjects["$ SHOPPING_BAG_GET_CALLBACK"].subscribe(function (eventResponse) { 5397 var jqXHR = { 5398 getResponseHeader: function () { return false; }, 5399 status: eventResponse.status 5400 }; 5401 if (eventResponse.success) { 5402 var data = eventResponse.data; 5403 if (data.redirect === "true") { 5404 _this.ConfirmRedirectModal(data.cancelUrl); 5405 } 5406 else { 5407 _this.LoadSuccess(data, eventResponse.textStatus, jqXHR, 'load'); 5408 } 5409 } 5410 else { 5411 _this.LoadError(jqXHR, eventResponse.textStatus, null, 'load'); 5412 } 5413 }); 5414 } 5415 }; 5416 // these methods should be overridden in the logically-concrete subclass (this class is logically abstract, but we do not enforced that to keep things simple) 5417 //todo enforce these are overridden? 5418 Basket.prototype.UpdateBagUI = function (basket) { }; 5419 Basket.prototype.PriceDial = function (bag) { }; 5420 Basket.prototype.Notify = function (messageObj) { }; 5421 Basket.prototype.DisplayRebuildNotification = function (messageObj) { }; 5422 Basket.prototype.NotifyItemAdded = function (itemObj) { }; 5423 Basket.prototype.NotifyGAAccount = function (GAAccountToUse, GASessionId, GAEnabled) { }; 5424 Basket.prototype.NotifyGAAccountForGTM = function (GAAccountForGTM, GASessionIdForGTM, GAEnabledForGTM) { }; 5425 //Executes bag actions 5426 Basket.prototype.ExecuteBagAction = function (actionParams) { 5427 var _this = this; 5428 if (this.SessionID == '') { 5429 return; 5430 } 5431 var eventId = Date.now().toString(); 5432 switch (actionParams.actionPath) { 5433 case "bag/add": 5434 var addBagSubscription = w.subjects["$ SHOPPING_BAG_ADD_CALLBACK"].subscribe(function (eventResponse) { 5435 if (eventId === eventResponse.eventId) { 5436 _this.EventCallbackHandler(eventResponse, actionParams, addBagSubscription); 5437 } 5438 }); 5439 var addBagEventData = { 5440 eventId: eventId, 5441 id: actionParams.postData.id, 5442 optionNumber: actionParams.postData.option, 5443 chain: actionParams.postData.chain, 5444 quantity: actionParams.postData.quantity, 5445 modalItemUrl: actionParams.postData.modalItemUrl, 5446 saleClearanceSofaImage: actionParams.postData.SaleClearanceSofaImage, 5447 itemNumberAdded: itemNumberAdded 5448 }; 5449 w.subjects["$ SHOPPING_BAG_ADD"].next(addBagEventData); 5450 return; 5451 case "bag/AddCist": 5452 var addCistSubscription = w.subjects["$ SHOPPING_BAG_ADD_CIST_CALLBACK"].subscribe(function (eventResponse) { 5453 if (eventId === eventResponse.eventId) { 5454 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5455 } 5456 }); 5457 var addCistEventData = { 5458 eventId: eventId, 5459 id: actionParams.postData.id, 5460 optionNumber: actionParams.postData.option, 5461 chain: actionParams.postData.chain, 5462 quantity: actionParams.postData.quantity, 5463 cistStoreId: actionParams.postData.ciststoreid, 5464 }; 5465 w.subjects["$ SHOPPING_BAG_ADD_CIST"].next(addCistEventData); 5466 return; 5467 case "bag/addevoucher": 5468 var addCistSubscription = w.subjects["$ SHOPPING_BAG_ADD_EVOUCHER_CALLBACK"].subscribe(function (eventResponse) { 5469 if (eventId === eventResponse.eventId) { 5470 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5471 } 5472 }); 5473 var addEvoucherEventData = { 5474 eventId: eventId, 5475 id: actionParams.postData.id, 5476 option: actionParams.postData.option, 5477 optionNumber: actionParams.postData.option, 5478 eVoucherTo: actionParams.postData.eVoucherTo, 5479 eVoucherFrom: actionParams.postData.eVoucherFrom, 5480 eVoucherEmail: actionParams.postData.eVoucherEmail, 5481 eVoucherMessage: actionParams.postData.eVoucherMessage,
5482 ecardDeliveryDate: actionParams.postData.ecardDeliveryDate, 5483 }; 5484 w.subjects["$ SHOPPING_BAG_ADD_EVOUCHER"].next(addEvoucherEventData); 5485 return; 5486 case "bag/addgiftcard": 5487 var addGCSubscription = w.subjects["$ SHOPPING_BAG_ADD_GIFT_CARD_CALLBACK"].subscribe(function (eventResponse) { 5488 if (eventId === eventResponse.eventId) { 5489 _this.EventCallbackHandler(eventResponse, actionParams, addGCSubscription); 5490 } 5491 }); 5492 var addGiftCardEventData = { 5493 eventId: eventId, 5494 id: actionParams.postData.id, 5495 amountOption: actionParams.postData.amountOption, 5496 firstName: actionParams.postData.firstName, 5497 lastName: actionParams.postData.lastName, 5498 addressLine1: actionParams.postData.addressLine1, 5499 addressLine2: actionParams.postData.addressLine2, 5500 addressLine3: actionParams.postData.addressLine3, 5501 addressLine4: actionParams.postData.addressLine4, 5502 addressLine5: actionParams.postData.addressLine5, 5503 postCode: actionParams.postData.postCode, 5504 message: actionParams.postData.message, 5505 deliveryDate: actionParams.postData.deliveryDate, 5506 deliveryOption: actionParams.postData.deliveryOption, 5507 deliveryCharge: actionParams.postData.deliveryCharge, 5508 mediacode: actionParams.postData.mediacode 5509 }; 5510 w.subjects["$ SHOPPING_BAG_ADD_GIFT_CARD"].next(addGiftCardEventData); 5511 return; 5512 case "bag/addlinkeditem": 5513 var addCistSubscription = w.subjects["$ SHOPPING_BAG_ADD_LINKED_ITEM_CALLBACK"].subscribe(function (eventResponse) { 5514 if (eventId === eventResponse.eventId) { 5515 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5516 } 5517 }); 5518 var addLinkedItemEventData = { 5519 eventId: eventId, 5520 id: actionParams.postData.id, 5521 optionNumber: actionParams.postData.option, 5522 linkedItemId: actionParams.postData.linkeditem, 5523 linkedItemOption: actionParams.postData.linkeditemoption, 5524 itemNumberAdded: itemNumberAdded 5525 }; 5526 w.subjects["$ SHOPPING_BAG_ADD_LINKED_ITEM"].next(addLinkedItemEventData); 5527 return; 5528 case "bag/addmultiple": 5529 var addCistSubscription = w.subjects["$ SHOPPING_BAG_ADD_MULTIPLE_CALLBACK"].subscribe(function (eventResponse) { 5530 if (eventId === eventResponse.eventId) { 5531 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5532 } 5533 }); 5534 var addMultipleEventData = { 5535 eventId: eventId, 5536 data: actionParams.postData.data, 5537 pageUrl: actionParams.postData.pageurl, 5538 deliveryWeeks: actionParams.postData.deliveryweeks, 5539 isGrouped: actionParams.postData.grouped, 5540 groupType: actionParams.postData.groupType 5541 }; 5542 w.subjects["$ SHOPPING_BAG_ADD_MULTIPLE"].next(addMultipleEventData); 5543 return; 5544 case "bag/addwarranty": 5545 var addCistSubscription = w.subjects["$ SHOPPING_BAG_ADD_WARRANTY_CALLBACK"].subscribe(function (eventResponse) { 5546 if (eventId === eventResponse.eventId) { 5547 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5548 } 5549 }); 5550 var addWarrantyEventData = { 5551 eventId: eventId, 5552 id: actionParams.postData.id, 5553 optionNumber: actionParams.postData.option, 5554 warrantyId: actionParams.postData.warranty, 5555 warrantyOptionNumber: actionParams.postData.warrantyoptionno, 5556 }; 5557 w.subjects["$ SHOPPING_BAG_ADD_WARRANTY"].next(addWarrantyEventData); 5558 return; 5559 case "bag/remove": 5560 var addCistSubscription = w.subjects["$ SHOPPING_BAG_REMOVE_CALLBACK"].subscribe(function (eventResponse) { 5561 if (eventId === eventResponse.eventId) { 5562 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5563 } 5564 }); 5565 var removeEventData = { 5566 eventId: eventId, 5567 id: actionParams.postData.id, 5568 customItemType: actionParams.postData.customItemType
5569 }; 5570 w.subjects["$ SHOPPING_BAG_REMOVE"].next(removeEventData); 5571 return; 5572 case "bag/reject": 5573 var addCistSubscription = w.subjects["$ SHOPPING_BAG_REJECT_CALLBACK"].subscribe(function (eventResponse) { 5574 if (eventId === eventResponse.eventId) { 5575 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5576 } 5577 }); 5578 var rejectEventData = { 5579 eventId: eventId, 5580 id: actionParams.postData.id, 5581 }; 5582 w.subjects["$ SHOPPING_BAG_REJECT"].next(rejectEventData); 5583 return; 5584 case "bag/accept": 5585 var addCistSubscription = w.subjects["$ SHOPPING_BAG_ACCEPT_CALLBACK"].subscribe(function (eventResponse) { 5586 if (eventId === eventResponse.eventId) { 5587 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5588 } 5589 }); 5590 var acceptEventData = { 5591 eventId: eventId, 5592 id: actionParams.postData.id, 5593 }; 5594 w.subjects["$ SHOPPING_BAG_ACCEPT"].next(acceptEventData); 5595 return; 5596 case "bag/addSample": 5597 var addCistSubscription = w.subjects["$ SHOPPING_BAG_ADD_SAMPLE_CALLBACK"].subscribe(function (eventResponse) { 5598 if (eventId === eventResponse.eventId) { 5599 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5600 } 5601 }); 5602 var addSampleEventData = { 5603 eventId: eventId, 5604 items: actionParams.postData.items, 5605 }; 5606 w.subjects["$ SHOPPING_BAG_ADD_SAMPLE"].next(addSampleEventData); 5607 return; 5608 case "bag/removeSample": 5609 var addCistSubscription = w.subjects["$ SHOPPING_BAG_REMOVE_SAMPLE_CALLBACK"].subscribe(function (eventResponse) { 5610 if (eventId === eventResponse.eventId) { 5611 _this.EventCallbackHandler(eventResponse, actionParams, addCistSubscription); 5612 } 5613 }); 5614 var removeSampleEventData = { 5615 eventId: eventId, 5616 id: actionParams.postData.id, 5617 }; 5618 w.subjects["$ SHOPPING_BAG_REMOVE_SAMPLE"].next(removeSampleEventData); 5619 return; 5620 default: 5621 var jqXHR = { 5622 getResponseHeader: function () { return false; }, 5623 status: "Error" 5624 }; 5625 console.log("ERROR UNHANDLED BAG EVENT - " + actionParams.actionPath); 5626 this.LoadError(jqXHR, "500", "ERROR UNHANDLED BAG EVENT - " + actionParams.actionPath, actionParams.actionType, actionParams.callBack, actionParams.dataLoadCallback); 5627 break; 5628 } 5629 }; 5630 Basket.prototype.EventCallbackHandler = function (eventResponse, actionParams, subscription) { 5631 console.log("SHOPPING_BAG_CALLBACK", actionParams, eventResponse); 5632 var jqXHR = { 5633 getResponseHeader: function () { return false; }, 5634 status: eventResponse.status 5635 }; 5636 if (eventResponse.success) { 5637 if (this.MobilePMeterAnimationEnabled) { 5638 this.TriggerMobilePMeterAnimation = true; 5639 } 5640 if (this.DesktopPMeterAnimationEnabled) { 5641 this.TriggerDesktopPMeterAnimation = true; 5642 } 5643 this.LoadSuccess(eventResponse.data, eventResponse.textStatus, jqXHR, actionParams.actionType, actionParams.loadBasketPage, actionParams.callBack, actionParams.dataLoadCallback); 5644 } 5645 else { 5646 this.LoadError(jqXHR, eventResponse.textStatus, null, actionParams.actionType, actionParams.callBack, actionParams.dataLoadCallback); 5647 } 5648 subscription.unsubscribe(); 5649 }; 5650 //All bag actions are passed here and added to the queue of actions 5651 Basket.prototype.AddBagAction = function (actionParams) {
5652 //Add action to inbound queue 5653 this.InboundQueue.push(actionParams); 5654 //If ItemsWaiting is set to false, we have not yet begun to process the items in the Inbound queue 5655 //Need to kick it off here 5656 if (!this.ItemsWaiting) { 5657 this.ItemsWaiting = true; 5658 //Get (and remove) first item from queue and send it off to be processed 5659 //In most cases we will be using the item we just added but if user has clicked 'add' multiple times, 5660 //we can loop through the items one by one 5661 var firstAction = this.InboundQueue.shift(); 5662 this.ExecuteBagAction(firstAction); 5663 } 5664 if (!Next.Settings.Channel.PlatModGoogleAnalyticsSDK) { 5665 if (actionParams.postData != undefined) { 5666 if (actionParams.actionType != undefined && (actionParams.actionType === "add" || actionParams.actionType === "addEvoucher" || actionParams.actionType === "addgiftcard")) { 5667 var products = ""; 5668 var itemNumber = ""; 5669 var sku = ""; 5670 if (actionParams.postData.id != undefined) { 5671 itemNumber = actionParams.postData.id; 5672 if (itemNumber.lastIndexOf("-") === -1) { 5673 itemNumber = itemNumber.substring(0, 3) + "-" + itemNumber.substring(3, 6); 5674 } 5675 else if (itemNumber.lastIndexOf("-") === 7) { 5676 itemNumber = itemNumber.substring(0, 7); 5677 } 5678 if (actionParams.postData.option != undefined) { 5679 sku = itemNumber + "-" + actionParams.postData.option; 5680 } 5681 else { 5682 sku = itemNumber + "-01"; 5683 } 5684 products = '{"prod_id": "' + itemNumber + '", "sku": "' + sku + '"}'; 5685 if (actionParams.postData.linkeditem != undefined) { 5686 itemNumber = actionParams.postData.linkeditem; 5687 if (itemNumber.lastIndexOf("-") === -1) { 5688 itemNumber = itemNumber.substring(0, 3) + "-" + itemNumber.substring(3, 6); 5689 } 5690 else if (itemNumber.lastIndexOf("-") === 7) { 5691 itemNumber = itemNumber.substring(0, 7); 5692 } 5693 if (actionParams.postData.linkeditemoption != undefined) { 5694 sku = itemNumber + "-" + actionParams.postData.linkeditemoption; 5695 } 5696 else { 5697 sku = itemNumber + "-01"; 5698 } 5699 products = products + "," + '{"prod_id": "' + itemNumber + '", "sku": "' + sku + '"}'; 5700 } 5701 if (actionParams.postData.warranty != undefined) { 5702 itemNumber = actionParams.postData.warranty; 5703 if (itemNumber.lastIndexOf("-") === -1) { 5704 itemNumber = itemNumber.substring(0, 3) + "-" + itemNumber.substring(3, 6); 5705 } 5706 else if (itemNumber.lastIndexOf("-") === 7) { 5707 itemNumber = itemNumber.substring(0, 7); 5708 } 5709 if (actionParams.postData.warrantyoptionno != undefined) { 5710 sku = itemNumber + "-" + actionParams.postData.warrantyoptionno; 5711 } 5712 else { 5713 sku = itemNumber + "-01"; 5714 } 5715 products = products + "," + '{"prod_id": "' + itemNumber + '", "sku": "' + sku + '"}'; 5716 } 5717 } 5718 else if (actionParams.postData.data != undefined) { 5719 var jsonData = JSON.parse(actionParams.postData.data); 5720 if (jsonData != undefined && jsonData.Items != undefined && jsonData.Items.length > 0) { 5721 for (var j = 0; j < jsonData.Items.length; j++) { 5722 if (jsonData.Items[j].id != undefined) { 5723 itemNumber = jsonData.Items[j].id; 5724 if (itemNumber.lastIndexOf("-") === -1) { 5725 itemNumber = itemNumber.substring(0, 3) + "-" + itemNumber.substring(3, 6); 5726 } 5727 else if (itemNumber.lastIndexOf("-") === 7) { 5728 itemNumber = itemNumber.substring(0, 7); 5729 } 5730 if (jsonData.Items[j].option != undefined) { 5731 sku = itemNumber + "-" + jsonData.Items[j].option; 5732 } 5733 else { 5734 sku = itemNumber + "-01"; 5735 } 5736 } 5737 if (products !== "") { 5738 products = products + ","; 5739 } 5740 products = products + '{"prod_id": "' + itemNumber + '", "sku": "' + sku + '"}'; 5741 if (jsonData.Items[j].linkeditem != undefined) { 5742 itemNumber = jsonData.Items[j].linkeditem; 5743 if (itemNumber.lastIndexOf("-") === -1) { 5744 itemNumber = itemNumber.substring(0, 3) + "-" + itemNumber.substring(3, 6); 5745 } 5746 else if (itemNumber.lastIndexOf("-") === 7) { 5747 itemNumber = itemNumber.substring(0, 7); 5748 } 5749 if (jsonData.Items[j].linkeditemoption != undefined) { 5750 sku = itemNumber + "-" + jsonData.Items[j].linkeditemoption; 5751 } 5752 else { 5753 sku = itemNumber + "-01"; 5754 } 5755 products = products + "," + '{"prod_id": "' + itemNumber + '", "sku": "' + sku + '"}'; 5756 } 5757 if (jsonData.Items[j].warranty != undefined) { 5758 itemNumber = jsonData.Items[j].warranty; 5759 if (itemNumber.lastIndexOf("-") === -1) { 5760 itemNumber = itemNumber.substring(0, 3) + "-" + itemNumber.substring(3, 6); 5761 } 5762 else if (itemNumber.lastIndexOf("-") === 7) { 5763 itemNumber = itemNumber.substring(0, 7); 5764 } 5765 if (jsonData.Items[j].warrantyoptionno != undefined) { 5766 sku = itemNumber + "-" + jsonData.Items[j].warrantyoptionno;
5767 } 5768 else { 5769 sku = itemNumber + "-01"; 5770 } 5771 products = products + "," + '{"prod_id": "' + itemNumber + '", "sku": "' + sku + '"}'; 5772 } 5773 } 5774 } 5775 } 5776 var productsAdded = JSON.parse('[' + products + ']'); 5777 var bloomReachPixelEnabled = false; 5778 var bloomReachSofaSku = ""; 5779 var bloomReachSofaId = ""; 5780 if (dataLayer != undefined && dataLayer.length > 0) { 5781 for (var j = 0; j < dataLayer.length; j++) { 5782 if (dataLayer[j].products_added_into_bag != undefined) { 5783 dataLayer.splice(j, 1); 5784 } 5785 if (dataLayer[j].br_pixel_enabled != undefined) { 5786 bloomReachPixelEnabled = dataLayer[j].br_pixel_enabled === "true"; 5787 } 5788 if (dataLayer[j].br_sofa_sku != undefined && dataLayer[j].br_sofa_id != undefined) { 5789 bloomReachSofaSku = dataLayer[j].br_sofa_sku; 5790 bloomReachSofaId = dataLayer[j].br_sofa_id; 5791 } 5792 } 5793 if (bloomReachPixelEnabled) { 5794 var brProducts = products.replace(/-/g, ""); 5795 if (bloomReachSofaId !== "" && bloomReachSofaSku !== "") { 5796 brProducts = '{"prod_id": "' + bloomReachSofaId + '", "sku": "' + bloomReachSofaSku + '"}'; 5797 } 5798 brProducts = JSON.parse('[' + brProducts + ']'); 5799 dataLayer.push({ "event": "BloomReachProductsAddedIntoBag", "br_products_added_into_bag": brProducts }); 5800 } 5801 dataLayer.push({ "event": "ProductsAddedIntoBag", "products_added_into_bag": productsAdded }); 5802 if (Next.Settings.Channel.Promotions.Sampling && actionParams.actionPath === "bag/addSample") { 5803 dataLayer.push({ 5804 event: 'Sampling', 5805 Sampling: { 5806 Category: 'Sampling', 5807 Action: 'Banner', 5808 Label: 'Sampling_ribbon_add_to_bag' 5809 } 5810 }); 5811 } 5812 } 5813 } 5814 if (window.monetateQ && typeof monetateEnabled != 'undefined' && monetateEnabled) { 5815 if (actionParams.actionType != undefined && (actionParams.actionType === "remove")) { 5816 window.monetateQ.push(["addCategories", ['RemovedFromBag']]); 5817 } 5818 } 5819 } 5820 } 5821 }; 5822 Basket.prototype.ItemRemove = function (itemID, customItemType) { 5823 RebuildDataLayer(); 5824 var actionParams = { 5825 actionPath: "bag/remove", 5826 postData: { id: itemID, customItemType: customItemType }, 5827 actionType: "remove", 5828 loadBasketPage: false 5829 }; 5830 this.AddBagAction(actionParams); 5831 }; 5832 Basket.prototype.ItemReject = function (itemID) { 5833 RebuildDataLayer(); 5834 var actionParams = { 5835 actionPath: "bag/reject", 5836 postData: { id: itemID }, 5837 actionType: "reject", 5838 loadBasketPage: false 5839 }; 5840 this.AddBagAction(actionParams); 5841 }; 5842 Basket.prototype.ItemAccept = function (itemID) { 5843 RebuildDataLayer(); 5844 var actionParams = { 5845 actionPath: "bag/accept", 5846 postData: { id: itemID }, 5847 actionType: "accept", 5848 loadBasketPage: false 5849 }; 5850 this.AddBagAction(actionParams); 5851 }; 5852 Basket.prototype.ItemAddSample = function (items) { 5853 RebuildDataLayer(); 5854 var actionParams = { 5855 actionPath: "bag/addSample", 5856 postData: { items: items }, 5857 actionType: "addSample", 5858 loadBasketPage: false 5859 }; 5860 this.AddBagAction(actionParams); 5861 }; 5862 Basket.prototype.ItemRemoveSample = function (itemID) { 5863 RebuildDataLayer(); 5864 var actionParams = { 5865 actionPath: "bag/removeSample", 5866 postData: { id: itemID }, 5867 actionType: "removeSample", 5868 loadBasketPage: false 5869 }; 5870 this.AddBagAction(actionParams); 5871 }; 5872 Basket.prototype.CustomAction = function (data, customActionId) { 5873 RebuildDataLayer(); 5874 var actionParams = { 5875 actionPath: "customAction", 5876 postData: { 5877 customActionId: customActionId, 5878 data: data 5879 }, 5880 actionType: "customAction", 5881 loadBasketPage: false 5882 }; 5883 this.AddBagAction(actionParams); 5884 }; 5885 Basket.prototype.ItemAdd = function (itemID, optionNo, quantity, chain, loadBasketPage, callBack, SaleClearanceS
5885ofaImage, modalItemUrl, originatesFromQbtTray, basePagePdpItemNumber) { 5886 if (SaleClearanceSofaImage === void 0) { SaleClearanceSofaImage = ""; } 5887 if (modalItemUrl === void 0) { modalItemUrl = ""; } 5888 if (originatesFromQbtTray === void 0) { originatesFromQbtTray = false; } 5889 if (basePagePdpItemNumber === void 0) { basePagePdpItemNumber = ""; } 5890 RebuildDataLayer(); 5891 //Build action paramters 5892 var actionParams = { 5893 actionPath: "bag/add", 5894 postData: { id: itemID, option: optionNo, chain: chain, quantity: quantity, modalItemUrl: modalItemUrl, SaleClearanceSofaImage: SaleClearanceSofaImage }, 5895 actionType: "add", 5896 loadBasketPage: loadBasketPage, 5897 callBack: callBack 5898 }; 5899 SaveCurrentLocation(); 5900 itemNumberAdded = itemID; 5901 if (originatesFromQbtTray && basePagePdpItemNumber != "") { 5902 itemNumberAdded = basePagePdpItemNumber; 5903 } 5904 if (this.MobilePMeterAnimationEnabled) { 5905 this.TriggerMobilePMeterAnimation = true; 5906 } 5907 if (this.DesktopPMeterAnimationEnabled) { 5908 this.TriggerDesktopPMeterAnimation = true; 5909 } 5910 //Pass to AddBagAction method 5911 this.AddBagAction(actionParams); 5912 }; 5913 Basket.prototype.ItemAddCist = function (itemID, optionNo, quantity, chain, loadBasketPage, callBack, branchId) { 5914 RebuildDataLayer(); 5915 //Build action paramters 5916 var actionParams = { 5917 actionPath: "bag/AddCist", 5918 postData: { id: itemID, option: optionNo, chain: chain, quantity: quantity, ciststoreid: branchId }, 5919 actionType: "add", 5920 loadBasketPage: loadBasketPage, 5921 callBack: callBack 5922 }; 5923 SaveCurrentLocation(); 5924 itemNumberAdded = itemID; 5925 //Pass to AddBagAction method 5926 this.AddBagAction(actionParams); 5927 }; 5928 Basket.prototype.ItemAddMultiple = function (data, options, callBack, dataLoadCallback) { 5929 RebuildDataLayer(); 5930 //Fallback in case some method calls are still sending a boolean (loadBasketPage) for 2nd parameter 5931 if (typeof options == "boolean") { 5932 options = { 5933 loadBasketPage: options 5934 }; 5935 } 5936 var postData = { 5937 data: $.toJSON(data), 5938 pageurl: null, 5939 deliveryweeks: null, 5940 grouped: false, 5941 groupType: null 5942 }; 5943 if (options.pageurl 5944 && options.pageurl.length) { 5945 postData.pageurl = options.pageurl; 5946 } 5947 if (options.deliveryweeks != null 5948 && options.deliveryweeks.toString().length) { 5949 postData.deliveryweeks = options.deliveryweeks; 5950 } 5951 if (options.grouped 5952 && options.groupType != null 5953 && options.groupType.length) { 5954 postData.grouped = options.grouped; 5955 postData.groupType = options.groupType; 5956 } 5957 var actionParams = { 5958 actionPath: "bag/addmultiple", 5959 postData: postData, 5960 actionType: "add", 5961 loadBasketPage: options.loadBasketPage, 5962 callBack: callBack, 5963 dataLoadCallback: dataLoadCallback 5964 }; 5965 SaveCurrentLocation(); 5966 this.AddBagAction(actionParams); 5967 }; 5968 Basket.prototype.ItemAddWarranty = function (itemID, optionNo, chain, warrantyID, warrantyOption, callBack) { 5969 RebuildDataLayer(); 5970 var actionParams = { 5971 actionPath: "bag/addwarranty", 5972 postData: { id: itemID, option: optionNo, warranty: warrantyID, warrantyoptionno: warrantyOption }, 5973 actionType: "add", 5974 loadBasketPage: false, 5975 callBack: callBack 5976 }; 5977 itemNumberAdded = itemID; 5978 //Pass to AddBagAction method 5979 this.AddBagAction(actionParams); 5980 }; 5981 // used by sofas 5982 Basket.prototype.ItemAddLinkedItem = function (itemID, optionNo, linkedID, linkedOption, quantity, loadBasketPage, callBack, originatesFromQbtTray, basePagePdpItemNumber) { 5983 if (originatesFromQbtTray === void 0) { originatesFromQbtTray = false; } 5984 if (basePagePdpItemNumber === void 0) { basePagePdpItemNumber = ""; } 5985 RebuildDataLayer(); 5986 var actionParams = { 5987 actionPath: "bag/addlinkeditem", 5988 postData: { id: itemID, option: optionNo, linkeditem: linkedID, linkeditemoption: linkedOption }, 5989 actionType: "add", 5990 loadBasketPage: loadBasketPage, 5991 callBack: callBack 5992 }; 5993 itemNumberAdded = itemID; 5994 if (originatesFromQbtTray && basePagePdpItemNumber != "") { 5995 itemNumberAdded = basePagePdpItemNumber; 5996 } 5997 //Pass to AddBagAction method 5998 this.AddBagAction(actionParams); 5999 }; 6000 Basket.prototype.ItemUpdateSize = function (itemID, optionNo, itemNumber) { 6001 var _this = this; 6002 RebuildDataLayer(); 6003 var eventId = Date.now().toString(); 6004 var updateSizeSubscription = w.subjects["$ SHOPPING_BAG_UPDATE_SIZE_CALLBACK"].subscribe(function (eventResponse) { 6005 if (eventId === eventResponse.eventId) { 6006 var jqXHR = { 6007 getResponseHeader: function () { return false; }, 6008 status: eventResponse.status 6009 }; 6010 if (eventResponse.success) { 6011 if (_this.MobilePMeterAnimationEnabled) { 6012 _this.TriggerMobilePMeterAnimation = true; 6013 } 6014 _this.LoadSuccess(eventResponse.data, eventResponse.textStatus, jqXHR, 'addupdatesize'); 6015 } 6016 else { 6017 _this.LoadError(jqXHR, eventResponse.textStatus, null, 'updatesize'); 6018 } 6019 updateSizeSubscription.unsubscribe(); 6020 } 6021 }); 6022 var updateSizeEventData = { eventId: eventId, id: itemID, optionNumber: optionNo, itemNumber: itemNumber }; 6023 w.subjects["$ SHOPPING_BAG_UPDATE_SIZE"].next(updateSizeEventData); 6024 }; 6025 Basket.prototype.ItemUpdateQuantity = function (itemID, quantity, loadBasketPage) { 6026 var _this = this; 6027 if (loadBasketPage === void 0) { loadBasketPage = true; } 6028 RebuildDataLayer(); 6029 var eventId = Date.now().toString(); 6030 var updateSizeSubscription = w.subjects["$ SHOPPING_BAG_UPDATE_QUANTITY_CALLBACK"].subscribe(function (eventResponse) { 6031 if (eventId === eventResponse.eventId) { 6032 var jqXHR = { 6033 getResponseHeader: function () { return false; }, 6034 status: eventResponse.status 6035 }; 6036 if (eventResponse.success) { 6037 if (_this.MobilePMeterAnimationEnabled) { 6038 _this.TriggerMobilePMeterAnimation = true; 6039 } 6040 if (_this.DesktopPMeterAnimationEnabled) { 6041 _this.TriggerDesktopPMeterAnimation = true; 6042 } 6043 _this.LoadSuccess(eventResponse.data, eventResponse.textStatus, jqXHR, 'updatequantity', loadBasketPage); 6044 } 6045 else { 6046 _this.LoadError(jqXHR, eventResponse.textStatus, null, 'updatequantity'); 6047 } 6048 updateSizeSubscription.unsubscribe(); 6049 } 6050 }); 6051 var updateSizeEventData = { eventId: eventId, id: itemID, quantity: quantity }; 6052 w.subjects["$ SHOPPING_BAG_UPDATE_QUANTITY"].next(updateSizeEventData); 6053 }; 6054 Basket.prototype.AddEVoucher = function (id, option, eVoucherTo, eVoucherFrom, eVoucherEmail, eVoucherMessage, ecardDeliveryDate) { 6055 RebuildDataLayer(); 6056 var actionParams = { 6057 actionPath: "bag/addevoucher", 6058 postData: { id: id, option: option, eVoucherTo: eVoucherTo, eVoucherFrom: eVoucherFrom, eVoucherEmail: eVoucherEmail, eVoucherMessage: eVoucherMessage, ecardDeliveryDate: ecardDeliveryDate }, 6059 actionType: "addEvoucher", 6060 loadBasketPage: false 6061 }; 6062 this.AddBagAction(actionParams); 6063 }; 6064 Basket.prototype.AddEVoucherMobile = function (id, option, eVoucherTo, eVoucherFrom, eVoucherEmail, eVoucherMessage, ecardDeliveryDate, callBack) { 6065 RebuildDataLayer(); 6066 var actionParams = { 6067 actionPath: "bag/addevoucher", 6068 postData: { id: id, option: option, eVoucherTo: eVoucherTo, eVoucherFrom: eVoucherFrom, eVoucherEmail: eVoucherEmail, eVoucherMessage: eVoucherMessage, ecardDeliveryDate: ecardDeliveryDate }, 6069 actionType: "addEvoucher", 6070 loadBasketPage: false, 6071 callBack: callBack 6072 }; 6073 this.AddBagAction(actionParams); 6074 };
6075 //add gift card item to Shopping Bag 6076 Basket.prototype.AddGiftCard = function (id, amountOption, firstName, lastName, addressLine1, addressLine2, addressLine3, addressLine4, addressLine5, postCode, message, deliveryDate, deliveryOption, deliveryCharge, mediacode, callBack) { 6077 RebuildDataLayer(); 6078 var actionParams = { 6079 actionPath: "bag/addgiftcard", 6080 postData: { id: id, amountOption: amountOption, firstName: firstName, lastName: lastName, addressLine1: addressLine1, addressLine2: addressLine2, addressLine3: addressLine3, addressLine4: addressLine4, addressLine5: addressLine5, postCode: postCode, message: message, deliveryDate: deliveryDate, deliveryOption: deliveryOption, deliveryCharge: deliveryCharge, mediacode: mediacode }, 6081 actionType: "addgiftcard", 6082 loadBasketPage: false, 6083 callBack: callBack 6084 }; 6085 this.AddBagAction(actionParams); 6086 }; 6087 Basket.prototype.Load = function () { 6088 w.subjects["$ SHOPPING_BAG_GET"].next({ guestAccountConverted: guestAccountConverted }); 6089 }; 6090 Basket.prototype.NotifySuccess = function () { 6091 if (this.Loaded) { 6092 for (var i = 0; i < this.NotifyChange.length; i++) { 6093 this.NotifyChange[i](); 6094 } 6095 } 6096 ; 6097 }; 6098 Basket.prototype.AddItemNotify = function (resultItemArray, currentBag) { 6099 var eventResponse = { 6100 success: true, 6101 data: { ShoppingBag: currentBag }, 6102 status: 200, 6103 textStatus: "OK", 6104 eventId: null 6105 }; 6106 w.subjects["$ SHOPPING_BAG_ADD_CALLBACK"].next(eventResponse); 6107 this.AddItemNotifyInternal(resultItemArray, currentBag); 6108 }; 6109 Basket.prototype.AddItemNotifyInternal = function (resultItemArray, currentBag) { 6110 var itemObj = { 6111 itemArray: resultItemArray, 6112 currentBag: currentBag 6113 }; 6114 this.OutboundQueue.push(itemObj); 6115 if (!this.NotifyWaiting) { 6116 this.NotifyWaiting = true; 6117 // var nextNotifyItem = NextBasket.OutboundQueue.shift(); 6118 var nextNotifyItem = this.OutboundQueue[0]; 6119 // check if the item is a part of multibuy if so than call get multibuy 6120 // if (NextBasket.OutboundQueue[0].itemArray[0].IsMultibuyItem) 6121 if (this.DesktopPMeterAnimationEnabled) { 6122 this.TriggerDesktopPMeterAnimation = true; 6123 } 6124 if (this.MobilePMeterAnimationEnabled) { 6125 this.TriggerMobilePMeterAnimation = true; 6126 } 6127 this.NotifyItemAdded(nextNotifyItem); 6128 } 6129 }; 6130 Basket.prototype.SetGAUp = function (SessionTransferVariables) { 6131 if (SessionTransferVariables != null) { 6132 this.SessionTransferVariables.IgnoreGA = SessionTransferVariables.IgnoreGA; 6133 this.SessionTransferVariables.GAAccount = SessionTransferVariables.GAAccount; 6134 this.SessionTransferVariables.GASessionId = SessionTransferVariables.GASessionId; 6135 this.SessionTransferVariables.HomeUrl = SessionTransferVariables.HomeUrl; 6136 this.SessionTransferVariables.SearchUrl = SessionTransferVariables.SearchUrl; 6137 this.SessionTransferVariables.ShoppingBagUrl = SessionTransferVariables.ShoppingBagUrl; 6138 this.SessionTransferVariables.GAEnabledForGTM = SessionTransferVariables.GAEnabledForGTM; 6139 this.SessionTransferVariables.GAAccountForGTM = SessionTransferVariables.GAAccountForGTM; 6140 this.NotifyGAAccount(this.SessionTransferVariables.GAAccount, this.SessionTransferVariables.GASessionId, !this.SessionTransferVariables.IgnoreGA); 6141 this.NotifyGAAccountForGTM(this.SessionTransferVariables.GAAccountForGTM, this.SessionTransferVariables.GASessionId, this.SessionTransferVariables.GAEnabledForGTM); 6142 } 6143 else { 6144 // Call GA with NOTHING so that defaults are resorted to. 6145 this.NotifyGAAccount(null, null, null); 6146 this.NotifyGAAccountForGTM(null, null, null); 6147 } 6148 }; 6149 Basket.prototype.convertProductGender = function (gender) { 6150 if (gender) { 6151 if (gender.toLowerCase().indexOf("boy") != -1) 6152 return "Men"; 6153 if (gender.toLowerCase().indexOf("girl") != -1) 6154 return "Women"; 6155 } 6156 else { 6157 gender = ''; 6158 } 6159 return gender; 6160 }; 6161 // callback - this is more visual, it is triggered once the notification is shown 6162 // dataLoadCallback - this is triggered as soon as we have data 6163 Basket.prototype.LoadSuccess = function (data, textStatus, jqXHR, actionType, loadBasketPage, callBack, dataLoadCallback) { 6164 // Call the GA setup routine 6165 if (setUpGA) { 6166 if (data != null && data.ShoppingBag != null) { 6167 this.SetGAUp(data.ShoppingBag.SessionTransferVariables); 6168 } 6169 else { 6170 this.SetGAUp(null); 6171 } 6172 } 6173 checkAndRedirectForAjaxError(jqXHR, false, 'ShoppingBagCommon.js: ' + actionType); 6174 if (dataLoadCallback) { 6175 dataLoadCallback(true); 6176 } 6177 if (this.SessionID === '') 6178 this.SessionID = data.SessionID; 6179 if (this.VisitorID === '') 6180 this.VisitorID = data.VisitorID; 6181 if (this.RoamingProfileId === '') 6182 this.RoamingProfileId = data.RoamingProfileId; 6183 this.BrowseOnlyMode = data.BrowseOnlyMode; 6184 //Check if RedirectURL has a value, and HTTP redirect the user to that value
6185 if (data.Variables && data.Variables.RedirectURL) { 6186 window.location.replace(data.Variables.RedirectURL); 6187 } 6188 if (actionType === "load" 6189 && typeof CountryRedirect === 'object') { 6190 //Initalise CountryRedirect object and pass in data from response 6191 CountryRedirect.Init(data.CountryRedirect); 6192 $(document).trigger("NextBasket_Load"); 6193 } 6194 if (actionType === "load" && typeof NextBasketState !== 'undefined') 6195 NextBasketState.SetNextBasketDataOnPageLoad(data.ShoppingBag); 6196 if (Next.Settings.Channel.IsNextUnlimitedEnabled) { 6197 if (((actionType === "remove") || (actionType === "removegroup") || (actionType === "updatequantity") || (actionType === "addupdatesize") || (Next.Settings.Mobile.IsShoppingbagDrawerEnabled && actionType === "load")) && typeof data.NextUnlimited === "object") { 6198 this.NextUnlimited.Update(data.NextUnlimited); 6199 } 6200 } 6201 //First need to start the next action waiting to be sent (if one exists) 6202 if (this.InboundQueue.length) { 6203 //Get next action and send to be executed 6204 var nextAction = this.InboundQueue.shift(); 6205 this.ExecuteBagAction(nextAction); 6206 } 6207 else { 6208 //Else - all items are processed 6209 this.ItemsWaiting = false; 6210 // Now we know we are on the last item, lets handle any errors 6211 this.NotifyErrors(); 6212 } 6213 var hasOutOfStockItems = false; 6214 if (Next.Settings.Channel.Promotions.GWP && typeof gwpShoppingBagBanners !== 'undefined') { 6215 gwpShoppingBagBanners.refresh(data.ShoppingBag); 6216 } 6217 if (Next.Settings.Channel.Promotions.Sampling && window.location.href.indexOf("shoppingbag") > -1 6218 && typeof SamplingRibbon !== 'undefined' && typeof SamplingBanner !== 'undefined') { 6219 SamplingRibbon.create(data.ShoppingBag); 6220 SamplingBanner.create(data.ShoppingBag); 6221 } 6222 if (Next.Settings.Channel.EnableFlowers && window.location.href.indexOf("shoppingbag") > -1) { 6223 if (typeof data.ShoppingBag !== 'undefined' && typeof data.ShoppingBag.Items !== 'undefined' && !data.ShoppingBag.Items.some(function (i) { return i.FulfilmentType === 'F'; })) { 6224 var flowersInfoBox = document.querySelectorAll('.flowersInfoBox'); 6225 flowersInfoBox.forEach(function (item) { 6226 item.remove(); 6227 }); 6228 } 6229 } 6230 if (Next.Settings.Channel.EnableFlowers && typeof FlowerItem !== 'undefined') { 6231 FlowerItem.ManageTimeoutForFlowerItem(data.ShoppingBag); 6232 } 6233 if (Next.Settings.Channel.ShoppingBagBannersSettings.ShoppingBagBanners.Enabled && window.location.href.indexOf("shoppingbag") > -1 && typeof ContentDeliverySdkInitializer !== 'undefined') { 6234 ContentDeliverySdkInitializer.refreshContentDeliverySdk(data.ShoppingBag); 6235 } 6236 switch (actionType) { 6237 case "add": 6238 case "addEvoucher": 6239 case "addSample": 6240 case "addgiftcard": 6241 // this.Data = data.Bag; 6242 (actionType == "addEvoucher" || actionType == "addgiftcard") ? this.Data = data.Bag : this.Data = data.ShoppingBag; 6243 //this.Data = data.ShoppingBag; 6244 this.Loaded = true; 6245 var newItemArr = []; 6246 // TODO: Filter out feet 6247 if (data.ItemStates != null) { 6248 for (var i = 0; i < data.ItemStates.length; i++) { 6249 if (data.ItemStates[i].State === "A" || data.ItemStates[i].State === "R") { 6250 newItemArr.push(data.ItemStates[i].Item); 6251 //FB 162345 - this functionality is not currently required as we do not want subsequent redirect from Clearance 6252 //if (data.ItemStates[i].Item.StockStatus === "soldout") 6253 // hasOutOfStockItems = true; 6254 } 6255 } 6256 } 6257 if (Next.Settings.Channel.ShoppingBagBannersSettings.ShoppingBagBanners.Enabled && 6258 dataLayer != undefined && 6259 newItemArr.length > 0) { 6260 this.pushAddtoBagClickToDataLayer(newItemArr); 6261 } 6262 if (Next.Settings.Channel.Promotions.GWP && newItemArr.length > 1) { 6263 var freeGift = newItemArr.find(function (item) { return item.IsFreeGiftItem; }); 6264 if (freeGift !== undefined) { 6265 newItemArr[0].TriggeredFreeGift = true; 6266 newItemArr[0].FreeGiftTitle = freeGift.Description; 6267 if (dataLayer != null && Next.Settings.Channel.Promotions.GWP) { 6268 dataLayer.push({ 6269 event: 'GWP', 6270 GWP: { 6271 Label: 'gwp_pdp_add_to_bag', 6272 Action: 'PDP', 6273 Category: 'Gift With Purchase' 6274 } 6275 }); 6276 } 6277 } 6278 } 6279 this.Data.LatestItem = newItemArr; 6280 if (actionType === "addEvoucher") { 6281 hasOutOfStockItems = false; 6282 } 6283 //var curBag = data.Bag; 6284 var curBag; 6285 (actionType == "addEvoucher" || actionType == "addgiftcard") ? curBag = data.Bag : curBag = data.ShoppingBag; 6286 // If we are displaying the basket page then we need to update the count and total summary in the header, otherwise we need to show the notification 6287 if (loadBasketPage || hasOutOfStockItems) 6288 this.PriceDial(curBag); 6289 else 6290 this.AddItemNotifyInternal(newItemArr, curBag); 6291 if (this.Data.LowerLimitReached || this.Data.IsShoppingBagFull) { 6292 // todo: this seems to be an error: 6293 // the behaviour in this area seems to be broken in live - past a certain # of items (lowerlimit?) the notification stops and the shopping bag is only updated on loading a new page (and once per page after?) - the shopping bag page isn't loaded 6294 //notifyItemAdded = true; // this forces the shopping bag page to be loaded - Raj 6295 //todo: we're going to revisit this at a later date, but for now we'll load the shopping bag page 6296 if (typeof NavigateToShoppingBag === 'function') { 6297 NavigateToShoppingBag(); 6298 } 6299 else { 6300 window.location.href = Next.Settings.Channel.SiteUrl + "/shoppingbag"; 6301 } 6302 } 6303 break; 6304 default: // all other action types (remove, update etc) do not need to perform any specific action (currently) - Raj 6305 var returnedData = null; 6306 // if (data.Bag != undefined) 6307 // returnedData = data.Bag; 6308 // else 6309 returnedData = data.ShoppingBag; 6310 //var dataVersion = returnedData.Version; 6311 this.Data = returnedData; 6312 this.Loaded = true; 6313 this.NotifySuccess(); // this should come first, to display total amount at the top of the page - Raj 6314 var forcePageRefresh = false; 6315 if (data.IgnoreBagVersion === true) 6316 forcePageRefresh = true; 6317 var url = window.location.pathname.toLowerCase(); 6318 if (typeof RefreshShoppingBag === 'function') { 6319 RefreshShoppingBag(forcePageRefresh); 6320 } 6321 else if (Next.Settings.Channel.ResponsiveShoppingBagIsEnabled && url.indexOf("/shoppingbag") > -1) { 6322 $(function () { 6323 if (typeof RefreshShoppingBag === 'function') { 6324 RefreshShoppingBag(forcePageRefresh); 6325 } 6326 }); 6327 } 6328 //Signify if the shopping bag has been recreated from a previous session and the user has not been previously notified. 6329 if (actionType === "load" && returnedData.RecreatedFromPreviousSession && !returnedData.UserNotifiedOfSessionRecreation && returnedData.Authenticated) { 6330 //this.Notify(returnedData); 6331 if (this.Data.IsShoppingBagFull) { 6332 if (typeof NavigateToShoppingBag === 'function') { 6333 NavigateToShoppingBag(); 6334 } 6335 else { 6336 window.location.href = Next.Settings.Channel.SiteUrl + "/shoppingbag"; 6337 } 6338 } 6339 else { 6340 this.DisplayRebuildNotification(returnedData); 6341 } 6342 } 6343 break; 6344 } 6345 if (data.ShoppingBag != null && $("#promoInputShoppingBag").length > 0) { 6346 if (data.ShoppingBag.MultiBuyDiscount != null && data.ShoppingBag.MultiBuyDiscount > 0 && !Next.Settings.Channel.AllowPromoWithMultibuy || data.ShoppingBag.ItemCount == 0) { 6347 $('#promoInputShoppingBag').hide(300);
6348 } 6349 else { 6350 $('#promoInputShoppingBag').show(300); 6351 } 6352 } 6353 if (callBack) { 6354 var callBackParam = !(Object(callBack).name == "removeFromSaveForLater" && this.Data.IsShoppingBagFull); 6355 /* 6356 Protocol relative SiteUrl causes the page to load faster 6357 Therefore the page is refreshed before the item is removed from save for later 6358 So, Feature switch for HTTPS to remove setTimeout 6359 */ 6360 if (Next.Settings.Channel.HttpsEnabled) { 6361 callBack(callBackParam); 6362 } 6363 else { 6364 setTimeout(function () { 6365 callBack(callBackParam); 6366 }, 500); 6367 } 6368 } 6369 this.UpdateBagUI(this); 6370 this.saveBagCookieForVIP(); 6371 if (loadBasketPage || hasOutOfStockItems) 6372 if (typeof NavigateToShoppingBag === 'function') { 6373 NavigateToShoppingBag(); 6374 } 6375 else { 6376 var loadHtmlContentUrl = Next.Settings.Channel.HttpsEnabled ? window.location.protocol + AjaxURL() : AjaxURL(); 6377 if (Next.Settings.Channel.IsTotalPlatformLiteClient && Next.Settings.Channel.MainSiteAbsolutePath && Next.Settings.Channel.MainSiteAbsolutePath !== "/") { 6378 loadHtmlContentUrl = AjaxURL() + Next.Settings.Channel.MainSiteAbsolutePath.replace("/", "") + "/"; 6379 } 6380 LoadHTMLContent(loadHtmlContentUrl + 'shoppingbag', 'Shopping Bag'); 6381 } 6382 var cartRows = ""; 6383 var products = ""; 6384 var productList = ""; 6385 var itemsData = ""; 6386 var position = 0; 6387 var dataLayerCurrencyCode = "GBP"; 6388 var isShoppingBagPage = false; 6389 var isDeliveryPage = false; 6390 var isPaymentPage = false; 6391 var isCompletePage = false; 6392 var pageType = ""; 6393 var bloomReachPixelEnabled = false; 6394 var productsAddedIntoBag = false; 6395 var productSKUs = ""; 6396 monetateEnabled = false; 6397 if (!Next.Settings.Channel.PlatModGoogleAnalyticsSDK) { 6398 if (rebuildDataLayer && dataLayer != undefined && dataLayer.length > 0) { 6399 for (var j = 0; j < dataLayer.length; j++) { 6400 if (dataLayer[j].page_type != undefined) { 6401 pageType = dataLayer[j].page_type; 6402 } 6403 else if (dataLayer[j].monetate_enabled != undefined) { 6404 monetateEnabled = dataLayer[j].monetate_enabled === "true"; 6405 } 6406 else if (dataLayer[j].br_pixel_enabled != undefined) { 6407 bloomReachPixelEnabled = dataLayer[j].br_pixel_enabled === "true"; 6408 } 6409 else if (dataLayer[j].channel_currency_code != undefined) { 6410 dataLayerCurrencyCode = dataLayer[j].channel_currency_code; 6411 } 6412 else if (dataLayer[j].is_shoppingbag_page != undefined) { 6413 isShoppingBagPage = dataLayer[j].is_shoppingbag_page === "true"; 6414 } 6415 else if (dataLayer[j].is_delivery_page != undefined) { 6416 isDeliveryPage = dataLayer[j].is_delivery_page === "true"; 6417 } 6418 else if (dataLayer[j].is_payment_page != undefined) { 6419 isPaymentPage = dataLayer[j].is_payment_page === "true"; 6420 } 6421 else if (dataLayer[j].is_complete_page != undefined) { 6422 isCompletePage = dataLayer[j].is_complete_page === "true"; 6423 } 6424 else if (dataLayer[j].event != undefined) { 6425 productsAddedIntoBag = (dataLayer[j].event === "ProductsAddedIntoBag"); 6426 } 6427 else if (dataLayer[j].monetateCartRows != undefined) { 6428 dataLayer.splice(j, 1); 6429 } 6430 else if (dataLayer[j].itemsData != undefined) { 6431 dataLayer.splice(j, 1); 6432 } 6433 else if (dataLayer[j].bloomreachBasketData != undefined) { 6434 dataLayer.splice(j, 1); 6435 } 6436 } 6437 } 6438 if (rebuildDataLayer && NextBasket.Data.Items != null && NextBasket.Data.Items.length > 0) { 6439 var bloomreachBasketData = itemsData; 6440 for (var i = 0; i < NextBasket.Data.Items.length; i++) { 6441 if (cartRows !== "") { 6442 cartRows = cartRows + ","; 6443 } 6444 var friendlyItemNumber = NextBasket.Data.Items[i].ItemNumber.substring(0, 3) + '-' + NextBasket.Data.Items[i].ItemNumber.substring(3); 6445 var itemNumber = NextBasket.Data.Items[i].ItemNumber; 6446 var quantity = NextBasket.Data.Items[i].Quantity; 6447 var price = NextBasket.Data.Items[i].Price; 6448 var name = NextBasket.Data.Items[i].Description; 6449 var variant = NextBasket.Data.Items[i].SizeDescription; 6450 if (bloomReachPixelEnabled) { 6451 if (bloomreachBasketData !== "") { 6452 bloomreachBasketData = bloomreachBasketData + ","; 6453 } 6454 var brItemNumber = NextBasket.Data.Items[i].ItemNumber.substring(0, 3) + 6455 NextBasket.Data.Items[i].ItemNumber.substring(3); 6456 var productId = ""; 6457 var sku = ""; 6458 if (NextBasket.Data.Items[i].ItemCategory === "sofas" && 6459 NextBasket.Data.Items[i].AlternativeDescription.split("|").length > 0) { 6460 var sofaStyleTitle = NextBasket.Data.Items[i].AlternativeDescription.split("|")[0].trim() 6461 .replace(/ /g, "").toLowerCase(); 6462 productId = sofaStyleTitle + "_" + NextBasket.Data.Items[i].OptionNo; 6463 sku = productId + "_" + NextBasket.Data.Items[i].ItemNumber; 6464 } 6465 else { 6466 productId = brItemNumber; 6467 sku = brItemNumber + NextBasket.Data.Items[i].OptionNo; 6468 } 6469 bloomreachBasketData = bloomreachBasketData + '{"prod_id": "' + productId + '", "sku": "' + sku + '", "name": "' + n
6469ame + '", "quantity": "' + quantity + '", "price": "' + price + '"}'; 6470 } 6471 cartRows = cartRows + '{"productId": "' + friendlyItemNumber + '", "quantity": "' + quantity + '","unitPrice": "' + price + '","currency": "' + dataLayerCurrencyCode + '"}'; 6472 if (itemsData !== "") { 6473 itemsData = itemsData + ","; 6474 } 6475 itemsData = itemsData + '{"prod_id": "' + friendlyItemNumber + '", "sku": "' + friendlyItemNumber + '-' + NextBasket.Data.Items[i].OptionNo + '", "name": "' + name + '", "quantity": "' + quantity + '", "price": "' + price + '"}'; 6476 friendlyItemNumber = friendlyItemNumber + '-'; 6477 if (products !== "") { 6478 products = products + ","; 6479 } 6480 if (productList !== "") { 6481 productList = productList + ","; 6482 } 6483 position = i + 1; 6484 var ItemVariant = NextBasket.Data.Items[i].ItemNumber + '-' + NextBasket.Data.Items[i].OptionNo + ' | ' + NextBasket.Data.Items[i].SizeDescription; 6485 var colour = NextBasket.Data.Items[i].Colour || ""; 6486 var markdownItem = NextBasket.Data.Items[i].IsDiscount ? "true" : "false"; 6487 var brand = NextBasket.Data.Items[i].ItemBrand || ""; 6488 products = products + '{"id": "' + friendlyItemNumber + '", "name": "' + name + '","price": "' + price + '","variant": "' + variant + '","position": ' + position + ', "quantity": "' + quantity + '","markdownItem":"' + markdownItem + '"}'; 6489 productList = productList + '{"id": "' + friendlyItemNumber + '", "name": "' + name + '","price": "' + price + '","brand":"' + brand + '" ,"category":"' + NextBasket.Data.Items[i].ItemCategory + '","variant": "' + ItemVariant + '","position": ' + position + ', "quantity": ' + quantity + ',"dimension21":"' + NextBasket.Data.Items[i].StockStatus + '","dimension22":"' + NextBasket.Data.Items[i].ItemCategory + '","dimension23":"' + NextBasket.Data.Items[i].Department + '","dimension24":"' + this.convertProductGender(NextBasket.Data.Items[i].Gender) + '","dimension25":"","dimension26":"' + brand + '","dimension27":"' + brand + '","dimension28":"' + colour + '","markdownItem":"' + markdownItem + '"}'; 6490 if (productSKUs !== "") { 6491 productSKUs = productSKUs + ","; 6492 } 6493 productSKUs = productSKUs + friendlyItemNumber; 6494 if (NextBasket.Data.Items[i].OptionNo != null) { 6495 productSKUs = productSKUs + NextBasket.Data.Items[i].OptionNo; 6496 } 6497 } 6498 cartRows = JSON.parse('[' + cartRows + ']'); 6499 products = JSON.parse('[' + products + ']'); 6500 itemsData = JSON.parse('[' + itemsData + ']'); 6501 productList = JSON.parse('[' + productList + ']'); 6502 if (bloomReachPixelEnabled) { 6503 bloomreachBasketData = JSON.parse('[' + JSON.stringify(bloomreachBasketData) + ']'); 6504 } 6505 if (firebase != undefined) { 6506 firebase.push({ "event": "monetateCart", "monetateCartRows": cartRows }); 6507 var eeCheckoutValues; 6508 if (isShoppingBagPage && dataLayer != undefined) { 6509 dataLayer.push({ "event": "shoppingbag", "total_number_of_items": NextBasket.Data.Items.length, "order_total": NextBasket.Data.FinalOrderValue, "product_skus": productSKUs }); 6510 if (pageType == "shoppingBag") { 6511 dataLayer.push({ "event": "checkout", "ecommerce": { "currencyCode": dataLayerCurrencyCode, "checkout": { "actionField": { "step": 1 }, "products": products } } }); 6512 eeCheckoutValues = { "event": "ee-checkout", "ecommerce": { "currencyCode": Next.Settings.Channel.CurrencyCode, "checkout": { "actionField": { "step": 1, "option": "" }, "products": productList } } }; 6513 EnhancedEcommerceHelper.checkAndPushtoDatalayer("ee-checkout", eeCheckoutValues); 6514 } 6515 } 6516 else if (isDeliveryPage) { 6517 firebase.push({ "event": "checkout", "ecommerce": { "currencyCode": dataLayerCurrencyCode, "checkout": { "actionField": { "step": 2 }, "products": productList } } }); 6518 // eeCheckoutValues = { "event": "ee-checkout", "ecommerce": { "currencyCode": Next.Settings.Channel.CurrencyCode, "checkout": { "actionField": { "step": 2 }, "products": productList } } }; 6519 // EnhancedEcommerceHelper.checkAndPushtoDatalayer("ee-checkout", eeCheckoutValues); 6520 } 6521 else if (isPaymentPage) { 6522 firebase.push({ "event": "checkout", "ecommerce": { "currencyCode": dataLayerCurrencyCode, "checkout": { "actionField": { "step": 3 }, "products": products } } }); 6523 // eeCheckoutValues = { "event": "ee-checkout", "ecommerce": { "currencyCode": Next.Settings.Channel.CurrencyCode, "checkout": { "actionField": { "step": 3 }, "products": productList } } }; 6524 // EnhancedEcommerceHelper.checkAndPushtoDatalayer("ee-checkout", eeCheckoutValues); 6525 } 6526 firebase.push({ "event": "basketData", "items": itemsData }); 6527 if (bloomReachPixelEnabled && bloomreachBasketData.length > 0) { 6528 firebase.push({ "event": "BRBasketdata", "br_basket_data": bloomreachBasketData }); 6529 } 6530 } 6531 } 6532 this.CheckPersonalisedItems(NextBasket.Data.Items); 6533 var pageTypeQry = (pageType === "productPage" || pageType === "BeautyproductPage" || pageType === "productPageModal" || pageType === "searchResults" || pageType === "productListPage" || pageType === "clearanceProductListPage" || pageType === "tabbedcontent" || pageType === "homeWarePDP"); 6534 monetateEnabled = monetateEnabled && (!pageTypeQry || actionType === "add"); 6535 if (rebuildDataLayer && (NextBasket.Data.Items == null || NextBasket.Data.Items.length === 0) && !isCompletePage) { 6536 if (dataLayer != undefined) { 6537 dataLayer.push({ "monetateCartRows": undefined }); 6538 if (isShoppingBagPage) { 6539 dataLayer.push({ "items": undefined }); 6540 dataLayer.push({ "br_basket_data": undefined }); 6541 dataLayer.push({ "ecommerce": undefined }); 6542 dataLayer.push({ "event": "gtm.dom" }); 6543 } 6544 } 6545 if (monetateEnabled) { 6546 window.monetateQ.push(["addCartRows", [{}]]); 6547 } 6548 } 6549 else if (monetateEnabled && rebuildDataLayer && !isCompletePage) { 6550 if (dataLayer != undefined && dataLayer.length > 0) { 6551 for (var k = 0; k < dataLayer.length; k++) { 6552 if (dataLayer[k].monetateCartRows != undefined) { 6553 window.monetateQ.push(["addCartRows", dataLayer[k].monetateCartRows]); 6554 if (productsAddedIntoBag) { 6555 window.monetateQ.push(["addCategories", ['addedToBag']]); 6556 } 6557 ; 6558 break; 6559 } 6560 } 6561 } 6562 } 6563 if (monetateEnabled && rebuildDataLayer) { 6564 var deviceType = "desktop"; 6565 if (dataLayer != undefined && dataLayer.length > 0) { 6566 if (isCompletePage) { 6567 for (var i = 0; i < dataLayer.length; i++) { 6568 if (dataLayer[i].monetatePurchaseRows != undefined) { 6569 window.monetateQ.push(["addPurchaseRows", dataLayer[i].monetatePurchaseRows]); 6570 break; 6571 } 6572 } 6573 } 6574 for (var i = 0; i < dataLayer.length; i++) { 6575 if (dataLayer[i].device_type) { 6576 deviceType = dataLayer[i].device_type; 6577 break; 6578 } 6579 if (dataLayer[i].siteLayout) { 6580 deviceType = dataLayer[i].siteLayout; //sometimes device-type isn't defined 6581 break; 6582 } 6583 } 6584 } 6585 window.monetateQ.push(["setPageType", pageType]); 6586 window.monetateQ.push(["addCategories", [deviceType]]); 6587 window.monetateQ.push(["setCustomVariables", [{ name: "deviceType", value: deviceType }]]); 6588 if (itemNumberAdded != "") { 6589 var friendlyItemNumber = itemNumberAdded.substring(0, 3) + '-' + itemNumberAdded.substring(3); 6590 window.monetateQ.push(["addProductDetails", JSON.parse('["' + friendlyItemNumber + '"]')]); 6591 itemNumberAdded = ""; 6592 } 6593 window.monetateQ.push(["trackData"]); 6594 } 6595 } 6596 else { 6597 this.CheckPersonalisedItems(NextBasket.Data.Items); 6598 if (itemNumberAdded != "") { 6599 itemNumberAdded = ""; 6600 } 6601 } 6602 rebuildDataLayer = false; 6603 if (Next.Settings.Channel.StateConvergence !== undefined && Next.Settings.Channel.StateConvergence.IsEnabled && typeof (NextStateConvergence) != "undefined") { 6604 NextStateConvergence.Execute(); 6605 } 6606 }; 6607 Basket.prototype.LoadError = function (jqXHR, textStatus, errorThrown, actionType, callBack, dataLoadCallback) { 6608 checkAndRedirectForAjaxError(jqXHR, false, 'ShoppingBagCommon.js: ' + actionType); 6609 this.SetGAUp(null); // Call GA with NOTHING so that defaults are resorted to. 6610 if (dataLoadCallback) { 6611 dataLoadCallback(false); 6612 } 6613 if (callBack) { 6614 callBack(false, jqXHR.status); 6615 } 6616 // First, push the error onto the NotifyError array as JSON 6617 this.NotifyError.push({ Data: jqXHR, TextStatus: textStatus, ErrorThrown: errorThrown, ActionType: actionType }); 6618 //On error, there may be more items waiting to be added, move on to these items 6619 if (this.InboundQueue.length) { 6620 var nextAction = this.InboundQueue.shift(); 6621 this.ExecuteBagAction(nextAction); 6622 } 6623 else { 6624 //Else - all items are processed 6625 this.ItemsWaiting = false;
6626 // Now we know we are on the last item, lets handle any errors 6627 this.NotifyErrors(); 6628 } 6629 }; 6630 Basket.prototype.CheckPersonalisedItems = function (items) { 6631 var personalisedGiftInBag = false; 6632 var isGiftItemInBag = false; 6633 if (items != null && items.length > 0) { 6634 for (var i = 0; i < items.length; i++) { 6635 var currentItem = items[i]; 6636 if (currentItem.Personalisation && currentItem.Personalisation === "Y" && currentItem.FulfilmentType !== "F") { 6637 if (Next.Settings.Channel.ResponsiveShoppingBagIsEnabled) { 6638 if (currentItem.FulfilmentType === 'M') { 6639 var IsCustomizableCustomItem = currentItem.CustomItemType && ["Curtain", "Blind"].indexOf(currentItem.CustomItemType.toString()) > -1; 6640 if (IsCustomizableCustomItem) { 6641 personalisedGiftInBag = true; 6642 } 6643 } 6644 else { 6645 personalisedGiftInBag = true; 6646 } 6647 } 6648 else { 6649 personalisedGiftInBag = true; 6650 } 6651 } 6652 if (Next.Settings.Channel.GiftCardMigration) { 6653 var departmentType = currentItem.Department; 6654 if (departmentType && departmentType == "GiftCard") { 6655 isGiftItemInBag = true; 6656 } 6657 } 6658 } 6659 } 6660 if (this.isResponsiveShoppingBag() && (personalisedGiftInBag || isGiftItemInBag)) { 6661 $('[id=divShoppingBagPersonalisation]').css({ 'display': 'block' }); 6662 } 6663 // hide top blue banner if there are no Personalised items 6664 if (!personalisedGiftInBag && !isGiftItemInBag) { 6665 $("#PersonalisedGiftsInBagMessage").hide(); 6666 if (this.isResponsiveShoppingBag()) { 6667 $('[id=divShoppingBagPersonalisation]').removeClass("show"); 6668 } 6669 } 6670 }; 6671 Basket.prototype.ConfirmRedirectModal = function (cancelUrl) { 6672 $("#ChannelRedirect_Form").attr("action", Next.Settings.Channel.SiteUrl + "/ChannelRedirect/ConfirmChannelChange"); 6673 this.ConfirmRedirectButtonInit(); 6674 }; 6675 Basket.prototype.ConfirmRedirectButtonInit = function () { 6676 $(document).on('click', '#ChannelRedirect_Confirm', function () {
6677 $("#ChannelRedirect_Background").css("height", "100%"); 6678 $("#ChannelRedirect_Background").fadeOut("slow"); 6679 }); 6680 }; 6681 Basket.prototype.RegisterChange = function (func) { 6682 this.NotifyChange.push(func); 6683 }; 6684 Basket.prototype.RegisterError = function (func) { 6685 this.NotifyError.push(func); 6686 }; 6687 Basket.prototype.NotifyErrors = function () { 6688 if (this.NotifyError.length) { 6689 ConsoleLog('Errors were detected when receiving a shopping bag response'); 6690 // Loop through each error 6691 for (var i = 0; i < this.NotifyError.length; i++) { 6692 var gaErrorString = "Shopping Bag Error"; 6693 // If we have a 513 status code then the session has been reset the maximum number of times, lets get the new bag and take the user to the error page 6694 if (this.NotifyError[i].Data.status === 513) { 6695 gaErrorString = "Shopping Bag Session Expiry Error"; 6696 } 6697 // Log the error to GA 6698 try { 6699 ConsoleLog(gaErrorString + ": " + this.NotifyError[i].ActionType + " - " + this.NotifyError[i].TextStatus + " - " + this.NotifyError[i].ErrorThrown); 6700 TrackGAEvent(gaErrorString, this.NotifyError[i].ActionType, this.NotifyError[i].TextStatus + " - " + this.NotifyError[i].ErrorThrown, 0); 6701 } 6702 catch (er) { 6703 //ConsoleLog('Error Logging to GA'); 6704 } 6705 } 6706 // Empty the errors no we have dealt with them 6707 this.NotifyError = []; 6708 } 6709 }; 6710 //used during vip to make sure customers are given the option to complete their order when moving between sale site and main
6710site 6711 Basket.prototype.saveBagCookieForVIP = function () { 6712 if (this.Data.ItemCount > 0) { //update cookie if items in bag 6713 var cookieData = "MainSiteBag=Quantity=" + this.Data.ItemCount + "&TotalValue=" + this.Data.OrderGoodsValue + "&SaleSiteRead=False;path=/;"; 6714 document.cookie = cookieData; 6715 } 6716 else { 6717 var nextMainSiteBagCookie = readCookie('MainSiteBag'); 6718 if (nextMainSiteBagCookie) { //check if cookie exists before removing 6719 document.cookie = "MainSiteBag=;expires=" + new Date().toGMTString() + ";path=/;"; 6720 } 6721 } 6722 }; 6723 Basket.prototype.isResponsiveShoppingBag = function () { 6724 var url = window.location.pathname.toLowerCase(); 6725 return Next.Settings.Channel.ResponsiveShoppingBagIsEnabled && 6726 url.indexOf("/shoppingbag") > -1 && 6727 typeof RefreshShoppingBag !== 'function'; 6728 }; 6729 Basket.prototype.pushAddtoBagClickToDataLayer = function (newItemArr) { 6730 var itemsForTracking = newItemArr.map(function (item) { 6731 var itemNumber = String(item.ItemNumber).replace("-", ""); 6732 var itemVariant = (itemNumber + '-' + item.OptionNo + " | " + item.SizeDescription); 6733 return { 6734 id: itemNumber, 6735 name: item.Description, 6736 price: item.Price, 6737 brand: item.ItemBrand, 6738 category: item.ItemCategory, 6739 variant: itemVariant, 6740 position: 1, 6741 quantity: item.Quantity, 6742 dimension21: item.StockStatus, 6743 dimension22: "", 6744 dimension23: item.Department, 6745 dimension24: item.Gender, 6746 dimension25: "", 6747 dimension26: "", 6748 dimension27: item.ItemBrand, 6749 dimension28: item.Colour, 6750 markdownItem: item.IsMarkedDownItem, 6751 titleType: "standard" 6752 }; 6753 }); 6754 dataLayer.push({ 6755 "event": "ee-addToCart", 6756 "ecommerce": { 6757 "currencyCode": Next.Settings.Channel.CurrencyCode, 6758 "add": { 6759 "products": itemsForTracking 6760 } 6761 } 6762 }); 6763 }; 6764 return Basket; 6765 }()); 6766 platmodCommon.Basket = Basket; 6767 function SaveCurrentLocation() { 6768 //save the url in jstorage for the breadcumbs 6769 if ($.jStorage && $.jStorage.storageAvailable()) { 6770 $.jStorage.set("LastPageVisited", window.location.toString()); 6771 } 6772 } 6773 function RebuildDataLayer() { 6774 rebuildDataLayer = true; 6775 setUpGA = false; 6776 if (!Next.Settings.Channel.PlatModGoogleAnalyticsSDK) { 6777 if (dataLayer != undefined && dataLayer.length > 0) { 6778 for (var j = 0; j < dataLayer.length; j++) { 6779 if (dataLayer[j].is_shoppingbag_page != undefined) { 6780 dataLayer.splice(j, 1); 6781 break; 6782 } 6783 } 6784 dataLayer.push({ "is_shoppingbag_page": "true" }); 6785 } 6786 } 6787 } 6788 function ValueRange(value) { 6789 var s = ""; 6790 if (isNaN(value) || value == 0) 6791 s = "0"; 6792 else if (value <= 50) 6793 s = "Under £50"; 6794 else if (value <= 100) 6795 s = "£50 - £100"; 6796 else if (value <= 250) 6797 s = "£100 - £250"; 6798 else if (value <= 500) 6799 s = "£250 - £500"; 6800 else if (value <= 1000) 6801 s = "£500 - £1000"; 6802 else if (value > 1000) 6803 s = "£1000+"; 6804 return s; 6805 } 6806 platmodCommon.ValueRange = ValueRange; 6807})(platmodCommon || (platmodCommon = {})); 6808//# sourceMappingURL=ShoppingBagCommon.js.map; 6809var __extends = (this && this.__extends) || (function () { 6810 var extendStatics = function (d, b) { 6811 extendStatics = Object.setPrototypeOf || 6812 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 6813 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 6814 return extendStatics(d, b); 6815 } 6816 return function (d, b) { 6817 extendStatics(d, b); 6818 function __() { this.constructor = d; } 6819 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 6820 }; 6821})(); 6822var touchDevice = isIOS || isTouchEnabled; 6823var addedToBagTranSpeed = 400; 6824var multipleAddToBagHideTimer; 6825var msVersion = navigator.userAgent.match(/MSIE ([0-9]{1,}[\.0-9]{0,})/); 6826var msie = !!msVersion; 6827var bagIsAnimating = false; 6828var accountDialogIsAnimating = false;
6829var enablePlatModUserVariables = Next.Settings.Channel.PlatModUserVariables && Next.Settings.Channel.EnableModernisedHeader; 6830var platmod; 6831(function (platmod) { 6832 var DesktopBasket = /** @class */ (function (_super) { 6833 __extends(DesktopBasket, _super); 6834 function DesktopBasket() { 6835 "use strict"; 6836 var _this = _super.call(this) || this; 6837 console.log("NEW HEADER DESKTOP/SHOPPINGBAG.TS"); 6838 if (!enablePlatModUserVariables) { 6839 //This method registers the new basket total and sets the value on the homepage. 6840 _this.RegisterChange(function () { 6841 SetSoftLoggedInState(); 6842 }); 6843 } 6844 _this.RegisterChange(function () { 6845 // Desktop Abandoned Basket Banner 6846 if (Next.Settings.Channel.AbandondedBagHomepage) { 6847 var abbBannerShown = window.sessionStorage.getItem('abbBannerShownKey'); 6848 if (abbBannerShown === null) { 6849 var abbInitialVisitBasket = window.sessionStorage.getItem('abbInitialVisitBasketKey'); 6850 if ((_this.Data.ItemCount > 0) && (abbInitialVisitBasket === null)) { 6851 if ($('#abbPlaceHolder').length) { 6852 var abbClosed = window.sessionStorage.getItem('abbClosedKey'); 6853 if (abbClosed === null) { 6854 BuildAbandonedBagBannerMH(); 6855 $('#abbCloseLink').on("click", AbandonedBagBannerCloseLinkMH); 6856 window.sessionStorage.setItem('abbBannerShownKey', 'true'); 6857 } 6858 } 6859 } 6860 else { 6861 window.sessionStorage.setItem('abbInitialVisitBasketKey', 'true'); 6862 } 6863 } 6864 } 6865 // Shopping Bag Dwell Prompt 6866 if (Next.Settings.Channel.ShoppingBagReEngagement != null && Next.Settings.Channel.ShoppingBagReEngagement.Enabled) { 6867 var setShoppingBagDwellPrompt = function (Items) { 6868 { 6869 if (Items.length > 0) { 6870 var pathname = window.location.pathname; 6871 if (pathname.indexOf('shoppingbag') > -1) { 6872 var shoppingBagDwellPromptDisplayed = sessionStorage.getItem("shoppingBagDwellPromptDisplayed"); 6873 if (shoppingBagDwellPromptDisplayed === null) { 6874 var dwellPromptTimeoutDuration = (Math.floor(Next.Settings.Channel.ShoppingBagReEngagement.DwellPromptTimeoutSecs)) * 1000; 6875 var inStockItems = Items.filter(function (x) { return !Next.Settings.Channel.NextUnlimitedItems.find(function (nu) { return nu.ItemNumberWithoutHyphen === x.ItemNumber; }) && x.StockStatus === 'instock' || x.StockStatus === 'delayed'; }); 6876 if (inStockItems.length > 0) { 6877 TimerEventMH(dwellPromptTimeoutDuration); 6878 } 6879 } 6880 } 6881 } 6882 } 6883 }; 6884 if (Next.Settings.Channel.ResponsiveShoppingBagIsEnabled) { 6885 setShoppingBagDwellPrompt(NextBasket.Data.Items); 6886 } 6887 else { 6888 setShoppingBagDwellPrompt(_this.Data.Items); 6889 } 6890 } 6891 }); 6892 //free delivery with next unlimited modal popup 6893 if (Next.Settings.Channel.FreeDeliveryWithNextUnlimitedEnabled) { 6894 var isNUModalActive_1 = false; 6895 $(document).on("click", "#next-unlimited-delivery", function (e) { 6896 event.preventDefault(); 6897 if (!isNUModalActive_1) { 6898 isNUModalActive_1 = true; 6899 $.ajax({
6900 url: Next.Settings.Channel.SiteUrl + "/NextUnlimitedModal/desktop", 6901 dataType: "HTML", 6902 type: "GET", 6903 cache: true, 6904 success: function (data, textStatus, jqXHR) { 6905 isNUModalActive_1 = false; 6906 var desktopElemModal = $(document.createElement("div")); 6907 desktopElemModal.html(data); 6908 $(desktopElemModal).find(".nu-modal-TCs-apply").attr("href", Next.Settings.Channel.SiteUrl + "/Unlimited"); 6909 $(desktopElemModal).find(".nu-modal-add-to-bag").attr("href", Next.Settings.Channel.SiteUrl + "/addtobag/?itemnumbers=17562401"); 6910 if (NextBasket.Data.Items.some(function (p) { return p.IsNextUnlimitedItem; }) || NextBasket.NextUnlimited.IsExistingNUCustomer) { 6911 $(desktopElemModal).find(".nu-modal-add-to-bag").hide(); 6912 } 6913 var $modal = ModalPopup.init({ 6914 width: 600, 6915 height: "auto", 6916 fixedTopPosition: "25%", 6917 fullyLockBackgroundScroll: true, 6918 modalDataSet: [] 6919 }); 6920 $modal.html($(desktopElemModal).html()); 6921 ModalPopup.show(); 6922 }, 6923 error: function (xhr, ajaxOptions, thrownError) { 6924 isNUModalActive_1 = false; 6925 ConsoleLog("Desktop Partial for Next Unlimited on Shopping Bag Modal did not load: " + thrownError); 6926 } 6927 }); 6928 } 6929 }); 6930 } 6931 return _this; 6932 } //Constructor 6933 DesktopBasket.prototype.NotifyItemAdded = function (itemObj) { 6934 this.OutboundQueue.shift(); 6935 this.NotifyWaiting = false; 6936 }; 6937 DesktopBasket.prototype.Notify = function (returnedData) { 6938 "use strict"; 6939 TrackGAEvent("Saved Shopping Bag", "Bag Recovered", "Count", 0); 6940 TrackGAEvent("Saved Shopping Bag", "Bag Quantity", returnedData.ItemCount, 0); 6941 TrackGAEvent("Saved Shopping Bag", "Bag Age", returnedData.OldShoppingBagAge, 0); 6942 TrackGAEvent("Saved Shopping Bag", "Bag Value", platmodCommon != undefined ? platmodCommon.ValueRange(returnedData.FinalOrderValue) : ValueRange(returnedData.FinalOrderValue), 0); 6943 }; //Class Functions 6944 DesktopBasket.prototype.DisplayRebuildNotification = function (returnedData) { 6945 "use strict"; 6946 TrackGAEvent("Saved Shopping Bag", "Bag Recovered", "Count", 0); 6947 TrackGAEvent("Saved Shopping Bag", "Bag Quantity", returnedData.ItemCount, 0); 6948 TrackGAEvent("Saved Shopping Bag", "Bag Age", returnedData.OldShoppingBagAge, 0); 6949 TrackGAEvent("Saved Shopping Bag", "Bag Value", platmodCommon != undefined ? platmodCommon.ValueRange(returnedData.FinalOrderValue) : ValueRange(returnedData.FinalOrderValue), 0); 6950 }; 6951 return DesktopBasket; 6952 }(platmodCommon.Basket)); //Class 6953 platmod.DesktopBasket = DesktopBasket; 6954 if (!enablePlatModUserVariables) { 6955 // If true, PlatMod Header will take responsibility of this function making use of the bag/get call. 6956 function SetSoftLoggedInState() { 6957 //need to check if there is a soft login state 6958 var firstName = NextBasket.Data.FirstName; 6959 var userName = NextBasket.Data.Name; 6960 if (userName || firstName) { 6961 window.sessionStorage.removeItem("RPID"); 6962 window.sessionStorage.removeItem("AcctType"); 6963 window.sessionStorage.removeItem("unlimitedCustomer"); 6964 window.sessionStorage.setItem("RPID", NextBasket.RoamingProfileId); 6965 if (NextBasket.Data.Authenticated) {
6966 var NextUnlimitedStatus = NextBasket.Data.NextUnlimitedStatus == 0 ? false : true; 6967 window.sessionStorage.setItem("AcctType", NextBasket.Data.AccountType); 6968 window.sessionStorage.setItem("unlimitedCustomer", NextUnlimitedStatus.toString()); 6969 if (firebase != undefined) { 6970 firebase.transferWebEventToApp("authenticated", NextBasket.Data.Authenticated.toString()); 6971 } 6972 } 6973 } 6974 } 6975 } 6976})(platmod || (platmod = {})); 6977//Helper methods 6978var NextBasket = new platmod.DesktopBasket(); 6979function BuildAbandonedBagBannerMH() { 6980 var abbWrapper = $("<div />"); 6981 $(abbWrapper).addClass("abb-wrapper"); 6982 $(abbWrapper).attr('id', 'abbWrapper'); 6983 var abbContainer = $("<div />"); 6984 $(abbContainer).addClass("abb-container"); 6985 var abbContainerContent = $("<div />"); 6986 $(abbContainerContent).addClass("abb-container-content"); 6987 var abbContentWrapper = $("<div />"); 6988 $(abbContentWrapper).addClass("abb-content-wrapper"); 6989 // Build Image Container 6990 var abbContentImgContainer = $("<div />"); 6991 $(abbContentImgContainer).addClass("abb-content-img-container"); 6992 var abbBagItems = NextBasket.Data["Items"]; 6993 abbBagItems.sort(function (a, b) { return b.ItemID - a.ItemID; }); 6994 var abbNumBagItems = 3; 6995 if (abbBagItems.length < 3) { 6996 abbNumBagItems = abbBagItems.length; 6997 } 6998 for (var j = 0; j < abbNumBagItems; j++) { 6999 var abbImage = BuildAbandonedBagImageElementMH(abbBagItems[j]); 7000 $(abbContentImgContainer).append(abbImage); 7001 } 7002 var abbContentInfoContainer = $("<div />"); 7003 $(abbContentInfoContainer).addClass("abb-content-info-container"); 7004 var abbContentInfoTop = $("<div />"); 7005 $(abbContentInfoTop).addClass("abb-content-info-top"); 7006 var abbIconBasket = $("<div />"); 7007 $(abbIconBasket).addClass("abb-icon-basket"); 7008 var abbIconBasketImage = $("<div />"); 7009 $(abbIconBasketImage).addClass("abb-icon-basket-image"); 7010 $(abbIconBasket).append(abbIconBasketImage); 7011 var abbIconText = $("<div />"); 7012 $(abbIconText).addClass("abb-icon-text"); 7013 var abbIconTextPara = $("<p />"); 7014 var abbBagItemCount = NextBasket.Data["ItemCount"]; 7015 var abbIconTextContent = ((abbBagItemCount !== 1) ? 7016 abbBagItemCount + " Items" : 7017 abbBagItemCount + " Item") + " in your "; 7018 var abbIconTextShopBagLinkText = "Shopping Bag"; 7019 var abbIconTextShopBagLinkURL = Next.Settings.Channel.SiteUrl + "/shoppingbag"; 7020 var abbIconTextShopBagLink = $("<a></a>") 7021 .text(abbIconTextShopBagLinkText) 7022 .attr("href", abbIconTextShopBagLinkURL); 7023 $(abbIconTextPara).append(abbIconTextContent); 7024 $(abbIconTextPara).append(abbIconTextShopBagLink); 7025 $(abbIconText).append(abbIconTextPara); 7026 $(abbContentInfoTop).append(abbIconBasket); 7027 $(abbContentInfoTop).append(abbIconText); 7028 var abbContentInfoBot = $("<div />"); 7029 $(abbContentInfoBot).addClass("abb-content-info-bot"); 7030 var abbContentInfoBotPara = $("<p />"); 7031 var abbContentInfoBotText = "Complete your order now before it's too late..."; 7032 $(abbContentInfoBotPara).text(abbContentInfoBotText); 7033 $(abbContentInfoBot).append(abbContentInfoBotPara); 7034 $(abbContentInfoContainer).append(abbContentInfoTop); 7035 $(abbContentInfoContainer).append(abbContentInfoBot); 7036 if (abbNumBagItems == 1) { 7037 $(abbContentImgContainer).addClass("abb-content-image-1"); 7038 $(abbContentInfoContainer).addClass("abb-content-info-1"); 7039 } 7040 else if (abbNumBagItems == 2) { 7041 $(abbContentImgContainer).addClass("abb-content-image-2"); 7042 $(abbContentInfoContainer).addClass("abb-content-info-2"); 7043 } 7044 $(abbContentWrapper).append(abbContentImgContainer); 7045 $(abbContentWrapper).append(abbContentInfoContainer); 7046 $(abbContainerContent).append(abbContentWrapper); 7047 var abbCloseLinkContainer = $("<div />"); 7048 $(abbCloseLinkContainer).addClass("abb-close-link-container"); 7049 $(abbCloseLinkContainer).attr('id', 'abbCloseLink'); 7050 var abbCloseLinkWrapper = $("<div />"); 7051 $(abbCloseLinkWrapper).addClass("abb-close-link-wrapper"); 7052 $(abbCloseLinkWrapper).append(abbCloseLinkContainer); 7053 // abb checkout button 7054 var abbContainerCTA = $("<div />"); 7055 $(abbContainerCTA).addClass("abb-container-cta"); 7056 var abbCheckoutTxt = nxt.jstranslations.shoppingbagts.checkout; 7057 var abbCheckoutLinkURL = Next.Settings.Channel.CheckoutURL; 7058 var abbCheckoutBtn = $("<a></a>").addClass("abb-btn-checkout") 7059 .text(abbCheckoutTxt) 7060 .attr("href", abbCheckoutLinkURL); 7061 $(abbContainerCTA).append(abbCloseLinkWrapper); 7062 $(abbContainerCTA).append(abbCheckoutBtn); 7063 $(abbContainer).append(abbContainerContent); 7064 $(abbContainer).append(abbContainerCTA); 7065 $(abbWrapper).append(abbContainer); 7066 $('#abbPlaceHolder').append(abbWrapper); 7067} 7068function BuildAbandonedBagImageElementMH(curItem) { 7069 var abbItemImage = $("<img />"); 7070 var abbImgUrl;
7071 if (curItem.SaleClearanceSofaImage != "" && curItem.SaleClearanceSofaImage != null) { 7072 abbImgUrl = curItem.SaleClearanceSofaImage; 7073 } 7074 else { 7075 if (curItem.ItemCategory === "sofas" && curItem.LinkedItems != null) { 7076 abbImgUrl = cdnURL + "/SofaContent/Item_AddToBag/" + curItem.ItemNumber + '_' + curItem.OptionNo + (curItem.LinkedItems.length > 0 && curItem.LinkedItems[0].ItemNumber !== "" ? '_' + curItem.LinkedItems[0].ItemNumber : "") + ".jpg"; 7077 } 7078 else if (curItem.ItemCategory === "diningchairs") { 7079 abbImgUrl = cdnURL + "/ChairContent/Item_AddToBag/" + curItem.ItemNumber + '_' + curItem.OptionNo; 7080 abbImgUrl += (curItem.LinkedItems.length > 0 && curItem.LinkedItems[0].ItemNumber !== "" ? '_' + curItem.LinkedItems[0].ItemNumber : ""); 7081 abbImgUrl += ".jpg"; 7082 } 7083 else if (curItem.ItemCategory === "beds") { 7084 abbImgUrl = cdnURL + "/BedContent/Item_AddToBag/" + curItem.ItemNumber + '_' + curItem.OptionNo; 7085 abbImgUrl += (curItem.LinkedItems.length > 0 && curItem.LinkedItems[0].ItemNumber !== "" ? '_' + curItem.LinkedItems[0].ItemNumber : ""); 7086 abbImgUrl += ".jpg"; 7087 } 7088 else if (curItem.Personalisation === "Y") { 7089 var abbItemContainsImage = false; 7090 // obtain personalised gifts image for shopping bag 7091 for (var i = 0; i < curItem.PersonalisationFields.length; i++) { 7092 if (curItem.PersonalisationFields[i].Field == "image") { 7093 abbItemContainsImage = true; 7094 abbImgUrl = curItem.PersonalisationFields[i].Value; 7095 break; 7096 } 7097 } 7098 // obtain Custom item image for shopping bag 7099 for (var i = 0; i < curItem.CustomItemFields.length; i++) { 7100 if (curItem.CustomItemFields[i].Field == "image") { 7101 abbItemContainsImage = true; 7102 abbImgUrl = curItem.CustomItemFields[i].Value; 7103 break; 7104 } 7105 } 7106 if (!abbItemContainsImage) { 7107 abbImgUrl = cdnURL + "/common/items/default/default/itemimages/altitembag/" + curItem.ItemNumber + ".jpg"; 7108 } 7109 } 7110 else { 7111 abbImgUrl = cdnURL + "/common/items/default/default/itemimages/altitembag/" + curItem.ItemNumber + ".jpg"; 7112 } 7113 } 7114 $(abbItemImage).attr('src', abbImgUrl); 7115 return abbItemImage; 7116} 7117function AbandonedBagBannerCloseLinkMH() { 7118 window.sessionStorage.setItem('abbClosedKey', 'true'); 7119 $('#abbWrapper').remove(); 7120 return false; 7121} 7122function TimerEventMH(dwellPromptTimeoutDuration) { 7123 // Set Dwell Prompt Session key 7124 sessionStorage.setItem("shoppingBagDwellPromptDisplayed", "true"); 7125 // Load Dwell Prompt HTML Partial 7126 $.ajax({
7127 url: Next.Settings.Channel.SiteUrl + "/shoppingbagprompt/desktop", 7128 dataType: "HTML", 7129 type: "GET", 7130 cache: true, 7131 success: function (data, textStatus, jqXHR) { 7132 setTimeout(function () { 7133 // Get the shopping bag items again - just in case there have been changes since timeout initiation 7134 var inStockItems = NextBasket.Data.Items.filter(function (x) { return !Next.Settings.Channel.NextUnlimitedItems.find(function (nu) { return nu.ItemNumberWithoutHyphen === x.ItemNumber; }) && x.StockStatus === 'instock' || x.StockStatus === 'delayed'; }); 7135 // If the Dwell Prompt Criteria are still met try to create Dwell Prompt modal 7136 if (inStockItems.length > 0) { 7137 var inStockItem = inStockItems[0]; 7138 var description = null; 7139 var formattedPrice = null; 7140 var itemNumber = null; 7141 var imageURL = null; 7142 if (inStockItem.Description) { 7143 description = inStockItem.Description; 7144 } 7145 if (inStockItem.PriceFormatted) { 7146 formattedPrice = inStockItem.PriceFormatted; 7147 } 7148 if (inStockItem.ItemNumber) { 7149 itemNumber = inStockItem.ItemNumber; 7150 } 7151 if (itemNumber != null && Next.Settings.Channel.ResponsiveShoppingBagIsEnabled) { 7152 imageURL = $(".sbm-item[data-itemnumber=" + itemNumber + "]").find(".detailed-image-url").attr("data-detailed-image-url"); 7153 } 7154 else if ($("tr[data-itemnumber=" + itemNumber + "] > td.imageColumn > a") && itemNumber != null) { 7155 imageURL = $("tr[data-itemnumber=" + itemNumber + "] > td.imageColumn > a").attr("href"); 7156 } 7157 // If all required Modal data available show Dwell Prompt Modal 7158 if (description != null && formattedPrice != null && imageURL != null) { 7159 // Get Monetate Key - used for testing 7160 var shoppingBagDwellPromptMonetateKey = sessionStorage.getItem("shoppingBagDwellPromptMonetateKey"); 7161 // Monetate if active, toggles Dwell Prompt by setting key to "control" to suppress or "test" to show modal 7162 if ((shoppingBagDwellPromptMonetateKey === null) || (shoppingBagDwellPromptMonetateKey === "test")) { 7163 // Remove any previous modals on page 7164 if ($('.ModalPopupWrapper').length > 0) { 7165 $('.ModalPopupWrapper').remove(); 7166 $('.ModalPopupContainer').remove(); 7167 } 7168 var desktopElemModal = $(document.createElement('div')); 7169 desktopElemModal.html(data); 7170 $(desktopElemModal).find(".sb-modal-text-product").text(description); 7171 $(desktopElemModal).find(".sb-modal-text-price").text(formattedPrice); 7172 if (Next.Settings.Channel.ResponsiveShoppingBagIsEnabled) { 7173 var add3_4ImageClass = $(".sbm-item[data-itemnumber=" + itemNumber + "]").find(".detailed-image-url").attr("data-image-3_4-class"); 7174 if (add3_4ImageClass.toLowerCase() === 'true') { 7175 $(desktopElemModal).find(".sb-modal-container").addClass("dwell-image-3-4-class"); 7176 } 7177 } 7178 $(desktopElemModal).find(".sb-modal-image-container").html('<img class="sb-modal-image" src="' + imageURL + '" alt="' + description + '">'); 7179 var $modal = ModalPopup.init({ 7180 width: 640, 7181 height: "auto", 7182 fixedTopPosition: "25%",
7183 fullyLockBackgroundScroll: true, 7184 modalDataSet: [] 7185 }); 7186 $modal.html($(desktopElemModal).html()); 7187 ModalPopup.show(); 7188 } 7189 } 7190 } 7191 }, dwellPromptTimeoutDuration); 7192 }, 7193 error: function (xhr, ajaxOptions, thrownError) { 7194 ConsoleLog('Desktop Partial for Dwell Prompt on Shopping Bag Modal did not load: ' + thrownError); 7195 } 7196 }); 7197} 7198//# sourceMappingURL=ShoppingBag.js.map; 7199 7200var SoftwareVersion = ""; 7201var HeaderTrace = ""; 7202 7203// Enable support for CORS (Cross Origin Resource Sharing). 7204jQuery.support.cors = true; 7205 7206var HidePrimaryNavigation = false; 7207 7208 7209function ShowBreadcrumbBar(Animation) { 7210 addToAnimation(Animation, '$(".PageHeader .BreadcrumbNavigation").fadeIn(250);', 250, true); 7211 7212 return Animation; 7213} 7214 7215function HideBreadcrumbBar(Animation) { 7216 addToAnimation(Animation, '$(".PageHeader .BreadcrumbNavigation").fadeIn(250);', 250, true); 7217 7218 return Animation; 7219} 7220 7221function UpdateBreadcrumbTrail(BCHTML) { 7222 $("section.Breadcrumbs").html(BCHTML); 7223 7224 $("ul.Breadcrumbs li").last().addClass("lastBreadcrumb"); 7225 7226 BreadcrumbResize(); 7227} 7228 7229//Function determines the available space for the breadcrumb navigation, and sets a width dependant on this 7230//If there is not enough space for the full breadcrumb, then the overflow is hidden, and an ellipsis is displayed 7231function BreadcrumbResize() { 7232 7233 //Static variables 7234 var bcrumbHolderWidth = 890; //bcrumbholderwidth set to the full available space for the breadcrumb, if no socialbookmarks are displayed 7235 var SocialBookmarkWidth = 160; //variable also includes additional spacing so that the breadcrumb and socialbookmarks have space between them 7236 7237 //Element variables 7238 var $bcrumbHolder = $(".BreadcrumbsHolder"); 7239 var $bcrumbList = $bcrumbHolder.find("ul.Breadcrumbs"); 7240 var $bcrumbLi = $bcrumbList.find("li"); 7241 var $bcrumbEllipsis = $(".BreadcrumbEllipsis"); 7242 7243 7244 if (AddThisEnabled) { 7245 //AddThis is enabled, therefore reduced the bcrumbholder width by the width of the socialbookmarkwidth variable 7246 bcrumbHolderWidth = bcrumbHolderWidth - SocialBookmarkWidth; 7247 } 7248 7249 $bcrumbHolder.width(bcrumbHolderWidth); 7250 7251 //Following loop determines the total length of all the Li elements matching the $bcrumbLi variable selector. 7252 var totalLiLength = 0; 7253 $bcrumbLi.each(function (i, e) { 7254 totalLiLength += $(e).outerWidth(true) + 2; //+2 has been added as a fix for IE7. Without this, it seems the outerWidth() for IE7 can be too small. 7255 }); 7256 7257 7258 //If the totalLiLength > $bcrumbholder width, then a width needs to be set on the $bcrumbList, and the ellipsis should be shown. 7259 if (totalLiLength >= $bcrumbHolder.width()) { 7260 $bcrumbList.width(totalLiLength); 7261 $bcrumbEllipsis.show(); 7262 } 7263 //Else statements reverse changes made in the If statement 7264 else { 7265 $bcrumbEllipsis.hide(); 7266 } 7267 7268 if (dataLayer != undefined) { 7269 var department = ""; 7270 var category = ""; 7271 var spread = ""; 7272 var bloomreachCategoryId = ""; 7273 7274 // TODO new platform mod way of tracking active department. 7275 7276 var activeDepartment = $(".Department.currentDepartment .DepartmentLink"); 7277 if (activeDepartment.length) { 7278 if (activeDepartment[0].pathname != undefined) { 7279 department = activeDepartment[0].pathname.replace(/\//gi, ""); 7280 } 7281 } 7282 7283 var collection = $(".spreadImageWrapper a"); 7284 if (collection.length > 1) { 7285 spread = collection[1]; 7286 if (spread != undefined) { 7287 var urlSplitData = spread.pathname.split("/"); 7288 if (urlSplitData != undefined) { 7289 if (urlSplitData.length > 1 && department === "") { 7290 department = urlSplitData[1]; 7291 } 7292 category = spread.innerText.replace(/"/gi, ""); 7293 } 7294 } 7295 } 7296 7297 var breadCrumb = $("section.Breadcrumbs"); 7298 7299 if (breadCrumb != undefined) { 7300 var breadCrumbList = breadCrumb.find("li"); 7301 if (breadCrumbList != undefined) { 7302 breadCrumbList.each(function (i, e) { 7303 if (i > 0) { 7304 if (i === 1 && department === "") { 7305 var firstLink = $(e).find("a"); 7306 department = $(e).text().replace(/"/gi, ""); 7307 if (firstLink.length) { 7308 if (firstLink[0].pathname != undefined) { 7309 department = firstLink[0].pathname.replace(/\//gi, ""); 7310 } 7311 } 7312 7313 if (department === "search") { 7314 category = $(e).text().replace(/"/gi, ""); 7315 } 7316 } 7317 if (i === 2) { 7318 var secondLink = $(e).find("a"); 7319 if (secondLink.length) { 7320 if (secondLink[0].innerText != undefined) { 7321 category = secondLink[0].innerText.replace(/"/gi, ""); 7322 } 7323 } else { 7324 category = $(e).text().replace(/"/gi, ""); 7325 } 7326 } 7327 } 7328 }); 7329 } 7330 } 7331 7332 if (category === "") { 7333 category = department; 7334 } 7335 7336 var pageType = ""; 7337 var bloomReachPixelEnabled = false; 7338 for (var j = 0; j < dataLayer.length; j++) { 7339 if (dataLayer[j].page_type != undefined) { 7340 pageType = dataLayer[j].page_type; 7341 } 7342 if (dataLayer[j].br_pixel_enabled != undefined) { 7343 bloomReachPixelEnabled = dataLayer[j].br_pixel_enabled === "true"; 7344 } 7345 } 7346 if (!Next.Settings.Channel.PlatModGoogleAnalyticsSDK) { 7347 if (pageType !== "") { 7348 if (pageType === department + "LandingPage" || pageType === "storefront") { 7349 dataLayer.push({ "event": "storefront", "department": department.toLowerCase(), "category": category.toLowerCase() }); 7350 7351 if (bloomReachPixelEnabled && category.toLowerCase() === "sofa collections") { 7352 var sofaCategoryId, sofaCategory; 7353 7354 if ($(".br_data-taxonomy-enabled").val().toLowerCase() === "true") { 7355 sofaCategoryId = $(".br_data-sofa-cat_id").val(); 7356 sofaCategory = $(".br_data-sofa-cat").val(); 7357 } 7358 else { 7359 sofaCategoryId = "homehomewaresofasandchairs"; 7360 sofaCategory = "home|homeware|sofasandchairs"; 7361 } 7362 7363 dataLayer.push({ "br_cat_id": sofaCategoryId, "br_cat": encodeURIComponent(sofaCategory) }); 7364 } 7365 7366 } else if (pageType === "spread") { 7367 dataLayer.push({ "event": "spread", "department": department.toLowerCase(), "category": category.toLowerCase() }); 7368 } else if (pageType === "productListPage" || pageType === "searchResults") { 7369 7370 if (bloomReachPixelEnabled) { 7371 var bloomreachCategoryId = $(".br_data-cat_id").val(); 7372 var bloomreachCategory = $(".br_data-cat").val(); 7373 7374 if (typeof bloomreachCategoryId !== "undefined" && typeof bloomreachCategory !== "undefined") { 7375 dataLayer.push({ "br_cat_id": bloomreachCategoryId, "br_cat": encodeURIComponent(bloomreachCategory) }); 7376 } 7377 } 7378 7379 if (pageType === "searchResults") { 7380 department = "search"; 7381 } 7382 dataLayer.push({ "event": "searchlanding", "department": department.toLowerCase(), "category": category.toLowerCase() }); 7383 } else if (pageType === "productPage" || pageType === "BeautyproductPage" || pageType === "productPageModal" || pageType === "homeWarePDP") { 7384 7385 if ($("article.Style").length > 0) { 7386 var item = $("article.Style.Selected"); 7387 var pDepartment = $(item).data("department") || ""; 7388 var departmentForDataLayer = $(item).data("departmentfordatalayer");
7389 if (pDepartment == "NOT_IN_USE" && (departmentForDataLayer != "" && departmentForDataLayer != undefined)) { 7390 pDepartment = departmentForDataLayer; 7391 } 7392 var pcategory = $(item).data("category"); 7393 dataLayer.push({ "department": pDepartment.toLowerCase(), "category": pcategory.toLowerCase() }); 7394 } 7395 else { 7396 7397 dataLayer.push({ "department": department.toLowerCase(), "category": category.toLowerCase() }); 7398 } 7399 7400 } 7401 } 7402 } else { 7403 if (window.subjects && window.subjects["$ GA_TRACK_PAGE_LOAD"]) { 7404 window.subjects["$ GA_TRACK_PAGE_LOAD"].next({ department: department, category: category }) 7405 } 7406 else { 7407 console.log("Error Tracking GA Page Load") 7408 } 7409 } 7410 } 7411} 7412 7413 7414; 7415function CreateCarouselLoopTimer(CSelector, Direction, Interval) { 7416 return window.setTimeout('ChangePage("' + CSelector + '","' + Direction + '","",' + Interval + ')', Interval); 7417} 7418 7419function ChangePage(CSelector, Direction, PageIndex, AutoScroll) { 7420 7421 if (!$("body").hasClass("Focus") && AutoScroll != 0 || PageTransitionLock == true) { 7422 eval(CSelector.replace('#','') + 'Timer = CreateCarouselLoopTimer("' + CSelector + '", "+", ' + AutoScroll + ')'); 7423 return false; 7424 } 7425 7426 if (CSelector == "" || Direction == "" && PageIndex == "" ) { 7427 return false; 7428 } 7429 7430 if (!$(CSelector).hasClass("Locked")) { 7431 var currentPageIndex; 7432 var PageDirection = Direction; 7433 var MovementDirection; 7434 7435 $(CSelector + " .CControlPage").each(function (index) { 7436 7437 if ($(CSelector + " .CControlPage:eq(" + index + ")").hasClass("Current")) { 7438 currentPageIndex = index; 7439 } 7440 }); 7441 7442 7443 if (PageIndex != "") { 7444 //PageIndex = PageIndex - 1; 7445 if (PageIndex > currentPageIndex) { 7446 PageDirection = "+"; 7447 } else { 7448 PageDirection = "-"; 7449 } 7450 } 7451 7452 switch (PageDirection) { 7453 case "+": 7454 MovementDirection = "-"; 7455 break; 7456 case "-": 7457 MovementDirection = "+"; 7458 break; 7459 } 7460 7461 var nextPageIndex 7462 if (PageIndex == "") { 7463 nextPageIndex = eval(currentPageIndex + PageDirection + "1"); 7464 } else { 7465 nextPageIndex = PageIndex; 7466 } 7467 7468 if (nextPageIndex == currentPageIndex) { 7469 return false; 7470 } else { 7471 if (nextPageIndex < 0) { 7472 nextPageIndex = ($(CSelector + " .CControlPage").length -1) 7473 MovementDirection = "-"; 7474 } else { 7475 if (nextPageIndex >= $(CSelector + " .CControlPage").length) { 7476 nextPageIndex = 0; 7477 MovementDirection = "+"; 7478 } 7479 } 7480 } 7481 7482 var numPagesToMove 7483 if (nextPageIndex > currentPageIndex) { 7484 numPagesToMove = nextPageIndex - currentPageIndex; 7485 } else { 7486 numPagesToMove = (currentPageIndex - nextPageIndex); 7487 } 7488 7489 var Animation = createAnimation(); 7490 addToAnimation(Animation, '$("' + CSelector + '").addClass("Locked");', 0, false); 7491 7492 addToAnimation(Animation, '$("' + CSelector + ' .CControlPage.Current").removeClass("Current")', 0, false); 7493 addToAnimation(Animation, '$("' + CSelector + ' .CControlPage:eq(' + nextPageIndex + ')").addClass("Current")', 0, false); 7494 7495 addToAnimation(Animation, '$("' + CSelector + ' .CContent").animate({"' + Next.Settings.CSS.left + '": "' + MovementDirection + '=' + ($(CSelector).width() * numPagesToMove) + '"},500)', 500, true); 7496 7497 addToAnimation(Animation, '$("' + CSelector + '").removeClass("Locked");', 0, true); 7498 if (AutoScroll != 0) { 7499 addToAnimation(Animation, CSelector.replace('#','') + 'Timer = CreateCarouselLoopTimer("' + CSelector + '", "+", ' + AutoScroll + ')', 0, false); 7500 } 7501 runAnimation(Animation,true); 7502 } 7503} 7504 7505function CarouselCreateLoopTimer(CSelector, Direction, Interval) { 7506 7507 var options = { 7508 TargetElement: CSelector 7509 } 7510 CarouselTouch.Init(options); 7511 //AddTouch(CSelector); 7512 return window.setTimeout('CarouselChangePage("' + CSelector + '","' + Direction + '","",' + Interval + ')', Interval); 7513 7514} 7515 7516function CarouselChangePage(CSelector, Direction, PageIndex, AutoScroll) { 7517 //debugger; 7518 7519 //ConsoleLog("Change Page"); 7520 7521 if (!$("body").hasClass("Focus") && AutoScroll != 0 || PageTransitionLock == true) { 7522 eval('Timer' + CSelector.replace('#', '') + ' = CarouselCreateLoopTimer("' + CSelector + '", "+", ' + AutoScroll + ')'); 7523 //ConsoleLog(1); 7524 return false; 7525 } 7526 7527 7528 7529 if (CSelector == "" || Direction == "" && PageIndex == "") { 7530 //ConsoleLog(2); 7531 return false; 7532 } 7533 if (!$(CSelector).hasClass("Locked")) { 7534 7535 var currentPageIndex; 7536 var PageDirection = Direction; 7537 var MovementDirection; 7538 7539 $(CSelector + " .PageButton").each(function (index) { 7540 if ($(CSelector + " .PageButton:eq(" + index + ")").hasClass("Current")) { 7541 currentPageIndex = index; 7542 } 7543 }); 7544 7545 if (PageIndex != "") { 7546 //PageIndex = PageIndex - 1; 7547 if (PageIndex > currentPageIndex) { 7548 PageDirection = "+"; 7549 } else { 7550 PageDirection = "-"; 7551 } 7552 } 7553 7554 switch (PageDirection) { 7555 case "+": 7556 MovementDirection = "-"; 7557 break; 7558 case "-": 7559 MovementDirection = "+"; 7560 break; 7561 } 7562 7563 var nextPageIndex 7564 if (PageIndex == "") { 7565 nextPageIndex = eval(currentPageIndex + PageDirection + "1"); 7566 } else { 7567 nextPageIndex = PageIndex; 7568 } 7569 7570 if (nextPageIndex == currentPageIndex) { 7571 return false; 7572 } else { 7573 if (nextPageIndex < 0) { 7574 nextPageIndex = ($(CSelector + " .PageButton").length - 1) 7575 MovementDirection = "-"; 7576 } else { 7577 if (nextPageIndex >= $(CSelector + " .PageButton").length) { 7578 nextPageIndex = 0; 7579 MovementDirection = "+"; 7580 } 7581 } 7582 } 7583 7584 var numPagesToMove 7585 if (nextPageIndex > currentPageIndex) { 7586 numPagesToMove = nextPageIndex - currentPageIndex; 7587 } else { 7588 numPagesToMove = (currentPageIndex - nextPageIndex); 7589 } 7590 7591 if (!$(CSelector).hasClass("Paused") || ($(CSelector).hasClass("Paused") && AutoScroll == 0)) { 7592 var Animation = createAnimation(); 7593 addToAnimation(Animation, '$("' + CSelector + '").addClass("Locked");', 0, false); 7594 7595 addToAnimation(Animation, '$("' + CSelector + ' .PageButton.Current").removeClass("Current")', 0, false); 7596 addToAnimation(Animation, '$("' + CSelector + ' .PageButton:eq(' + nextPageIndex + ')").addClass("Current")', 0, false); 7597 7598 var NewPosition = $(CSelector).width() * nextPageIndex; 7599 7600 addToAnimation(Animation, '$("' + CSelector + ' .Pages").animate({"' + Next.Settings.CSS.left + '": "-' + NewPosition + '"},500)', 500, true); 7601 7602 addToAnimation(Animation, '$("' + CSelector + '").removeClass("Locked");', 0, true); 7603 if (AutoScroll != 0) { 7604 addToAnimation(Animation, 'Timer' + CSelector.replace('#', '') + ' = CarouselCreateLoopTimer("' + CSelector + '", "+", ' + AutoScroll + ')', 0, false); 7605 } 7606 runAnimation(Animation, true); 7607 } 7608 if ($(CSelector).hasClass("Paused") && AutoScroll != 0) { 7609 eval('Timer' + CSelector.replace('#', '') + ' = CarouselCreateLoopTimer("' + CSelector + '", "+", ' + AutoScroll + ')'); 7610 } 7611 } 7612} 7613 7614function PauseCarousel(carousel) { 7615 $(carousel).addClass("Paused"); 7616} 7617
7618function UnpauseCarousel(carousel) { 7619 $(carousel).removeClass("Paused"); 7620} 7621 7622var CarouselTouch = function($,undefined){ 7623 7624 var _ = { 7625 Settings: { 7626 isMouseDown: false, 7627 startPosition: null, 7628 endPosition:null, 7629 TargetElement: null, 7630 TargetElementStart: null, 7631 Pages: null, 7632 PageWidth: null, 7633 Initialised:false 7634 }, 7635 Init: function(options){ 7636 if (_.Settings.Initialised) { 7637 return false; 7638 } 7639 _.Settings.Initialised = true; 7640 _.Settings.TargetElement = options.TargetElement; 7641 _.Settings.Pages = $(_.Settings.TargetElement).find("section.Pages"); 7642 _.Settings.PageWidth = $(_.Settings.TargetElement).find("section.Pages .Page").width(); 7643 7644 _.AddTouch(); 7645 7646 // This is required to swipe the carousel with mouse on desktop to test, uncomment it to enable on desktop, comment it before checking in the code 7647 //$( _.Settings.TargetElement).find("a").each(function () { 7648 // $(this).removeAttr("onclick").click(function () { 7649 // return false; 7650 // }); 7651 // }); 7652 7653 7654 }, 7655 AddTouch: function () { 7656 7657 var config = { 7658 touchStart: _.touchStart, 7659 touchMove: _.touchMove, 7660 touchEnd: _.touchEnd, 7661 useMouse: true 7662 } 7663 7664 $(_.Settings.TargetElement).on("mouseleave", function () {_.touchEnd() }).NXTouch(config); 7665 7666 }, 7667 touchMove: function (e, NXTouchObj) { 7668 if (_.Settings.isMouseDown) { 7669 var targetLeft = _.Settings.TargetElementStart + NXTouchObj.DistanceX; 7670 7671 if (targetLeft > 0) 7672 targetLeft = 0; 7673 7674 if (targetLeft < -(_.Settings.Pages.width() - _.Settings.PageWidth)) 7675 targetLeft = -(_.Settings.Pages.width() - _.Settings.PageWidth); 7676 7677 _.Settings.Pages.css("left", targetLeft); 7678 7679 } 7680 7681 }, 7682 touchStart:function(e, NXTouchObj){ 7683 _.Settings.isMouseDown = true; 7684 _.Settings.startPosition = NXTouchObj.X; 7685 7686 _.Settings.TargetElementStart = parseInt(_.Settings.Pages.css("left")); 7687 if (isNaN(_.Settings.TargetElementStart)) 7688 _.Settings.TargetElementStart = 0; 7689 }, 7690 touchEnd:function(){ 7691 7692 if (_.Settings.isMouseDown) { 7693 _.Settings.isMouseDown = false; 7694 7695 _.Settings.endPosition = -Math.round((Math.abs(_.Settings.Pages.position().left) / _.Settings.PageWidth) * _.Settings.PageWidth); 7696 7697 if (_.Settings.endPosition >= 0) 7698 return false; 7699 7700 if (_.Settings.endPosition <= -(_.Settings.Pages.width() - _.Settings.PageWidth)) { 7701 return false; 7702 } 7703 7704 var direction = '-'; 7705 7706 var distanceDragged = _.Settings.endPosition % _.Settings.PageWidth; 7707 7708 //console.log('distanceDragged:'+distanceDragged) 7709 //console.log('pageWidth:' + _.Settings.PageWidth) 7710 //console.log('endPosition:' + _.Settings.endPosition) 7711 //console.log('target start:', _.Settings.TargetElementStart) 7712 7713 if (_.Settings.endPosition < _.Settings.TargetElementStart) { 7714 direction = '+'; 7715 } 7716 else 7717 { 7718 distanceDragged = _.Settings.PageWidth - Math.abs(distanceDragged); 7719 } 7720 7721 if (Math.abs(distanceDragged) < (_.Settings.PageWidth / 4)) { 7722 _.Settings.Pages.animate({ "left": _.Settings.endPosition - (distanceDragged) },300); 7723 } 7724 else { 7725 CarouselChangePage(_.Settings.TargetElement, direction, 0, 0); 7726 } 7727 7728 //CarouselChangePage(_.Settings.TargetElement,direction, 0, 0); 7729 7730 _.Settings.isMouseDown = false; 7731 } 7732 } 7733 } 7734 7735 7736 return { 7737 Init: _.Init 7738 } 7739 7740 7741}(jQuery); 7742 7743 7744 7745 7746//$(function () { 7747// AddTouch(); 7748//}); 7749 7750 7751; 7752var AddThisEnabled = false; 7753 7754function appendAddThis() { 7755 var container = $("<div/>").attr("class", "addthis_toolbox addthis_default_style"); 7756 var facebook = $("<a/>").attr("class", "addthis_button_facebook").html(" "); 7757 var twitter = $("<a/>").attr("class", "addthis_button_twitter"); 7758 var email = $("<a/>").attr("class", "addthis_button_email"); 7759 var google = $("<a/>").attr("class", "addthis_button_google_plusone_share"); 7760 var addThisButton = $("<a/>").attr("class", "addthis_button_compact"); 7761 var addThisCounter = $("<a/>").attr("class", "addthis_counter addthis_bubble_style"); 7762 var clear = $("<div/>").attr("class", "ClearBoth").html(" "); 7763 container.append(facebook, twitter, email, google, addThisButton, addThisCounter); 7764 $(".SocialBookmarks").empty(); 7765 $(".SocialBookmarks").append(container, clear); 7766 7767 $.ajax({
7768 url: "https://s7.addthis.com/js/250/addthis_widget.js#domready=1&async=1", 7769 dataType: "script", 7770 success: function (d, txtStatus, xhr) { 7771 socialSuccess(d, txtStatus, xhr); 7772 }, 7773 error: function (d, txtStatus, xhr) { 7774 socialError(d, txtStatus, xhr); 7775 }, 7776 cache: false 7777 }); 7778} 7779 7780 7781function socialError(d, txtStatus, xhr) { 7782 var data = d; 7783} 7784 7785function socialSuccess(d, txtStatus, xhr) { 7786 if (xhr.status == 200) { 7787 addthis.toolbox('#toolbox'); 7788 addthis.counter('.addthis_counter'); 7789 addthis.addEventListener('addthis.menu.open', AddThisMenuOpen); 7790 } 7791} 7792 7793function updateSocialMediaButtons(path) { 7794 7795 window.addthis_share = { url: "//" + window.location.host + path }; 7796 7797 $("#at20mc").remove(); 7798 window.addThisLoaded = false; 7799 window.addthis = null; 7800 window._atc = null; 7801 window._atd = null; 7802 window._ate = null; 7803 window._atr = null; 7804 window._atw = null; 7805 window._at_plusonecallback = null; 7806 appendAddThis(); 7807} 7808 7809function AddThisMenuOpen() { 7810 //Set menu popup to fixed position so that scrolling the page persists the visibility of the popup 7811 $("#at20mc #at15s").css("position", "fixed"); 7812}; 7813//Recenty viewed object contains service call methods and plugin initialisation methods 7814var RecentlyViewed = { 7815 //Initialise the scroller plugin 7816 initScroller: function (selector, loadOnVisible) { 7817 //make sure we have a visitor id 7818 if (typeof NextBasket == "undefined" || NextBasket.VisitorID == "") { 7819 setTimeout(function () { 7820 RecentlyViewed.initScroller(selector, loadOnVisible); 7821 }, 100); 7822 return false; 7823 } 7824 $(selector).ItemScroller("init", NextBasket.VisitorID, loadOnVisible); 7825 }, 7826 //Initialise the view all plugin 7827 initViewAll: function (selector) { 7828 //make sure we have a visitor id 7829 if (typeof NextBasket == "undefined" || NextBasket.VisitorID == "") { 7830 setTimeout(function () { 7831 RecentlyViewed.initViewAll(selector); 7832 }, 100); 7833 return false; 7834 } 7835 7836 $(selector).viewAllRecentlyViewed("init", NextBasket.VisitorID); 7837 7838 }, 7839 //Get ListsURL or SecureListsURL based on protocol or HTTPS feature 7840 getListsURLOrSecureListsURL: function () { 7841 if (window.location.protocol === "https:" || Next.Settings.Channel.HttpsEnabled) 7842 return SecureListsURL; 7843 else 7844 return ListsURL; 7845 }, 7846 getRecentlyViewed: function (visitorID, callback, errorCallBack) { 7847 7848 7849 //Check if recently viewed data has already been loaded - if it has, just pas that into the callback rather than making another call 7850 if (typeof recentlyViewedGlobal != "undefined" && recentlyViewedGlobal != null) { 7851 if (callback) { 7852 callback(recentlyViewedGlobal); 7853 } 7854 return; 7855 } 7856 7857 //make sure we have a visitor id 7858 if (typeof NextBasket == "undefined" || NextBasket.VisitorID == null) { 7859 setTimeout(function () { 7860 RecentlyViewed.getRecentlyViewed(visitorID, callback); 7861 }, 100); 7862 return false; 7863 } 7864 7865 var profileClientId = Next.Settings.Channel.ProfileClientId; 7866 7867 $.ajax({
7868 url: RecentlyViewed.getListsURLOrSecureListsURL() + "/recentlyviewed/get/" + visitorID, 7869 type: "POST", 7870 dataType: "json", 7871 data: { ProfileClientId: profileClientId }, 7872 xhrFields: { withCredentials: true }, 7873 crossDomain: true, 7874 success: function (data, textStatus, jqXHR) { 7875 7876 //Uncomment this to test the error message 7877 // setTimeout(function () { 7878 // errorCallBack(); 7879 // }, 2000); 7880 7881 // return false; 7882 7883 // data = [{ "Path": "/g38330s3", "SpreadPage": "330", "DateAdded": "2013-06-11T15:06:39.39" }, { "Path": "/g38330s4", "ItemNumber": "780-632-G38", "Size": "07", "DateAdded": "2013-06-11T15:06:21.517" }, { "Path": "/g38330s7", "SpreadPage": "330", "DateAdded": "2013-06-11T15:06:05.733"}] 7884 7885 if (callback) { 7886 7887 if (!Next.Settings.Channel.IsMobileSite) { 7888 data = RecentlyViewed.filterRecentlyViewedBySingleSegment(data); 7889 } 7890 7891 callback(data); 7892 } 7893 }, 7894 error: function (xhr, ajaxOptions, thrownError) { 7895 if (errorCallBack) { 7896 errorCallBack(); 7897 } 7898 }, 7899 complete: function () { 7900 RecentlyViewed.updateRecentlyViewedFooterLink(null); 7901 } 7902 }); 7903 }, 7904 updateRecentlyViewedFooterLink: function (element) { 7905 7906 // find the recently viewed wrapper that is generated on the page (not the pop up footer) 7907 7908 7909 element = element || $(".RecentlyViewedWrapper.inPage"); 7910 7911 7912 // return if there are no in page wrappers 7913 7914 if ($(element).length === 0 || !$(element).hasClass("inPage")) { 7915 return false; 7916 } 7917 7918 var scrollTop = $(window).scrollTop(); 7919 var grabPoint = $(element).offset().top - scrollTop; 7920 7921 var url = window.location.pathname.toLowerCase(); 7922 if (Next.Settings.Channel.ResponsiveShoppingBagIsEnabled && url.indexOf("/shoppingbag") > -1 && grabPoint == 0) {//fix grab point through css, "display:none"" removes element from layout, so we bring RV into view for split second, but set it's size to 0 so no flickering, then we grab point and remove the class. 7923 $(element).addClass("grabPoint"); 7924 grabPoint = $(element).offset().top - scrollTop; 7925 $(element).removeClass("grabPoint") 7926 } 7927 7928 var wHeight = window.innerHeight; //$(window).height() doesn't take into account initial resizing that would happen when content would fall below min width, 1024px on desktop. 7929 var intermediateBottom = wHeight - 50; 7930 var intermediateTop = wHeight - 250; 7931 var bind = false; 7932 // The grabPoint initially calculates to zero due to browser/speed issues with rendering 7933 7934 if (grabPoint >= 0) { 7935 7936 // find the link in the footer 7937 var recentlyViewedFooterLink = $("a.recentlyViewedFooterLink"); 7938 7939 // if there is a link in the footer decide how it should behave 7940 7941 if ($(recentlyViewedFooterLink).length > 0) { 7942 7943 if ((grabPoint < intermediateBottom) && (Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayInPage)) { 7944 $(recentlyViewedFooterLink).addClass("disabled"); 7945 bind = true; 7946 } else { 7947 if (!$(recentlyViewedFooterLink).hasClass("disableForPage")) { 7948 $(recentlyViewedFooterLink).removeClass("disabled"); 7949 } 7950 } 7951 7952 if (grabPoint < intermediateBottom && grabPoint > intermediateTop) { 7953 $(recentlyViewedFooterLink).addClass("intermediate"); 7954 bind = true; 7955 } else { 7956 $(recentlyViewedFooterLink).removeClass("intermediate"); 7957 } 7958 7959 } 7960 7961 if ((grabPoint < intermediateBottom) && (Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayInPage)) { 7962 $(element).addClass("BINDME"); 7963 } 7964 7965 } 7966 7967 if (!$(element).hasClass("loadedInWindow")) { 7968 $(element).addClass("loadedInWindow"); 7969 } 7970 7971 7972 7973 return bind; 7974 7975 }, 7976 getItem: function (path, callback) { 7977 7978 $.get(path, function (data) { 7979 7980 if (callback) { 7981 callback(data); 7982 } 7983 }); 7984 }, 7985 addItem: function (item, callback) { 7986 if (!Next.FeatureManager.FeatureSettings.RecentlyViewed.EnableAdditions) { 7987 return false; 7988 } 7989 7990 if (!Next.Settings.Channel.RvDisplayInPage) { 7991 return false; 7992 } 7993 7994 //make sure we have a visitor id 7995 if (typeof NextBasket == "undefined" || NextBasket.VisitorID == "") { 7996 setTimeout(function () { 7997 RecentlyViewed.addItem(item, callback); 7998 }, 100); 7999 return false; 8000 } 8001 8002 var homewareIndxStartPos = item.path.indexOf('/homeware/'); 8003 var styleIndxStartPos = item.path.indexOf('/style/'); 8004 var lastSlashIndxStartPos = item.path.lastIndexOf('/'); 8005 8006 //handle furniture url's different to url with publication_book_shot 8007 if (homewareIndxStartPos > -1) { 8008 item.path = item.path.substring(homewareIndxStartPos); 8009 } 8010 //handle style items properly (we need to keep /style in the URL) 8011 else if (styleIndxStartPos > -1) { 8012 item.path = item.path.substring(styleIndxStartPos); 8013 } 8014 else if (!Next.Settings.Channel.EnhancedStyleWithRibbonEnabled || !item.isStyleWith) {
8015 //New URL check 8016 item.path = !ModularPDPHelper.isUrlInNewFormat() 8017 ? item.path.substring(lastSlashIndxStartPos) 8018 : ModularPDPHelper.getNewUrlWithoutHash({ includeOrigin: false, includeStyle: false, itemNumber: item.itemNumber || null, replaceLastSegment: true }); 8019 } 8020 8021 //Build target url 8022 var targetURL = RecentlyViewed.getListsURLOrSecureListsURL() + "/recentlyviewed/add/" + NextBasket.VisitorID; 8023 var postParams = { Path: item.path }; 8024 if (item.itemNumber) { 8025 //targetURL += "&itemNumber=" + item.itemNumber; 8026 postParams.ItemNumber = item.itemNumber; 8027 } 8028 8029 //If a page number was supplied, add it to the querystring 8030 if (item.pageNumber) { 8031 //targetURL += "&SpreadPage=" + item.pageNumber; 8032 postParams.SpreadPage = item.pageNumber; 8033 } 8034 8035 if (item.Size) { 8036 //targetURL += "&size=" + item.Size; 8037 postParams.Size = item.Size; 8038 } 8039 8040 var profileClientId = Next.Settings.Channel.ProfileClientId; 8041 if (profileClientId) { 8042 postParams.ProfileClientId = profileClientId; 8043 } 8044 8045 $.ajax({
8046 url: targetURL, 8047 type: "POST", 8048 dataType: "json", 8049 data: postParams, 8050 xhrFields: { withCredentials: true }, 8051 crossDomain: true, 8052 success: function (data, textStatus, jqXHR) { 8053 if (callback) { 8054 callback(data); 8055 } 8056 }, 8057 error: function (xhr, ajaxOptions, thrownError) { 8058 8059 } 8060 }); 8061 }, 8062 clearList: function (path, callback) { 8063 8064 var profileClientId = Next.Settings.Channel.ProfileClientId; 8065 8066 $.ajax({ 8067 url: path, 8068 type: "GET", 8069 dataType: "json", 8070 xhrFields: { withCredentials: true }, 8071 data: { ProfileClientId: profileClientId }, 8072 crossDomain: true, 8073 success: function (data, textStatus, jqXHR) { 8074 if (callback) { 8075 callback(data); 8076 } 8077 }, 8078 error: function (xhr, ajaxOptions, thrownError) { 8079 8080 } 8081 }); 8082 8083 // Disable the Recently Viewed Link in the Footer 8084 if (!$("a.recentlyViewedFooterLink").hasClass("active")) { 8085 $("a.recentlyViewedFooterLink").addClass("disabled"); 8086 } 8087 8088 $("a.recentlyViewedFooterLink").addClass("disableForPage"); 8089 }, 8090 filterRecentlyViewedBySingleSegment: function (items) { 8091 for (var i = 0; i < items.length; i++) { 8092 var currentItem = items[i]; 8093 8094 if (currentItem.Path.split("/").length == 2) { 8095 items.splice(i, 1); 8096 } 8097 } 8098 return items; 8099 } 8100}; 8101 8102 8103function SetRecentlyViewedDisplay() { 8104 8105 checkForRecentlyViewedCookie(); 8106 8107 8108 if (Next.FeatureManager.FeatureSettings.RecentlyViewed.IsEnabled) { 8109 //Added flagselector check to display RecentlyViewed in Footer when HTTPS is turned ON 8110 if (Next.Settings.Channel.HttpsEnabled) { 8111 if ((window.location.protocol.indexOf("https:") === 0) && (!Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayInSecureFooter) && $(".PageFooter .flagSelectorContainer").length == 0) { 8112 $(".recentlyViewedFooterLink").hide(); 8113 } 8114 } else { 8115 if ((window.location.protocol.indexOf("https:") === 0) && (!Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayInSecureFooter)) { 8116 $(".recentlyViewedFooterLink").hide(); 8117 } 8118 } 8119 8120 if (Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayCounter) { 8121 $(".RVDisplayCounter").show(); 8122 } 8123 8124 if (Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayViewAllLink) { 8125 $(".RVDisplayViewAllLink").show(); 8126 } 8127 8128 if (!Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayInPage) { 8129 $(".RVDisplayInPage").hide(); 8130 $(".RecentlyViewedWrapper.inPage").hide(); 8131 } else { 8132 $(".RVDisplayInPage").show(); 8133 $(".RecentlyViewedWrapper.inPage").show(); 8134 } 8135 8136 if (!Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayInFooter) { 8137 $(".RVDisplayInFooter").hide(); 8138 } else { 8139 $(".RVDisplayInFooter").show(); 8140 } 8141 } else { 8142 8143 $(".recentlyViewedFooterLink").hide(); 8144 8145 $(".RecentlyViewed").each(function () { 8146 $(this).hide(); 8147 }); 8148 $(".RVDisplayInPage").each(function () { 8149 $(this).hide(); 8150 }); 8151 $(".RVDisplayCounter").each(function () { 8152 $(this).hide(); 8153 }); 8154 $(".RVDisplayViewAllLink").each(function () { 8155 $(this).hide(); 8156 }); 8157 $(".RecentlyViewedWrapper").each(function () { 8158 $(this).hide(); 8159 }); 8160 $(".RVDisplayInFooter").each(function () { 8161 $(this).hide(); 8162 }); 8163 } 8164}; 8165 8166//Check if any components need initialising 8167function checkForRecentlyViewedCookie() { 8168 // Check whether the ShowRecentlyViewed flag is set to true, if it is then initScroller, otherwise remove the wrapper from the DOM 8169 8170 var showInPage = true; 8171 var showFooterLink = true; 8172 var showCoachTip = true; 8173 8174 // Get the ShowRecentlyViewed flag 8175 var showRVCookie = readSplitCookie("NextSessionVariables", "ShowRecentlyViewed");
8176 if (showRVCookie == null || showRVCookie === "false") { 8177 showInPage = false; 8178 showFooterLink = false; 8179 } 8180 8181 // Disable the Footer Link if the global config says to do so 8182 if (typeof disableRecentlyViewedFooterLink != "undefined") { 8183 showFooterLink = false; 8184 } 8185 8186 8187 // Check the list of PageTypes and update the flags accordingly 8188 var disabledInPageList = Next.FeatureManager.FeatureSettings.RecentlyViewed.InPageDisabledPageTypes; 8189 var currentPageType = PageType; 8190 8191 if (typeof currentPageType === "undefined") { 8192 currentPageType = "undefined"; 8193 } 8194 8195 if (currentPageType.toLowerCase() == "sale") { 8196 showInPage = false; 8197 } 8198 if (currentPageType.toLowerCase() == "socialplp") { 8199 showInPage = false; 8200 } 8201 8202 //Check current page type against list of disabled page types for in-page display 8203 if (typeof disabledInPageList != "undefined") { 8204 for (var i = 0; i < disabledInPageList.length; i++) { 8205 if (currentPageType.toLowerCase() == disabledInPageList[i].toLowerCase()) { 8206 showInPage = false; 8207 } 8208 } 8209 } 8210 8211 //Same for toolbar 8212 var disabledToolBarList = Next.FeatureManager.FeatureSettings.RecentlyViewed.ToolbarDisabledPageTypes; 8213 //Check current page type against list of disabled page types for ToolBar display 8214 if (typeof currentPageType != "undefined" && typeof disabledToolBarList != "undefined") { 8215 for (var i = 0; i < disabledToolBarList.length; i++) { 8216 if (currentPageType.toLowerCase() == disabledToolBarList[i].toLowerCase()) { 8217 showFooterLink = false; 8218 } 8219 } 8220 } 8221 8222 8223 if ($(".HomeFooter").length) { 8224 showFooterLink = false; 8225 } 8226 8227 if (showInPage) { 8228 // Init the In-Page components 8229 if ($(".RecentlyViewedWrapper").length) { 8230 $(".RecentlyViewedWrapper").each(function () { 8231 var wrapper = $(this); 8232 8233 if (wrapper.hasClass("LoadNow") && !wrapper.hasClass("inFooter")) { 8234 if (!wrapper.hasClass("CustomPosition")) { 8235 if ($(".CurrentContent").length) { 8236 $(".CurrentContent").append(wrapper); 8237 } else if ($("#ct").length) { 8238 $("#ct").append(wrapper); 8239 } 8240 } 8241 8242 RecentlyViewed.initScroller(wrapper, false); 8243 8244 } 8245 8246 if (wrapper.hasClass("loadOnVisible") && !wrapper.hasClass("inFooter")) { 8247 if (!wrapper.hasClass("CustomPosition")) { 8248 if ($(".CurrentContent").length) { 8249 $(".CurrentContent").append(wrapper); 8250 } else if ($("#ct").length) { 8251 $("#ct").append(wrapper); 8252 } 8253 } 8254 8255 RecentlyViewed.initScroller(wrapper, true); 8256 8257 } 8258 8259 }); 8260 } 8261 } else { 8262 // Remove the In-Page component from the DOM 8263 $(".RecentlyViewedWrapper.inPage").remove(); 8264 } 8265 8266 if (!showFooterLink) { 8267 // Disable the Recently Viewed Link in the Footer 8268 $(".recentlyViewedFooterLink").addClass("disabled").addClass("disableForPage"); 8269 } 8270 8271 if (showFooterLink && showCoachTip) { 8272 setTimeout("openCoachTip()", 1000); 8273 } 8274} 8275 8276function openCoachTip() { 8277 // Display the coach tip 8278 $(".coachTip").addClass("open").animate({ 8279 top: -120, 8280 opacity: 1 8281 }, 1000, "easeOutExpo"); 8282} 8283 8284function closeCoachTip() { 8285 8286 var $coachTip = $(".coachTip"); 8287 8288 if ($coachTip.length == 0 || !$coachTip.hasClass("open") || $coachTip.hasClass("locked")) { 8289 return false; 8290 } 8291 8292 $coachTip.addClass("locked"); 8293 8294 $(".coachTip").animate({ 8295 top: 40, 8296 opacity: 0 8297 }, 500, "easeInCirc", function () { 8298 8299 $(".coachTip").removeClass("open").removeClass("locked"); 8300 8301 });
8302 8303} 8304 8305 8306///Helper functions///////////////////////////////////////////////// 8307 8308//Return the list of recently viewed items grouped by day 8309function groupByDate(objArray) { 8310 8311 var previousDay = null; 8312 var groupedArray = new Array(); 8313 var currentGroup = null; 8314 var path = window.location.pathname; 8315 var createOlderItemsGroup = true; 8316 var now = new Date(); 8317 now.setDate(now.getDate() - 15); 8318 8319 var isRTL = Next.Settings.Channel.DirectionRTL ? true : false; 8320 8321 // If RTL, then process the list from back to front as thats how we wish to display it 8322 var i = 0; 8323 if (isRTL) i = objArray.length - 1; 8324 while (i >= 0 && i < objArray.length) { 8325 8326 //Uncomment below to enable interaction list 8327 // var skipEntry = false; 8328 // if (typeof interactionList != 'undefined') { 8329 // 8330 8331 // if (objArray[i].ItemNumber == null && objArray[i].Path == path) { 8332 // skipEntry = true; 8333 // } 8334 8335 // for (var interactionObject = 0; interactionObject < interactionList.length; interactionObject++) { 8336 // if (objArray[i].ItemNumber == interactionList[interactionObject]) { 8337 // skipEntry = true; 8338 // } 8339 // } 8340 8341 8342 // } 8343 8344 // if (skipEntry) continue; 8345 //Get current day 8346 var thisDay = new Date(objArray[i].DateAdded); 8347 8348 if (isNaN(thisDay)) { 8349 var dateString = objArray[i].DateAdded.split("T")[0]; 8350 var dateArray = dateString.split("-"); 8351 var formattedDateString = dateArray[1] + "/" + dateArray[2] + "/" + dateArray[0]; 8352 thisDay = new Date(formattedDateString); 8353 } 8354 8355 //Check if we're on the first day or if this date is different to previous 8356 if ((previousDay == null || formatViewedDate(thisDay, "nn mm yy") != formatViewedDate(previousDay, "nn mm yy")) // All items under 14 days old 8357 && ((thisDay > now) || createOlderItemsGroup)) // All older items 8358 { 8359 //if it is - create a new currentGroup object and set its date to current item 8360 8361 8362 var groupDate = formatViewedDate(thisDay, "", true); 8363 currentGroup = { 8364 rawDate: objArray[i].DateAdded, 8365 date: groupDate, 8366 itemArray: new Array() 8367 }; //Add this group object to the parent array 8368 groupedArray.push(currentGroup); 8369 8370 if (thisDay < now) // All older items 8371 createOlderItemsGroup = false; 8372 } 8373 //Add this day to the current group 8374 currentGroup.itemArray.push(objArray[i]); 8375 //Reset previous day 8376 previousDay = thisDay; 8377 8378 if (isRTL) i--; 8379 else i++; 8380 } 8381 8382 return groupedArray; 8383} 8384 8385 8386//Custom date formatting function 8387function formatViewedDate(date, format, displayFriendyDates) { 8388 8389 8390 //If we're displaying friendly names e.g. Today, Yesterday etc. check that condition first 8391 if (displayFriendyDates) { 8392 8393 var matchFound = false; 8394 8395 for (var i = 0; i < 15; i++) { 8396 8397 var loopDay = new Date(); 8398 8399 loopDay.setDate(loopDay.getDate() - i); 8400 8401 if (loopDay.getDate() == date.getDate() && 8402 loopDay.getMonth() == date.getMonth() && 8403 loopDay.getYear() == date.getYear()) { 8404 matchFound = true; 8405 8406 switch (i) { 8407 case 0: 8408 return "Today"; 8409 break; 8410 case 1: 8411 return "Yesterday"; 8412 break; 8413 default: 8414 return i + " Days Ago"; 8415 } 8416 } 8417 8418 } 8419 8420 if (!matchFound) { 8421 return "Older"; 8422 } 8423 8424 } 8425 8426 var monthNames = [ 8427 "January", "February", "March", "April", "May", "June",
8428 "July", "August", "September", "October", "November", "December" 8429 ]; 8430 8431 var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; 8432 8433 var dDay = dayNames[date.getDay()]; 8434 var dayNumber = date.getDate(); 8435 var dMonth = monthNames[date.getMonth()]; 8436 var dYear = date.getYear() + 1900; 8437 8438 var ordinalString = "th"; 8439 8440 if (dayNumber == 1 || dayNumber == 11 || dayNumber == 21 || dayNumber == 21) { 8441 ordinalString = "st"; 8442 } 8443 8444 if (dayNumber == 2 || dayNumber == 12 || dayNumber == 22) { 8445 ordinalString = "nd"; 8446 } 8447 8448 if (dayNumber == 3 || dayNumber == 13 || dayNumber == 23) { 8449 ordinalString = "rd"; 8450 } 8451 8452 switch (format) { 8453 8454 case "nn mm yy": 8455 return dayNumber + " " + dMonth + " " + dYear; 8456 break; 8457 case "nn/mm/yy": 8458 return dayNumber + "/" + (date.getMonth() + 1) + "/" + dYear; 8459 break; 8460 default: 8461 return dDay + ", " + dayNumber + ordinalString + " " + (dMonth) + ", " + dYear; 8462 8463 } 8464 8465} 8466 8467function roundToHalf(value) { 8468 var converted = parseFloat(value); // Make sure we have a number 8469 var decimal = (converted - parseInt(converted, 10)); 8470 //Set to Math.floor to match value returned by search provider 8471 decimal = Math.floor(decimal * 10); 8472 if (decimal == 5) { 8473 return (parseInt(converted, 10) + 0.5); 8474 } 8475 if ((decimal < 3) || (decimal > 7)) { 8476 return Math.round(converted); 8477 } else { 8478 return (parseInt(converted, 10) + 0.5); 8479 } 8480} 8481 8482 8483/////////////Plugins///////////////////////////////////// 8484 8485///viewAllRecentlyViewed Plugin//////// 8486 8487(function ($) { 8488 8489 var methods = { 8490 init: function (visitorId) { 8491 //Create copy of element we're intialising as this plugin - use in nested/callback functions 8492 var $this = this; 8493 //Set scroll hanfler 8494 $(window).scroll(function () { 8495 methods.loadItemsInviewport($this); 8496 }); 8497 //Call getRecentlyViewed and pass callback 8498 RecentlyViewed.getRecentlyViewed(visitorId, function (itemList) { 8499 8500 8501 //If no elements in recently viewed, stop here 8502 if (itemList.length == 0) 8503 return false; 8504 8505 //Get formated array of items - grouped by date 8506 var groupedArray = groupByDate(itemList);
8507 //Loop through each group/day 8508 for (var i = 0; i < groupedArray.length; i++) { 8509 8510 var thisGroup = groupedArray[i]; 8511 //Create wrapper element for this day of recently viewed items 8512 var dayWrapper = $("<div />"); 8513 dayWrapper.addClass("dayWrapper"); 8514 //Heading for this day 8515 var heading = $("<h2 />"); 8516 heading.text(thisGroup.date); 8517 //Add heading to wrapper 8518 dayWrapper.append(heading); 8519 //Create list to display all recently viewed items for this day 8520 var dayList = $("<ul />"); 8521 //Add the list to the wrapper 8522 dayWrapper.append(dayList); 8523 8524 //Loops through each recently viewed item in this list 8525 for (var j = 0; j < thisGroup.itemArray.length; j++) { 8526 8527 var thisDay = thisGroup.itemArray[j]; 8528 //Create li element to display item 8529 var itemDay = $("<li />"); 8530 //Build path to item component/display 8531 var path = siteAssetsUrl + "/singleitemview/viewall" + thisDay.Path; 8532 //If an item number was included, add it to the querystring 8533 if (thisDay.ItemNumber != null) { 8534 path += "/" + thisDay.ItemNumber; 8535 } 8536 8537 //Set data attribute on LI 8538 itemDay.attr("data-itempath", path); 8539 //Set item path 8540 var itemTargetPath = thisDay.Path; 8541 8542 //If we're targeting a specific item 8543 if (thisDay.ItemNumber != null) { 8544 itemTargetPath += "#" + thisDay.ItemNumber.replace(/-/g, ""); 8545 } 8546 //If a size was selected 8547 if (thisDay.Size != null) { 8548 itemTargetPath += "?o=" + thisDay.Size; 8549 } 8550 //Add the target path 8551 itemDay.attr("data-itemURL", itemTargetPath); 8552 //Check if item we're addng is in the shopping bag 8553 if (NextBasket.Data.Items != null) { 8554 for (var k = 0; k < NextBasket.Data.Items.length; k++) { 8555 if (thisDay.ItemNumber != null && NextBasket.Data.Items[k].ItemNumber == thisDay.ItemNumber.replace(/-/g, "").substring(0, 6)) { 8556 //If it is in the shopping bag, add appropriate class 8557 itemDay.addClass("inBag"); 8558 } 8559 } 8560 } 8561 //Add the item to the list for this day 8562 dayList.append(itemDay); 8563 8564 } 8565 //Add the wrapper for this current day/item group 8566 $this.append(dayWrapper); 8567 8568 } 8569 //Load items currently visible 8570 methods.loadItemsInviewport($this); 8571 8572 }); 8573 8574 }, 8575 //This method loads the data/component for any list item currently visible in the viewport 8576 loadItemsInviewport: function (element) { 8577 8578 //Get height of window and scroll position of page 8579 var wHeight = $(window).height(); 8580 var scrollTop = $(window).scrollTop(); 8581 //Loop through each list item in the recently viewed wrapper 8582 $(element).find("li").each(function () { 8583 //If we haven't previously bound data to it and it's not a heading item 8584 if (!$(this).hasClass("newDay") && !$(this).hasClass("visible")) { 8585 //Check position of list item - is it visible? 8586 if (($(this).offset().top - scrollTop) > 100 && ($(this).offset().top - scrollTop < wHeight)) { 8587 $(this).addClass("visible"); 8588 //If so - get the item data 8589 methods.getItemData(this); 8590 } 8591 8592 } 8593 }); 8594 8595 }, 8596 getItemData: function (li) { 8597 8598 var $li = $(li); 8599 8600 var path = $li.attr("data-itempath"); 8601 var url = $li.attr("data-itemURL"); 8602 8603 8604 RecentlyViewed.getItem(path, function (data) { 8605 8606 var d = $(data); 8607 var img = d.find("img"); 8608 8609 //This assumes the first element returned is an <a> 8610 $(d[0]).attr("href", url); 8611 8612 img.css("opacity", 0); 8613 $li.prepend(d); 8614 img.animate({ opacity: 1 }, 300); 8615 $li.addClass("databound"); 8616 8617 }); 8618 8619 } 8620 }; 8621 8622 $.fn.viewAllRecentlyViewed = function (method) { 8623 8624 if (methods[method]) { 8625 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 8626 } else if (typeof method === "object" || !method) { 8627 return methods.init.apply(this, arguments); 8628 } else { 8629 $.error("Method " + method + " does not exist on jQuery.viewAllRecentlyViewed"); 8630 } 8631 8632 }; 8633 8634})(jQuery);; 8635 8636 8637/////////////Item Scroller plug-in////////////////////// 8638 8639var RVInitialized = false; 8640 8641(function ($) { 8642 8643 var _visitorId; 8644 var isTouchDown = false; 8645 var isRTL = Next.Settings.Channel.DirectionRTL ? true : false; 8646 const rvItemWidthWithMargins = 102; 8647 var maxItemsOnPage = 9; 8648 var url = window.location.pathname.toLowerCase(); 8649 if (Next.Settings.Channel.ResponsiveShoppingBagIsEnabled && url.indexOf("/shoppingbag") > -1) { 8650 maxItemsOnPage = 8; 8651 } 8652 8653 8654 var methods = { 8655 //Initialize 8656 init: function (visitorId, loadOnVisible) { 8657 //Store visitor id 8658 _visitorId = visitorId; 8659 8660 8661 //If we're only loading the data when the element becomes visible (scrolls into view) 8662 //pass to initOnVisible and exit 8663 if (loadOnVisible) { 8664 methods.initOnVisible(this); 8665 return; 8666 } 8667 8668 //Otherwise, call the bindData method 8669 methods.bindData(this); 8670 8671 8672 }, 8673 //Get ListsURL or SecureListsURL based on protocol or HTTPS feature 8674 getListsURLOrSecureListsURL: function () { 8675 if (window.location.protocol === "https:" || Next.Settings.Channel.HttpsEnabled) 8676 return SecureListsURL; 8677 else 8678 return ListsURL; 8679 }, 8680 windowEvents: function (element) { 8681 RecentlyViewed.updateRecentlyViewedFooterLink(element); 8682 }, 8683 initOnVisible: function (element) { 8684 if (RVInitialized) { 8685 return; 8686 } 8687 //wait for recentlyviewed to finish loading 8688 var bindRecentlyViewed = setInterval(function () { 8689 if (element.hasClass("BINDME")) { 8690 methods.bindData(element); 8691 clearInterval(bindRecentlyViewed); 8692 } 8693 }, 400); 8694 8695 //for home page 8696 setTimeout(function () { 8697 RecentlyViewed.updateRecentlyViewedFooterLink(element); 8698 }, 1000); 8699 8700 //Scroll handler 8701 $(window).scroll(function () { 8702 methods.windowEvents(element); 8703 }); 8704 8705 //Resize handler 8706 $(window).resize(function () { 8707 methods.windowEvents(element); 8708 }); 8709 8710 RVInitialized = true; 8711 }, 8712 bindData: function (element) { 8713 8714 //Local copy of the element 8715 var $this = element; 8716 8717 //Add click handler for reload button in the error message 8718 $this.find("a.reload").click(function () { methods.reload($this); return false; }); 8719 8720 8721 //getRecentlyViewed - shared service call in recentlyViewed.js 8722 RecentlyViewed.getRecentlyViewed(_visitorId, function (data) { 8723 8724 //Remove the ajax loader
8725 8726 8727 $this.find(".loader").remove(); 8728 8729 var itemList = data; 8730 var vLength = itemList.length; 8731 var totalWidth = 0; 8732 8733 //Get formatted array of items - grouped by date 8734 var groupedArray = groupByDate(itemList); 8735 8736 var recentlyViewedGroupWrapper = $("<div />"); 8737 8738 //Loop through each group/day 8739 for (var i = 0; i < groupedArray.length; i++) { 8740 8741 var thisGroup = groupedArray[i]; 8742 //Create wrapper element for this day of recently viewed items 8743 var dayWrapper = $("<div />"); 8744 dayWrapper.addClass("dayWrapper"); 8745 if (isRTL) dayWrapper.addClass("RTL"); 8746 //Heading for this day 8747 var headingText = thisGroup.date; 8748 if (thisGroup.itemArray.length == 1) { 8749 // headingText = formatViewedDate(new Date(thisGroup.rawDate), "nn/mm/yy", true); 8750 headingText = thisGroup.date; 8751 } 8752 8753 var heading = $("<h2 />"); 8754 heading.text(headingText); 8755 // Amend arrow direction if RTL 8756 if (isRTL) heading.addClass("RTL"); 8757 //Add heading to wrapper 8758 dayWrapper.append(heading); 8759 //Create list to display all recently viewed items for this day 8760 var dayList = $("<ul />"); 8761 //Add the list to the wrapper 8762 dayWrapper.append(dayList); 8763 8764 8765 //Force the width of the wrapper 8766 if (Next.Settings.Channel.ResponsiveShoppingBagIsEnabled && url.indexOf("/shoppingbag") > -1) { 8767 dayWrapper.width(thisGroup.itemArray.length * rvItemWidthWithMargins - 5); 8768 dayList.width(thisGroup.itemArray.length * rvItemWidthWithMargins - 4); 8769 totalWidth += thisGroup.itemArray.length * rvItemWidthWithMargins - 5; 8770 } 8771 else { 8772 dayWrapper.width((thisGroup.itemArray.length * 95)); 8773 dayList.width(thisGroup.itemArray.length * 95); 8774 totalWidth += thisGroup.itemArray.length * 95; 8775 } 8776 8777 //Loops through each recently viewwed item in this list 8778 for (var j = 0; j < thisGroup.itemArray.length; j++) { 8779 8780 var thisDay = thisGroup.itemArray[j]; 8781 8782 //Create li element to display item 8783 var itemDay = $("<li />"); 8784 8785 if (i == 0 && j == 0) { 8786 itemDay.addClass("lastViewed"); 8787 } 8788 8789 if (Next.Settings.Channel.ThreeFourImageManagerEnabled && (thisDay.Path && !thisDay.Path.toLowerCase().includes("/homeware/sofas"))) { 8790 itemDay.addClass("three-four-image"); 8791 } 8792 8793 var isTabbedPageItem = false; 8794 if (thisDay.Path.indexOf(thisDay.ItemNumber) > -1) { 8795 isTabbedPageItem = true; 8796 itemDay.data("type", "tabbedItem"); 8797 } 8798 8799 //Build path to item component/display 8800 //var path = "/singleitemview" + thisDay.Path + "?itemView=true"; 8801 8802 if (thisDay.Path.indexOf("/style") === -1 && (thisDay.Path.substring(0, 3).toLowerCase() == '/st' || thisDay.Path.substring(0, 3).toLowerCase() == '/es')) { 8803 thisDay.Path = '/style' + thisDay.Path; 8804 } 8805 8806 //Store the correct path before modifying it 8807 var currentItemPath = thisDay.Path; 8808 8809 //New URL check 8810 var urlSegments = ModularPDPHelper.getSegmentOptions(thisDay.Path); 8811 if (ModularPDPHelper.isUrlPathNameInNewFormat(thisDay.Path, urlSegments.segmentsCount)) { 8812 //Eg: /style/g8316s12/649138 or /g8316s12/649138 8813 urlSegments.segments.pop(); 8814 thisDay.Path = urlSegments.segments.join('/'); 8815 } 8816 8817 var path = siteAssetsUrl + "/singleitemview/rvscroller"; 8818 8819 var seqStartIndex = thisDay.Path.indexOf("seq"); 8820 var nonSeqPath = ""; 8821 8822 if (seqStartIndex > 0) { 8823 nonSeqPath = thisDay.Path; 8824 var textToReplace = nonSeqPath.substring(seqStartIndex, 20); 8825 nonSeqPath = nonSeqPath.replace(textToReplace, ''); 8826 path = path + nonSeqPath; 8827 } 8828 else { 8829 path = path + thisDay.Path; 8830 } 8831 8832 8833 //If an item number was included, add it to the querystring 8834 if (thisDay.ItemNumber != null && !isTabbedPageItem) { 8835 path += "/" + thisDay.ItemNumber; 8836 } 8837 8838 //Set data attribute on LI 8839 itemDay.attr("data-itempath", path); 8840 8841 //Set item path 8842 var itemTargetPath = currentItemPath; 8843 if (thisDay.ItemNumber != null && Next.Settings.UI.ModularPDPNewUrlFormatEnabled && ModularPDPHelper.isUrlPathNameInOldFormat(itemTargetPath, itemTargetPath.split('/').length)) { 8844 itemTargetPath = ModularPDPHelper.getNewUrlPathName(itemTargetPath, thisDay.ItemNumber.replace(/-/g, '')); 8845 } 8846 8847 if (thisDay.ItemNumber != null && !isTabbedPageItem) { 8848 itemTargetPath += "#" + thisDay.ItemNumber.replace(/-/g, ''); 8849 } 8850 8851 if (thisDay.Size != null) { 8852 itemTargetPath += "?o=" + thisDay.Size; 8853 } 8854 8855 itemDay.attr("data-itemURL", siteBaseUrl + itemTargetPath); 8856 8857 //Check if item we're addng is in the shopping bag 8858 if (NextBasket.Data.Items != null) { 8859 for (var k = 0; k < NextBasket.Data.Items.length; k++) { 8860 8861 if (thisDay.ItemNumber) { 8862 8863 var inBag = false; 8864 8865 if (isTabbedPageItem) { 8866 if (thisDay.Path.indexOf(NextBasket.Data.Items[k].ItemNumber + "-" + NextBasket.Data.Items[k].OptionNo) > -1) { 8867 inBag = true; 8868 } 8869 } 8870 else if (NextBasket.Data.Items[k].ItemNumber == thisDay.ItemNumber.replace(/-/g, '').substring(0, 6)) { 8871 inBag = true; 8872 } 8873 8874 if (inBag == true) { 8875 itemDay.addClass("inBag"); 8876 } 8877 8878 } 8879 } 8880 } 8881 8882 //Add the item to the list for this day 8883 dayList.append(itemDay); 8884 8885 } 8886 //Add the wrapper for this current day/item group 8887 //setTimeout(function () { 8888 recentlyViewedGroupWrapper.append(dayWrapper).width(totalWidth);
8889 // }, 5000); 8890 8891 8892 } 8893 8894 //Clear out any existing data - we may be reloading a newer set of data 8895 $this.find(".recentlyViewedGroupWrapper").empty(); 8896 var temp = recentlyViewedGroupWrapper.find(".dayWrapper"); 8897 $this.find(".recentlyViewedGroupWrapper").append(temp).width(totalWidth); 8898 8899 //More than 9 - fix width 8900 if (vLength > maxItemsOnPage) { 8901 // $this.width(976); 8902 // $this.find(".clip").width(855) 8903 if (isRTL) { 8904 $this.find("a.scrollNav.previous").removeClass("disabled"); 8905 $this.find("a.scrollNav.next").addClass("disabled"); 8906 } else { 8907 $this.find("a.scrollNav.next").removeClass("disabled"); 8908 $this.find("a.scrollNav.previous").addClass("disabled"); 8909 } 8910 $this.find(".clip").addClass("noBackground"); 8911 //$this.find(".scrollControl").show(); 8912 methods.enableTouchEvents($this.find(".clip")); 8913 } else { 8914 8915 //if (resize) { 8916 //Less than 10 - calculate width and disable both scroll buttons 8917 // $this.width(vLength * 97); 8918 $this.find("a.scrollNav.previous").addClass("disabled"); 8919 $this.find("a.scrollNav.next").addClass("disabled"); 8920 // $this.find(".scrollControl").hide(); 8921 // $this.find(".clip").width(vLength * 97); 8922 // } 8923 } 8924 8925 if (groupedArray.length == 0) { 8926 $this.find(".emptyMessage").fadeIn(500); 8927 } 8928 8929 8930 //Set counter text if enabled 8931 if (Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayCounter) { 8932 $this.find(".viewCounter").text(vLength); 8933 } 8934 8935 8936 //Set class on parent element to indicate content has loaded 8937 $this.addClass("contentLoaded"); 8938 methods.loadVisibleItems($this.find(".recentlyViewedGroupWrapper")); 8939 $this.find("a.scrollNav.previous").click(function () { methods.scroll(1, this, $this); return false; }); 8940 $this.find("a.scrollNav.next").click(function () { methods.scroll(-1, this, $this); return false; }); 8941 8942 }, 8943 //This is the error callback 8944 function () { 8945 8946 $this.find(".recentlyViewedGroupWrapper").fadeOut(500); 8947 $this.find(".loader").hide(); 8948 $this.find(".errorMessageBlock").fadeIn(500); 8949 8950 }); 8951 8952 }, 8953 reload: function (element) { 8954 8955 var $this = $(element); 8956 8957 $this.find(".recentlyViewedGroupWrapper").fadeIn(500); 8958 8959 $this.find(".errorMessageBlock").fadeOut(300, function () { 8960 $this.find(".loader").fadeIn(300); 8961 }); 8962 8963 methods.bindData($this); 8964 8965 }, 8966 addItem: function (item) { 8967 8968 //Add to recently viewed 8969 RecentlyViewed.addItem(item); 8970 8971 }, 8972 scroll: function (direction, element, parent) { 8973 8974 var listWrapper = parent.find(".recentlyViewedGroupWrapper"); 8975 8976 if ($(element).hasClass("disabled") 8977 || listWrapper.hasClass("locked") 8978 || listWrapper.position().left >= 0 && direction == 1 8979 || listWrapper.find("li").length <= maxItemsOnPage) { 8980 return false; 8981 } 8982 8983 var liCount = listWrapper.find("li").length; 8984 8985 var currentOffset = listWrapper.css("left").replace('px', ''); 8986 8987 var pageSize = 95 * 9; 8988 var listLen = liCount * 95; 8989 var distance = pageSize * direction; 8990 8991 if (Next.Settings.Channel.ResponsiveShoppingBagIsEnabled && url.indexOf("/shoppingbag") > -1) { 8992 pageSize = rvItemWidthWithMargins * 8; 8993 listLen = liCount * rvItemWidthWithMargins; 8994 distance = pageSize * direction; 8995 } 8996 8997 //The following checks are there to prevent the scroller getting out of sync when touch enabled scrolling is enabled. 8998 //When a user uses touch-scroll, the paging will become irrelevant so need a check to make sure we don't scroll beyond the limit of the list 8999 var newOffset = parseInt(currentOffset) + parseInt(distance);
9000 var maxOffset = (listLen - pageSize) * direction; 9001 9002 if (!isRTL) { 9003 if ((newOffset < maxOffset) && direction == -1) { 9004 distance = maxOffset - parseInt(currentOffset); 9005 } 9006 9007 if ((newOffset > 0) && direction == 1) { 9008 distance = 0 - parseInt(currentOffset); 9009 } 9010 } 9011 else { 9012 if ((newOffset > maxOffset) && direction == 1) { 9013 distance = maxOffset - parseInt(currentOffset); 9014 } 9015 9016 if ((newOffset < 0) && direction == -1) { 9017 distance = 0 - parseInt(currentOffset); 9018 } 9019 } 9020 9021 //Safety check 9022 if (distance > pageSize) { 9023 distance = pageSize; 9024 } 9025 9026 if (distance < -pageSize) { 9027 distance = -pageSize; 9028 } 9029 9030 //ie8 and below don't like animating from one css class to another - so, set the animation time to 0 9031 var transitionSpeed = 200; 9032 if (!Modernizr.csstransitions) { 9033 transitionSpeed = 0; 9034 } 9035 9036 9037 listWrapper.addClass("locked").animate({ 9038 left: "+=" + distance 9039 }, 500, function () { 9040 9041 if (direction == -1) { 9042 parent.find("a.scrollNav.previous").removeClass("disabled", transitionSpeed); 9043 } 9044 9045 if (listWrapper.position().left >= 0) { 9046 parent.find("a.scrollNav.previous").addClass("disabled", transitionSpeed); 9047 } 9048 9049 if (parent.find("li").length * rvItemWidthWithMargins < listWrapper.width()) { 9050 parent.find("a.scrollNav.next").addClass("disabled", transitionSpeed); 9051 } else { 9052 parent.find("a.scrollNav.next").removeClass("disabled", transitionSpeed); 9053 } 9054 9055 listWrapper.removeClass("locked"); 9056 9057 methods.loadVisibleItems(listWrapper); 9058 9059 }); 9060 9061 }, 9062 clearList: function () { 9063 9064 //Clears the full list of recently viewed items 9065 preventCollapse = true; 9066 9067 //Build target url 9068 var targetURL = methods.getListsURLOrSecureListsURL() + "/recentlyviewed/clear/" + _visitorId; 9069 9070 //Clear recently viewed data 9071 RecentlyViewed.clearList(targetURL); 9072 9073 //And update the UI 9074 var _this = this; 9075 9076 _this.find(".recentlyViewedGroupWrapper").fadeOut(500, function () { 9077 //Fade in message 9078 _this.find(".emptyMessage").fadeIn(500); 9079 }); 9080 9081 //Disable clear link 9082 _this.find("a.clearAll").addClass("disabled"); 9083 9084 //Disable scroll buttons 9085 $("a.scrollNav.previous").addClass("disabled", 200); 9086 $("a.scrollNav.next").addClass("disabled", 200); 9087 9088 //Make sure clip background is visible 9089 $(".clip").removeClass("noBackground"); 9090 9091 9092 //Update display count 9093 if (Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayCounter) { 9094 _this.find(".viewCounter").text("0"); 9095 } 9096 }, 9097 loadVisibleItems: function (wrapper) { 9098 //Load items as they appear in view 9099 if (wrapper.length > 0) { 9100 var allowance = Next.Settings.UI.IsInnovationDesktopResponsiveEnabled ? 1100 : 1000; //change this value to add more buffer/tune performance) 9101 9102 var itemDataPromises = []; 9103 9104 wrapper.find("ul").each(function () { 9105 if ($(this).length > 0) { 9106 var ulOffset = $(this).position().left + wrapper.position().left; 9107 9108 $(this).find("li").each(function () { 9109 if (!$(this).hasClass("databound")) { // Check if already loaded 9110 var liOffset = $(this).position().left + ulOffset; 9111 if (liOffset >= 0 && liOffset < allowance) { 9112 itemDataPromises.push(methods.getItemDataPromise(this)); 9113 } 9114 } 9115 }); 9116 } 9117 }); 9118 9119 var allItemsLoaded = Promise.all(itemDataPromises); 9120 var itemCount = wrapper.find("li").length; 9121 9122 allItemsLoaded.then(() => { 9123 var prices = wrapper.find("span"); 9124 9125 if (prices.length != itemCount) { 9126 return; 9127 } 9128 9129 var prices = wrapper.find("span"); 9130 $(".RecentlyViewedWrapper").removeClass("price-range"); 9131 9132 for (var i = 0; i < prices.length; i++) { 9133 var price = prices[i]; 9134 9135 if (price.innerText.includes("-")) { 9136 $(".RecentlyViewedWrapper").addClass("price-range"); 9137 9138 if (price.innerText.includes("-") && Next.Settings.Channel.ResponsiveShoppingBagIsEnabled && url.indexOf("/shoppingbag") > -1) { 9139 var splitedPrice = price.innerText.split("-"); 9140 var nicelyBrokenText = `<span>${splitedPrice[0]} -</span><span class="price-second-part"> ${splitedPrice[1]}</span>`; 9141 price.innerHTML = nicelyBrokenText; 9142 } 9143 } 9144 } 9145 }); 9146 } 9147 }, 9148 getItemData: function (li, callback) { 9149 //Loads the item component for a given list-item with provided data-attributes 9150 var $li = $(li); 9151 $li.addClass("databound"); 9152 var path = $li.attr("data-itempath"); 9153 var url = $li.attr("data-itemURL"); 9154 9155 RecentlyViewed.getItem(path, function (data) { 9156 if (data !== undefined && data !== null && data.indexOf("<a") > -1) { 9157 var d = $(data); 9158 var img = d.find("img"); 9159 9160 if ($li.data("type") != "tabbedItem") { 9161 d.attr("href", url); 9162 } 9163 img.css("opacity", 0); 9164 $li.prepend(d); 9165 img.animate({ opacity: 1 }, 300); 9166 } 9167 else { 9168 var siblings = $li.siblings().length; 9169 $li.remove(); 9170 if (siblings === 0) { 9171 methods.displayClearDataUI(); 9172 } 9173 } 9174 9175 if (callback) { 9176 callback(); 9177 } 9178 }); 9179 }, 9180 getItemDataPromise: function (li) { 9181 return new Promise((resolve, reject) => { 9182 this.getItemData(li, () => { 9183 resolve(); 9184 }); 9185 }); 9186 }, 9187 enableTouchEvents: function (element) { 9188 9189 var config = { 9190 touchStart: methods.touchStart, 9191 touchMove: methods.touchMove, 9192 touchEnd: methods.touchEnd, 9193 useMouse: true 9194 } 9195 9196 $(element).NXTouch(config); 9197 9198 var maxScroll = -(($(element).find("li").length * 95) - 855); 9199 9200 $(element).attr("data-maxscroll", maxScroll) 9201 9202 }, 9203 touchStart: function (e, NXTouchObj) { 9204 isTouchDown = true; 9205 9206 var targetEl = $(e.currentTarget).find(".recentlyViewedGroupWrapper"); 9207 var currentX = parseInt(targetEl.css("left").replace("px")); 9208 9209 if (!currentX) { 9210 currentX = 0; 9211 } 9212 9213 targetEl.attr("data-startx", currentX); 9214 }, 9215 touchMove: function (e, NXTouchObj) { 9216 9217 if (isTouchDown) { 9218 var maxScroll = parseInt($(e.currentTarget).attr("data-maxscroll")); 9219 9220 var targetEl = $(e.currentTarget).find(".recentlyViewedGroupWrapper"); 9221 9222 var targetX = parseInt(targetEl.attr("data-startx")) + NXTouchObj.DistanceX; 9223 9224 9225 if (targetX >= 0) { 9226 9227 targetX = 0; 9228 9229 $(e.currentTarget).closest(".scrollRowWrapper").find("a.scrollNav.previous").addClass("disabled"); 9230 9231 } else { 9232 $(e.currentTarget).closest(".scrollRowWrapper").find("a.scrollNav.previous").removeClass("disabled"); 9233 } 9234 9235 if (targetX <= maxScroll) { 9236 targetX = maxScroll; 9237 $(e.currentTarget).closest(".scrollRowWrapper").find("a.scrollNav.next").addClass("disabled"); 9238 } else { 9239 $(e.currentTarget).closest(".scrollRowWrapper").find("a.scrollNav.next").removeClass("disabled"); 9240 } 9241 9242 targetEl.css("left", targetX); 9243 9244 methods.loadVisibleItems(targetEl); 9245 } 9246 }, 9247 touchEnd: function (e, NXTouchObj) { 9248 isTouchDown = false; 9249 var targetEl = $(e.currentTarget).find(".recentlyViewedGroupWrapper"); 9250 var currentX = parseInt(targetEl.css("left").replace('px', '')); 9251 9252 var snapTo = (Math.round(currentX / 95)) * 95; 9253 var maxScroll = parseInt($(e.currentTarget).attr("data-maxscroll")); 9254 targetEl.animate({ left: snapTo + "px" }, 300, function () { 9255 9256 if (snapTo == 0) { 9257 9258 $(e.currentTarget).closest(".scrollRowWrapper").find("a.scrollNav.previous").addClass("disabled"); 9259 9260 } else { 9261 $(e.currentTarget).closest(".scrollRowWrapper").find("a.scrollNav.previous").removeClass("disabled"); 9262 } 9263 9264 if (snapTo == maxScroll) { 9265 9266 $(e.currentTarget).closest(".scrollRowWrapper").find("a.scrollNav.next").addClass("disabled"); 9267 } else { 9268 $(e.currentTarget).closest(".scrollRowWrapper").find("a.scrollNav.next").removeClass("disabled"); 9269 } 9270 }); 9271 9272 }, 9273 displayClearDataUI: function () { 9274 9275 $(".recentlyViewedGroupWrapper").fadeOut(500); 9276 9277 //Fade in message 9278 $(".emptyMessage").fadeIn(500); 9279 9280 //Disable clear link 9281 $("a.clearAll").addClass("disabled"); 9282 9283 //Disable scroll buttons 9284 $("a.scrollNav.previous").addClass("disabled", 200);
9285 $("a.scrollNav.next").addClass("disabled", 200); 9286 9287 //Make sure clip background is visible 9288 $(".clip").removeClass("noBackground"); 9289 9290 //Update display count 9291 if (Next.FeatureManager.FeatureSettings.RecentlyViewed.DisplayCounter) { 9292 $(".viewCounter").text("0"); 9293 } 9294 } 9295 }; 9296 9297 $.fn.ItemScroller = function (method) { 9298 9299 if (methods[method]) { 9300 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 9301 } else if (typeof method === 'object' || !method) { 9302 return methods.init.apply(this, arguments); 9303 } else { 9304 $.error('Method ' + method + ' does not exist on jQuery.ItemScroller'); 9305 } 9306 }; 9307 9308})(jQuery); 9309 9310; 9311// ------------------------------------------------------------------------------------------ // 9312// Name: NXTouch - Touch Event Layer Plug-in 9313// Authors: John Loydall 9314// Copyright: Next PLC 9315// Description: Abstracts the touch events from different vendors/devices 9316// into a common set of touch events with custom, device-independent touch objects 9317// 9318// ------------------------------------------------------------------------------------------ // 9319 9320/* 9321 9322//Usage//////////////////////////////////////// 9323 9324Initialise the component to be touch-enabled. Pass in config object defining function calls for touch events 9325along with other initialisation paramaters 9326 9327//Parameters//////////////////////////////////// 9328 9329touchStart - named function to handle touch start 9330touchMove - named function to handle touch move 9331touchEnd - named function to handle touch end 9332useMouse - boolean to indicate whether to map mouse events to the touch event handers (useful for testing) 9333 9334e.g. 9335var config = { 9336touchStart: touchStart, 9337touchMove: touchMove, 9338touchEnd: touchEnd, 9339useMouse: true 9340} 9341 9342Then initialise: 9343 9344$(element).NXTouch(config); 9345 9346And - make sure you define each function with the signature (e, NXTouchObj) 9347e.g. 9348 9349function touchMove(e, NXTouchObj) { 9350 9351} 9352 9353...where e will be the original event and NXTouchObj is the custom client touch point object 9354 9355The custom object exposes a number of properties of the current client point regardless of device 9356 9357e.g. 9358{ 9359DistanceX: 28, 9360DistanceY: 2, 9361SpeedX: 1, 9362SpeedY: 0, 9363X: 68, 9364Y: 28 9365} 9366 9367e.g. 9368 9369function touchMove(e, NXTouchObj) { 9370console.log(NXTouchObj.DistanceX); 9371} 9372 9373 9374*/ 9375 9376 9377 9378 9379 9380 9381 9382(function ($) { 9383 var startX = 0; 9384 var startY = 0; 9385 var currentX = 0; 9386 var currentY = 0; 9387 var previousX = 0; 9388 var previousY = 0; 9389 var movedX = 0; 9390 var movedY = 0; 9391 var speedX = 0; 9392 var speedY = 0; 9393 var currentClientPoint = {}; 9394 var isMouseDown = false; 9395 var useMouse = false; 9396 var elementOffsetX = 0; 9397 var elementOffsetY = 0; 9398 var methods = { 9399 init: function (config) { 9400 9401 //If device isn't touch enabled and mouse input isn't set, return false 9402 if (!isTouchEnabled && !config.useMouse) { 9403 return false; 9404 } 9405 9406 9407 elementOffsetX = this.offset().left; 9408 elementOffsetY = this.offset().top; 9409 9410 9411 9412 //If using mouse, set plug-in variable 9413 if (config.useMouse) { 9414 useMouse = true; 9415 } 9416 9417 //If a touchstart event handler has been defined, bind to the corresponding device events 9418 if (config.touchStart) { 9419 9420 if (isTouchEnabled) { 9421 this.bind('touchstart', function (e) { $(this).NXTouch('tStart', e, config.touchStart); }) 9422 } 9423 if (isMSPointerDevice) { 9424 this[0].addEventListener('MSPointerDown', function (e) { $(this).NXTouch('tStart', e, config.touchStart); }) 9425 } 9426 // if (useMouse) { 9427 // this.bind('mousedown', function (e) { $(this).NXTouch('tStart', e, config.touchStart); }) 9428 // } 9429 } 9430 //If a touchMove event handler has been defined, bind to the corresponding device events 9431 if (config.touchMove) { 9432 9433 if (isTouchEnabled) { 9434 this.bind('touchmove', function (e) { $(this).NXTouch('tMove', e, config.touchMove); }) 9435 } 9436 if (isMSPointerDevice) { 9437 this[0].addEventListener('MSPointerMove', function (e) { $(this).NXTouch('tMove', e, config.touchMove); }) 9438 } 9439 // if (useMouse) { 9440 // this.bind('mousemove', function (e) { $(this).NXTouch('tMove', e, config.touchMove); }) 9441 // } 9442 } 9443 //If a touchEnd event handler has been defined, bind to the corresponding device events 9444 if (config.touchEnd) { 9445 9446 if (isTouchEnabled) { 9447 this.bind('touchend', function (e) { $(this).NXTouch('tEnd', e, config.touchEnd); }) 9448 } 9449 if (isMSPointerDevice) { 9450 this[0].addEventListener('MSPointerUp', function (e) { $(this).NXTouch('tEnd', e, config.touchEnd); }) 9451 } 9452 // if (useMouse) { 9453 // this.bind('mouseup', function (e) { $(this).NXTouch('tEnd', e, config.touchEnd); }) 9454 // } 9455 } 9456 9457 }, 9458 tStart: function (e, targetFunction) { 9459 9460 //touchStart wrapper - creates a standard client point object from the different device touch event properties 9461 var clientPoint = {}; 9462 9463 9464 //Get client X & Y from different devices 9465 9466 9467 if (event.touches != null) { 9468 clientPoint.X = event.touches[0].pageX - elementOffsetX; 9469 clientPoint.Y = event.touches[0].pageY - elementOffsetY; 9470 } 9471 else if (e.pageX != null) { 9472 clientPoint.X = e.pageX - elementOffsetX; 9473 clientPoint.Y = e.pageY - elementOffsetY; 9474 } 9475 9476 9477 // if (useMouse) { 9478 // isMouseDown = true; 9479 // clientPoint.X = e.clientX - elementOffsetX; 9480 // clientPoint.Y = e.clientY - elementOffsetY; 9481 // } 9482 9483 //Store client location 9484 startX = clientPoint.X; 9485 startY = clientPoint.Y; 9486 9487 currentX = clientPoint.X; 9488 currentY = clientPoint.Y; 9489 9490 currentClientPoint = clientPoint; 9491 9492 //Pass custom client object to function defined in plugin config
9493 targetFunction(e, clientPoint); 9494 9495 }, 9496 tMove: function (e, targetFunction) { 9497 9498 e.preventDefault(); 9499 9500 9501 9502 if (useMouse && !isMouseDown) { 9503 9504 // return false; 9505 } 9506 9507 var clientPoint = {}; 9508 9509 9510 9511 if (event.touches != null) { 9512 clientPoint.X = event.touches[0].pageX - elementOffsetX; 9513 clientPoint.Y = event.touches[0].pageY - elementOffsetY; 9514 } 9515 else if (e.pageX != null) { 9516 9517 switch (e.pointerType) { 9518 case e.MSPOINTER_TYPE_TOUCH: 9519 //console.log("touch"); 9520 break; 9521 case e.MSPOINTER_TYPE_PEN: 9522 return false; 9523 break; 9524 case e.MSPOINTER_TYPE_MOUSE: 9525 return false; 9526 break; 9527 } 9528 9529 clientPoint.X = e.pageX - elementOffsetX; 9530 clientPoint.Y = e.pageY - elementOffsetY; 9531 rvScrollOffset = currentX - startX; //this will be useful in Footer.js to identify item scrolling has taken place or not 9532 } 9533 9534 9535 // if (useMouse) { 9536 // clientPoint.X = e.clientX - elementOffsetX; 9537 // clientPoint.Y = e.clientY - elementOffsetY; 9538 // } 9539 9540 previousX = currentX; 9541 previousY = currentY; 9542 9543 currentX = clientPoint.X; 9544 currentY = clientPoint.Y; 9545 9546 movedX = currentX - startX; 9547 movedY = currentY - startY; 9548 9549 speedX = currentX - previousX; 9550 speedY = currentY - previousY; 9551 9552 clientPoint.DistanceX = movedX; 9553 clientPoint.DistanceY = movedY; 9554 9555 clientPoint.SpeedX = speedX; 9556 clientPoint.SpeedY = speedY; 9557 9558 currentClientPoint = clientPoint; 9559 9560 targetFunction(e, clientPoint); 9561 9562 }, 9563 tEnd: function (e, targetFunction) { 9564 9565 isMouseDown = false; 9566 var clientPoint = currentClientPoint; 9567 targetFunction(e, clientPoint); 9568 } 9569 }; 9570 9571 $.fn.NXTouch = function (method) { 9572 9573 //Method calling 9574 if (methods[method]) { 9575 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 9576 } else if (typeof method === 'object' || !method) { 9577 return methods.init.apply(this, arguments); 9578 } else { 9579 $.error('Method ' + method + ' does not exist on jQuery.NXTouch'); 9580 } 9581 9582 }; 9583 9584})(jQuery);; 9585/*! 9586* Modal Popup 9587* Version: 2014.5.9.0 9588* Original author: Curtis Timson 9589*/ 9590 9591var ModalPopup = { 9592 options: null, 9593 vars: { 9594 showMidClass: "show-mid", 9595 showClass: "show", 9596 modalPopupClass: "ModalPopup", 9597 modalPopupContainerClass: "ModalPopupContainer", 9598 modalPopupContainerOverlayClass: "overlay", 9599 modalPopupWrapperClass: "ModalPopupWrapper", 9600 $modalPopupContainer: null, 9601 $modalPopupWrapper: null, 9602 $modalPopup: null, 9603 isActive: false, 9604 modalDataSet: null 9605 }, 9606 constants: { 9607 scrollLockEvents: "mousewheel.modalscroll DOMMouseScroll.modalscroll MozMousePixelScroll.modalscroll" 9608 }, 9609 init: function (options) { 9610 var _this = this; 9611 9612 //Plugin Name and Default values 9613 var pluginName = 'ModalPopup', 9614 defaults = { 9615 closeClass: 'closemodal', 9616 scrollClass: 'ModalScroll', 9617 overlay: true, 9618 width: null, 9619 height: null, 9620 onPreClose: null, 9621 onClose: null, 9622 lockBackgroundScroll: false, 9623 closeOnContainerClick: true, 9624 fixedTopPosition: null,//these variables are used by Secci responsive design 9625 fixedLeftPosition: null, 9626 fullyLockBackgroundScroll: false, //this is used for Find in Store modal on Desktop PDP, this will override lockBackgroundScroll 9627 isPersonalisedGift: false 9628 }; 9629 9630 _this.options = $.extend({}, defaults, options); 9631
9632 if (_this.options && _this.options.fullyLockBackgroundScroll && _this.vars.$modalPopup) { 9633 _this.vars.$modalPopup.remove(); 9634 } 9635 9636 _this.vars.$modalPopup = $("." + _this.vars.modalPopupClass); 9637 if (!_this.vars.$modalPopup.length) { 9638 _this.vars.$modalPopup = $("<div/>"); 9639 _this.vars.$modalPopup.addClass(_this.vars.modalPopupClass); 9640 if (_this.options.isPersonalisedGift) { 9641 _this.vars.$modalPopup.addClass("PersonalisedModal"); 9642 } 9643 9644 if (_this.options && _this.options.fullyLockBackgroundScroll) { 9645 _this.vars.$modalPopup.css("position", "relative"); 9646 _this.vars.$modalPopupWrapper = $("<div/>"); 9647 _this.vars.$modalPopupWrapper.addClass(_this.vars.modalPopupWrapperClass); 9648 $("body").append(_this.vars.$modalPopupWrapper.append(_this.vars.$modalPopup)); 9649 } else { 9650 $("body").append(_this.vars.$modalPopup); 9651 } 9652 } 9653 9654 9655 _this.addContainer(); 9656 9657 if (_this.options.isPersonalisedGift){ 9658 this.CalculateScreenSpaceForPersonalisedGiftModal(); 9659 } 9660 9661 if(!_this.vars.$modalPopup.hasClass("ZoomedModal")){ 9662 if (_this.options.width) { 9663 _this.vars.$modalPopup.width(_this.options.width); 9664 } 9665 9666 if (_this.options.height) { 9667 _this.vars.$modalPopup.height(_this.options.height); 9668 } 9669 } 9670 else { 9671 _this.vars.$modalPopup.removeAttr("style"); 9672 } 9673 9674 //close binding 9675 _this.vars.$modalPopup.on("click", "." + _this.options.closeClass, function (e) { 9676 _this.close(e); 9677 }); 9678 9679 //window resize 9680 $(window).resize(function () { 9681 _this.setModalPosition(); 9682 }); 9683 9684 if (_this.options && _this.options.fullyLockBackgroundScroll) { 9685 this.LockBackgroundScroll(); 9686 } 9687 else if (_this.options.lockBackgroundScroll) { 9688 this.EnableScrollLock(); 9689 } 9690 9691 if (_this.options.modalDataSet) { 9692 _this.vars.modalDataSet = this.options.modalDataSet; 9693 } 9694 9695 9696 return _this.vars.$modalPopup; 9697 }, 9698 addContainer: function () { 9699 var _this = this; 9700 _this.vars.$modalPopupContainer = $("<div/>"); 9701 _this.vars.$modalPopupContainer.addClass(_this.vars.modalPopupContainerClass); 9702 if (_this.options.overlay) { 9703 _this.vars.$modalPopupContainer.addClass(_this.vars.modalPopupContainerOverlayClass); 9704 } 9705 9706 if (_this.options && _this.options.fullyLockBackgroundScroll) { 9707 _this.vars.$modalPopupWrapper.before(_this.vars.$modalPopupContainer); 9708 } else { 9709 _this.vars.$modalPopup.before(_this.vars.$modalPopupContainer); 9710 } 9711 9712 _this.vars.$modalPopupContainer = $("." + _this.vars.modalPopupContainerClass); 9713 _this.vars.$modalPopupContainer.click(function (e) { 9714 if (_this.options.closeOnContainerClick) { 9715 _this.close(e); 9716 } 9717 }); 9718 }, 9719 show: function () { 9720 var _this = this; 9721 9722 if ($("." + _this.vars.modalPopupContainerClass).length < 1) { 9723 _this.addContainer(); 9724 } 9725 9726 _this.setModalPosition(); 9727 9728 if (_this.options && _this.options.fullyLockBackgroundScroll) { 9729 _this.vars.$modalPopupWrapper.addClass(_this.vars.showClass); 9730 } 9731 9732 _this.vars.$modalPopup.addClass(_this.vars.showMidClass); 9733 setTimeout(function () { 9734 _this.vars.$modalPopup.addClass(_this.vars.showClass); 9735 document.dispatchEvent(new CustomEvent("PopUpDisplayed", this)); 9736 }, 1); 9737 9738 _this.vars.isActive = true; 9739 }, 9740 close: function (e) { 9741 9742 var _this = this; 9743 9744 if (_this.options && _this.options.fullyLockBackgroundScroll) { 9745 $("body").css("overflow", ""); 9746 } 9747 9748 if (_this.vars.isActive) { 9749 if (e) { 9750 e.preventDefault(); 9751 } 9752 9753 if (_this.options.onPreClose) { 9754 _this.options.onPreClose(); 9755 } 9756 9757 //Remove container element 9758 if (_this.vars.$modalPopupContainer) { 9759 9760 if (_this.options && !_this.options.fullyLockBackgroun
9760dScroll) { 9761 _this.vars.$modalPopupContainer.off(_this.constants.scrollLockEvents); 9762 } 9763 9764 _this.vars.$modalPopupContainer.replaceWith(_this.vars.$modalPopupContainer.contents()); 9765 } 9766 9767 if (_this.options && _this.options.fullyLockBackgroundScroll) { 9768 _this.vars.$modalPopupWrapper.remove(); 9769 } else { 9770 _this.vars.$modalPopup.off(_this.constants.scrollLockEvents); 9771 } 9772 9773 _this.vars.$modalPopup.removeClass(_this.vars.showClass); 9774 setTimeout(function () { 9775 _this.vars.$modalPopup.removeClass(_this.vars.showMidClass); 9776 }, 100); 9777 9778 if (_this.options && !_this.options.fullyLockBackgroundScroll) { 9779 $(document).off(_this.constants.scrollLockEvents); 9780 } 9781 9782 _this.vars.isActive = false; 9783 if (_this.options.onClose) { 9784 _this.options.onClose(); 9785 } 9786 } 9787 }, 9788 setModalPosition: function () { 9789 var _this = this; 9790 if(_this.options.isPersonalisedGift){ 9791 this.CalculateScreenSpaceForPersonalisedGiftModal(); 9792 } 9793 if(!_this.vars.$modalPopup.hasClass("ZoomedModal")){ 9794 //Adjust position of modal popup 9795 var modalPopupTop = parseInt(($(window).height() / 2) - (_this.vars.$modalPopup.outerHeight() / 2), 10); 9796 var modalPopupLeft = parseInt(($(window).width() / 2) - (_this.vars.$modalPopup.outerWidth() / 2), 10); 9797 9798 if (_this.options.fixedTopPosition != null) { 9799 modalPopupTop = _this.options.fixedTopPosition; 9800 } 9801 9802 if (_this.options.fixedLeftPosition != null) { 9803 modalPopupLeft = _this.options.fixedLeftPosition; 9804 } 9805 9806 _this.vars.$modalPopup.css("top", modalPopupTop); 9807 _this.vars.$modalPopup.css("left", modalPopupLeft); 9808 } 9809 else{ 9810 _this.vars.$modalPopup.removeAttr("style"); 9811 } 9812 }, 9813 EnableScrollLock: function () { 9814 var _this = this; 9815 9816 $(document).on(_this.constants.scrollLockEvents, function (e) { 9817 return false; 9818 }); 9819 9820 _this.vars.$modalPopupContainer.on(_this.constants.scrollLockEvents, function (e) { 9821 e.preventDefault(); 9822 }); 9823 _this.vars.$modalPopup.on(_this.constants.scrollLockEvents, function (e) { 9824 e.preventDefault(); 9825 9826 var $target = $(e.target); 9827 var $scrollableElement = _this.vars.$modalPopup.find("." + _this.options.scrollClass); 9828 var $targetScrollable = $target.closest($scrollableElement); 9829 9830 if ($targetScrollable.length) { 9831 var scrollTo = null; 9832 9833 if (e.type == 'mousewheel') { 9834 scrollTo = (e.originalEvent.wheelDelta * -1); 9835 } 9836 else if (e.type == 'DOMMouseScroll') { 9837 scrollTo = 40 * e.originalEvent.detail; 9838 } 9839 9840 if (scrollTo) { 9841 $targetScrollable.scrollTop(scrollTo + $targetScrollable.scrollTop()); 9842 } 9843 } 9844 9845 }); 9846 }, 9847 LockBackgroundScroll: function () { 9848 $("body").css("overflow", "hidden"); 9849 }, 9850 GetModalDataSet: function() { 9851 return this.vars.modalDataSet; 9852 }, 9853 9854 CalculateScreenSpaceForPersonalisedGiftModal: function(){ 9855 var _this = this; 9856 if(window.innerHeight <= 625){ 9857 _this.vars.$modalPopup.addClass("ZoomedModal"); 9858 } 9859 else{ 9860 _this.vars.$modalPopup.removeClass("ZoomedModal"); 9861 } 9862 } 9863};; 9864/* 9865 * June 2015 9866 * This code is written to support search suggestions in departments. 9867 * However the departments have been removed from the SLI feed, so much of the code is 9868 * redundant. This might change in the future. 9869 * 9870 * October 2019 9871 * The search auto complete has been updated to include an enriched view with product list 9872 */ 9873var Search; 9874(function (Search) { 9875 var Keys; 9876 (function (Keys) { 9877 Keys[Keys["UpArrow"] = 38] = "UpArrow"; 9878 Keys[Keys["DownArrow"] = 40] = "DownArrow";
9879 Keys[Keys["Tab"] = 9] = "Tab"; 9880 Keys[Keys["Enter"] = 13] = "Enter"; 9881 Keys[Keys["Escape"] = 27] = "Escape"; 9882 })(Keys || (Keys = {})); 9883 var DomSelector; 9884 (function (DomSelector) { 9885 DomSelector["Input"] = "sli_search_1"; 9886 DomSelector["Form"] = "#newsearch"; 9887 DomSelector["StateIsEnriched"] = "is-enriched"; 9888 DomSelector["StateIsLoading"] = "is-loading"; 9889 DomSelector["StateHideIfNoResults"] = "hide-if-no-results"; 9890 DomSelector["StateShowRecent"] = "show-recent"; 9891 DomSelector["StateNoRecent"] = "no-recent"; 9892 DomSelector["StateSelected"] = "selected"; 9893 DomSelector["StateNoResults"] = "no-results"; 9894 DomSelector["SearchDropdownCover"] = "search-dropdown-content-cover"; 9895 DomSelector["RecentHeading"] = "recent-heading"; 9896 DomSelector["ClearRecent"] = "clear-recent"; 9897 DomSelector["SearchItem"] = "search-item"; 9898 DomSelector["Open"] = "open"; 9899 DomSelector["Divider"] = "divider"; 9900 DomSelector["Title"] = "title"; 9901 DomSelector["CurrentSearchTerm"] = "freeTextSearchTerm"; 9902 })(DomSelector || (DomSelector = {})); 9903 var AutoComplete = /** @class */ (function () { 9904 function AutoComplete(baseAddress, recentSearchesSaved, suggestionsEnabled, autoCompleteUrl) { 9905 var _this = this; 9906 this.recentSearches = new KeyCookies(recentSearchesSaved); 9907 this.input = document.getElementById(DomSelector.Input); 9908 this.filter = document.getElementById('search-af'); 9909 this.$form = $('#newsearch'); 9910 this.$dropdownContainer = this.$form.find('.search-dropdown-container'); 9911 this.$dropdown = this.$form.find('.search'); 9912 if (baseAddress.length > 0 && baseAddress.charAt(baseAddress.length - 1) !== "/") { 9913 baseAddress += "/"; 9914 } 9915 this.url = baseAddress + autoCompleteUrl; 9916 this.$form.submit(function (e) { return _this.searchSubmit(); }); 9917 var $input = $(this.input); 9918 $input.on('focus, click', function (e) { 9919 _this.openDropdown(); 9920 }); 9921 $input.on('blur', function (e) { 9922 _this.delayCloseDropdown(); 9923 }); 9924 $('#MainNavigation').hover(function (e) { return _this.delayCloseDropdown(); }); 9925 this.setLastSearch(); 9926 this.initClearSearchTextButton(); 9927 this.initSelectionEvents(); 9928 if (suggestionsEnabled) { 9929 this.initSuggestions(); 9930 } 9931 else { 9932 this.initNoSuggestions(); 9933 } 9934 } 9935 /** 9936 * Use this to unload this class and all associated DOM items 9937 */ 9938 AutoComplete.prototype.destroy = function () { 9939 // Remove all events on input field 9940 $('#' + DomSelector.Input).off('click blur focus keyup keydown'); 9941 // Remove items added to the DOM 9942 this.$dropdown.empty(); 9943 }; 9944 /** 9945 * Initialise when suggestions are turned off 9946 */ 9947 AutoComplete.prototype.initNoSuggestions = function () { 9948 var _this = this; 9949 var inputText = this.input.value; 9950 $(this.input).keyup(function (e) { 9951 // No change so return 9952 if (_this.input.value === inputText) 9953 return; 9954 inputText = _this.input.value; 9955 // Ignore keys that can change the content but shouldn't close the dropdown 9956 if (_this.IgnoreKey(e.keyCode)) 9957 return; 9958 _this.clearDropdown(); 9959 if (inputText.length == 0) 9960 _this.loadRecentSearches(); 9961 _this.refresh(); 9962 }); 9963 }; 9964 /** 9965 * Set up button to clear the search text. 9966 */ 9967 AutoComplete.prototype.initClearSearchTextButton = function () { 9968 var _this = this; 9969 var btn = this.$form.find('.clear-search-text'); 9970 this.showOrHideClearSearchTextButton(); 9971 btn.click(function (e) { 9972 _this.input.value = ''; 9973 _this.showOrHideClearSearchTextButton(); 9974 _this.clearDropdown(); 9975 _this.loadRecentSearches(); 9976 setTimeout(function () { return _this.input.focus(); }, 500); 9977 }); 9978 }; 9979 AutoComplete.prototype.showOrHideClearSearchTextButton = function () { 9980 var btn = this.$form.find('.clear-search-text'); 9981 var show = this.input.value !== ''; 9982 show ? btn.addClass("visible") : btn.removeClass("visible"); 9983 }; 9984 /** 9985 * Initialise the suggestion searches 9986 */ 9987 AutoComplete.prototype.initSuggestions = function () { 9988 var _this = this; 9989 var inputText; 9990 var timeoutId = 0; 9991 $(this.input).keyup(function (e) { 9992 // If no change in input return 9993 if (_this.input.value === inputText) 9994 return; 9995 inputText = _this.input.value; 9996 _this.showOrHideClearSearchTextButton(); 9997 // Ignore keys that can change the content but don't need to invoke a search
9998 if (_this.IgnoreKey(e.keyCode)) 9999 return; 10000 // Input changed so clear any selection 10001 _this.clearSelected(); 10002 // Restart timer 10003 clearTimeout(timeoutId); 10004 timeoutId = setTimeout(function () { 10005 if (inputText.length == 0) { 10006 _this.clearDropdown(); 10007 _this.loadRecentSearches(); 10008 _this.refresh(); 10009 } 10010 else { 10011 if (Next.Settings.Channel.BloomReachSearchOverrideEnabled) { 10012 _this.brsearch(inputText) 10013 .done(function (result) { 10014 _this.clearDropdown(); 10015 _this.brProcessResponse(result); 10016 _this.refresh(); 10017 }) 10018 .fail(function (xhr, status, error) { 10019 ConsoleLog("Auto-complete feed error: " + status); 10020 _this.clearDropdown(); 10021 _this.refresh(); 10022 }); 10023 } 10024 //start: to remove 10025 else if (Next.Settings.Channel.BloomReachSearchEnabled && 10026 typeof _this.recentSearches.monetateSearchCookie != 'undefined' && 10027 _this.recentSearches.monetateSearchCookie.indexOf(Next.Settings.Cookie.MonetateCookieBloomReachValue) === 0) { 10028 _this.brsearch(inputText) 10029 .done(function (result) { 10030 _this.clearDropdown(); 10031 _this.brProcessResponse(result); 10032 _this.refresh(); 10033 }) 10034 .fail(function (xhr, status, error) { 10035 ConsoleLog("Auto-complete feed error: " + status); 10036 _this.clearDropdown(); 10037 _this.refresh(); 10038 }); 10039 } 10040 } 10041 }, 500); 10042 }); 10043 this.initialSuggestions(); 10044 }; 10045 AutoComplete.prototype.initSelectionEvents = function () { 10046 var _this = this; 10047 $(this.input).keydown(function (e) { 10048 switch (e.keyCode) { 10049 case Keys.UpArrow: 10050 case Keys.DownArrow: 10051 e.preventDefault(); 10052 _this.keyUpDown(e.keyCode); 10053 break; 10054 case Keys.Enter: 10055 return _this.selectItem(); 10056 } 10057 }); 10058 }; 10059 /** 10060 * Initialise the suggestions 10061 */ 10062 AutoComplete.prototype.initialSuggestions = function () { 10063 var _this = this; 10064 if (this.input.value && Next.Settings.Channel.BloomReachSearchOverrideEnabled) { 10065 this.brsearch(this.input.value) 10066 .done(function (result) { 10067 _this.clearDropdown(); 10068 _this.brProcessResponse(result); 10069 _this.refresh(); 10070 }) 10071 .fail(function (xhr, status, error) { 10072 if ('abort' !== status) { 10073 // If ajax request fails then it could be due to adblocker 10074 _this.clearDropdown(); 10075 _this.refresh(); 10076 } 10077 }); 10078 } 10079 }; 10080 /** 10081 * Which keys to ignore when search input has focus. 10082 */ 10083 AutoComplete.prototype.IgnoreKey = function (keyCode) { 10084 if (keyCode == Keys.UpArrow) 10085 return true; 10086 if (keyCode == Keys.DownArrow) 10087 return true; 10088 return false; 10089 }; 10090 /** 10091 * Invoke the Ajax search for suggestions 10092 */ 10093 AutoComplete.prototype.search = function (term) { 10094 var ajaxSettings = { 10095 url: this.url, 10096 dataType: "json", 10097 cache: true, 10098 data: { 10099 word: term 10100 } 10101 }; 10102 return $.ajax(ajaxSettings); 10103 }; 10104 /** 10105 * Parse the SLI response and return the suggestions array. 10106 */ 10107 AutoComplete.prototype.processResponse = function (response) { 10108 var _this = this; 10109 var ss = response.results.search_suggestions; 10110 if (!ss) 10111 return null; 10112 if (ss.department_suggestions) 10113 $.each(ss.department_suggestions, function (index, item) { return _this.addDepartment(item); }); 10114 if (ss.suggestions) { 10115 if (this.haveItemsInList()) { 10116 this.addDivider(); 10117 this.addSearchesTitle(); 10118 } 10119 $.each(ss.suggestions, function (index, text) { return _this.addSuggestion(text); }); 10120 } 10121 }; 10122 //bloomreach 10123 /** 10124 * Invoke the Ajax search for suggestions 10125 */ 10126 AutoComplete.prototype.brsearch = function (term) { 10127 var bruid = this.recentSearches.brUIDCookie; 10128 var requestID = new Date().getTime(); 10129 var viewId = Next.Settings.Channel.BloomReachAutoSuggestViewIdEnabled ? (Next.Settings.Channel.BloomReachCountryCode ? "&view_id=" + Next.Settings.Channel.BloomReachCountryCode : "") : ""; // View_id is a parameter that needs to be present for Intl but not for UK 10130 var url = Next.Settings.Channel.BloomReachAutosuggestDomain + 10131 "?account_id=" + Next.Settings.Channel.BloomReachAccountID + 10132 "&auth_key=" + 10133 "&domain_key=" + Next.Settings.Channel.BloomReachDomainKey + 10134 viewId + 10135 "&request_id=" + requestID + 10136 "&_br_uid_2=" + bruid + 10137 "&request_type=suggest" + 10138 "&q=" + term; 10139 var ajaxSettings = { 10140 url: url, 10141 dataType: "json" 10142 }; 10143 return $.ajax(ajaxSettings); 10144 }; 10145 /** 10146 */ 10147 AutoComplete.prototype.brProcessResponse = function (response) { 10148 var _this = this; 10149 var ss = response.response; 10150 if (!ss) 10151 return null; 10152 if (ss.suggestions) { 10153 if (this.haveItemsInList()) { 10154 this.addDivider(); 10155 this.addSearchesTitle(); 10156 } 10157 ss.suggestions = ss.suggestions.slice(0, 10); 10158 $.each(ss.suggestions, function (index, s) { return _this.addSuggestion(s.q); }); 10159 } 10160 }; 10161 /** 10162 * Simulate a click on the currently selected item 10163 */ 10164 AutoComplete.prototype.selectItem = function () { 10165 var $selected = this.$dropdown.find('li.selected'); 10166 if (!$selected.length) 10167 return true; 10168 $selected.click(); 10169 return false; 10170 }; 10171 AutoComplete.prototype.keyUpDown = function (keyCode) { 10172 var $list = this.$dropdown.find('li.search-item'); 10173 var listLength = $list.length; 10174 if (listLength == 0) 10175 return; 10176 if (listLength == 1) { 10177 $list.first().addClass('selected'); 10178 return; 10179 } 10180 var $selected = $list.filter('.selected'); 10181 var index = $list.index($selected); 10182 $selected.removeClass('selected'); 10183 if (keyCode == Keys.UpArrow) { 10184 if (index-- < 0) 10185 index = listLength - 1; 10186 } 10187 if (keyCode == Keys.DownArrow) { 10188 if (++index == listLength) 10189 index = 0; 10190 } 10191 var $li = $list.eq(index); 10192 $li.addClass('selected'); 10193 this.input.value = $li.data("term"); 10194 }; 10195 /** 10196 * Clear the selected list item 10197 */ 10198 AutoComplete.prototype.clearSelected = function () { 10199 var $item = this.$dropdown.find('li.selected'); 10200 $item.removeClass("selected"); 10201 }; 10202 /** 10203 * Add the heading in the list for the recently searched 10204 */ 10205 AutoComplete.prototype.addRecentHeading = function () { 10206 var _this = this; 10207 var $li = $("<li>") 10208 .addClass("recent-heading")
10209 .text(nxt.jstranslations.autocomplete.recentsearches || "Your Recent Searches...") 10210 .append("<a>") 10211 .find("a") 10212 .addClass("clear-recent") 10213 .attr("href", "#") 10214 .click(function () { return _this.deleteRecent(); }) 10215 .text(nxt.jstranslations.common.clear || "Clear") 10216 .end(); 10217 this.$dropdown.append($li); 10218 }; 10219 /** 10220 * Add the divider in the list 10221 */ 10222 AutoComplete.prototype.addDivider = function () { 10223 var $li = $("<li>") 10224 .addClass("divider"); 10225 this.$dropdown.append($li); 10226 }; 10227 /** 10228 * Add the title of the search section in the list 10229 */ 10230 AutoComplete.prototype.addSearchesTitle = function () { 10231 var $li = $("<li>") 10232 .addClass("title") 10233 .text(nxt.jstranslations.autocomplete.populasearches || "Popular Searches"); 10234 this.$dropdown.append($li); 10235 }; 10236 /** 10237 * Get the recently searched cookies and add them to the dropdown list 10238 */ 10239 AutoComplete.prototype.loadRecentSearches = function () { 10240 var _this = this; 10241 this.addRecentHeading(); 10242 var kc = this.recentSearches.load(); 10243 $.each(kc, function (i, s) { 10244 if (i < Next.FeatureManager.FeatureSettings.Search.RecentSearchesSaved) { 10245 _this.addRecent(s); 10246 } 10247 }); 10248 }; 10249 /** 10250 * Refresh the state of the search drop down 10251 */ 10252 AutoComplete.prototype.refresh = function () { 10253 if (this.haveItemsInList()) 10254 this.showDropdown(); 10255 else 10256 this.hideDropdown(); 10257 }; 10258 AutoComplete.prototype.openDropdown = function () { 10259 this.refresh(); 10260 }; 10261 AutoComplete.prototype.delayCloseDropdown = function () { 10262 var _this = this; 10263 // delay is visually better but also necessary to make sure the "clear" event is not squashed 10264 setTimeout(function () { return _this.hideDropdown(100); }, 150); 10265 }; 10266 AutoComplete.prototype.hideDropdown = function (duration) { 10267 duration ? this.$dropdownContainer.hide(duration, 'swing') : this.$dropdownContainer.hide(); 10268 $(this.input).removeClass('open'); 10269 }; 10270 AutoComplete.prototype.showDropdown = function () { 10271 this.$dropdownContainer.show(); 10272 $(this.input).addClass('open'); 10273 }; 10274 /** 10275 * Remove leading, trailing and double spaces. 10276 */ 10277 AutoComplete.prototype.clean = function (term) { 10278 term = term.replace(/^[\s]*/, '').replace(/[\s]*$/, '').replace(/ +/, ' '); 10279 return term; 10280 }; 10281 /** 10282 * The form is being submitted 10283 */ 10284 AutoComplete.prototype.searchSubmit = function () { 10285 this.input.value = this.clean(this.input.value); 10286 if (!this.input.value) 10287 return; 10288 // If filter is set recent searches have already been added. 10289 if (this.filter.value) 10290 return; 10291 var suggestion = { 10292 term: this.input.value, 10293 af: '', 10294 name: '' 10295 }; 10296 this.recentSearches.add(suggestion); 10297 }; 10298 /** 10299 * Set the search input control to the last searched term if on a search page. 10300 */ 10301 AutoComplete.prototype.setLastSearch = function () { 10302 var lastSearch = this.recentSearches.getLastSearch(); 10303 if (!lastSearch) { 10304 this.loadRecentSearches(); 10305 return; 10306 } 10307 if (this.onResultsPage()) { 10308 this.input.value = lastSearch; 10309 return; 10310 } 10311 this.recentSearches.clearLastSearch(); 10312 this.loadRecentSearches(); 10313 }; 10314 /** 10315 * return true if this is the search results page. 10316 */ 10317 AutoComplete.prototype.onResultsPage = function () { 10318 // To do when we can determine if we are a search results page. 10319 return false; 10320 }; 10321 /** 10322 * Remove all items from the list 10323 */ 10324 AutoComplete.prototype.clearDropdown = function () { 10325 this.$dropdown.empty(); 10326 }; 10327 AutoComplete.prototype.deleteRecent = function () { 10328 this.recentSearches.clear(); 10329 this.clearDropdown(); 10330 }; 10331 AutoComplete.prototype.dropdownItemSelected = function (item) { 10332 this.input.value = item; 10333 this.$form.submit(); 10334 }; 10335 AutoComplete.prototype.dropdownDepartmentItemSelected = function (item) { 10336 this.input.value = item.term; 10337 this.filter.value = item.af; 10338 this.recentSearches.add(item); 10339 this.$form.submit(); 10340 }; 10341 /** 10342 * Add a department suggestion into the dropdown list. 10343 */ 10344 AutoComplete.prototype.addDepartment = function (item) { 10345 var _this = this; 10346 var display = this.surroundTextThatMatchesInput(item.term); 10347 display += "<span class='dept-name'>" + nxt.stringFormat(nxt.jstranslations.autocomplete.autocompleteIn || "in", item.name) + "</span>"; 10348 var $li = $("<li/>", { "html": display, "class": "search-item" }) 10349 .click(function (e) { 10350 _this.dropdownDepartmentItemSelected(item); 10351 }); 10352 $li.data("term", item.term); 10353 this.$dropdown.append($li); 10354 }; 10355 /** 10356 * Add a search suggestion into the dropdown list. 10357 */ 10358 AutoComplete.prototype.addSuggestion = function (text) { 10359 var _this = this; 10360 var display = this.surroundTextThatMatchesInput(text); 10361 var $li = $("<li/>", { "html": display, "class": "search-item" }) 10362 .click(function (e) { 10363 _this.dropdownItemSelected(text); 10364 }); 10365 $li.data("term", text); 10366 this.$dropdown.append($li); 10367 }; 10368 /** 10369 * Add a recent search item into the dropdown list 10370 */ 10371 AutoComplete.prototype.addRecent = function (item) { 10372 if (item.name) { 10373 this.addDepartment(item); 10374 } 10375 else if (item.term) { 10376 this.addSuggestion(item.term); 10377 } 10378 }; 10379 /** 10380 * Surround the portion of the text that matches the input with a span 10381 */ 10382 AutoComplete.prototype.surroundTextThatMatchesInput = function (suggestion) { 10383 var input = this.clean(this.input.value); 10384 var term = input.replace(/ /g, "|"); 10385 var re = new RegExp(term, 'ig'); 10386 var display = suggestion.replace(re, "<span class='typed-text'>$&</span>");
10387 return display; 10388 }; 10389 AutoComplete.prototype.haveRecentSearches = function () { 10390 return this.recentSearches.any(); 10391 }; 10392 AutoComplete.prototype.haveItemsInList = function () { 10393 return this.$dropdown.find('li.search-item').length > 0; 10394 }; 10395 AutoComplete.prototype.haveInput = function () { 10396 return this.input.value.length > 0; 10397 }; 10398 return AutoComplete; 10399 }()); 10400 var EnrichedAutoComplete = /** @class */ (function () { 10401 function EnrichedAutoComplete(baseAddress, recentSearchesSaved, suggestionsEnabled, autoCompleteCharCountTrigger, autoCompleteUrl, hideIfNoResults) { 10402 var _this = this; 10403 this.searchTimeoutId = 0; 10404 this.recentSearches = new KeyCookies(recentSearchesSaved); 10405 this.eventLogger = new EventLogger(); 10406 this.input = document.getElementById(DomSelector.Input); 10407 this.filter = document.getElementById('search-af'); 10408 this.$form = $(DomSelector.Form); 10409 this.$dropdownContainer = this.$form.find('.search-dropdown-container'); 10410 this.$currentSearchHeading = this.$form.find('.search-dropdown-products__heading'); 10411 this.$dropdown = this.$form.find('.search'); 10412 this.$dropdownProducts = this.$form.find('.search-dropdown-products__list'); 10413 this.searchCache = {}; 10414 this.isOpening = false; 10415 this.autoCompleteTermLimit = 10; 10416 this.autoCompleteCharCountTrigger = autoCompleteCharCountTrigger; 10417 this.hideIfNoResults = hideIfNoResults; 10418 this.currentSearchTerm = document.getElementById(DomSelector.CurrentSearchTerm) ? document.getElementById(DomSelector.CurrentSearchTerm).value : ''; 10419 if (baseAddress.length > 0 && baseAddress.charAt(baseAddress.length - 1) !== "/") 10420 baseAddress += "/"; 10421 this.url = baseAddress + autoCompleteUrl; 10422 this.$form.submit(function (e) { return _this.searchSubmit(); }); 10423 var $input = $(this.input); 10424 $input.on('focus', function (e) { 10425 if (!_this.isOpening) { 10426 _this.openDropdown(); 10427 } 10428 }); 10429 // In case have focus but dropdown closed e.g. by hover over main navigation 10430 $input.on('click', function (e) { 10431 _this.isOpening = true; 10432 _this.openDropdown(); 10433 }); 10434 // Hide autosuggest if user clicks anywhere other than the input field or enriched auto complete dropdown 10435 $(document).click(function (e) { 10436 var $target = $(e.target); 10437 if (_this.input !== document.activeElement && 0 == $target.closest(DomSelector.Form).length) { 10438 _this.hideDropdown(100); 10439 } 10440 else { 10441 // If user has clicked on the cover 10442 if ($target.hasClass('search-dropdown-content-cover')) { 10443 _this.hideDropdown(100); 10444 } 10445 } 10446 }); 10447 // Hide autosuggest if user presses escape 10448 $(document).keyup(function (e) { 10449 if (Keys.Escape == e.keyCode) { 10450 _this.hideDropdown(100); 10451 } 10452 }); 10453 this.setLastSearch(); 10454 this.initClearSearchTextButton(); 10455 this.initSelectionEvents(); 10456 if (suggestionsEnabled) { 10457 this.initSuggestions(); 10458 } 10459 else { 10460 this.initNoSuggestions(); 10461 } 10462 this.manageEnrichedSuggestionsEnabledState(); 10463 } 10464 /** 10465 * Use this to unload this class and all associated DOM items 10466 */ 10467 EnrichedAutoComplete.prototype.destroy = function () { 10468 // Remove all events on input field 10469 $('#' + DomSelector.Input).off('click blur focus keyup keydown'); 10470 // Remove items added to the DOM 10471 this.$dropdown.empty(); 10472 this.$dropdownContainer.find($('.' + DomSelector.SearchDropdownCover)).remove(); 10473 // Remove CSS Classes 10474 this.$dropdownContainer.removeClass(DomSelector.StateIsEnriched); 10475 this.$form.removeClass(DomSelector.StateIsEnriched); 10476 }; 10477 /** 10478 * Initialise when suggestions are turned off 10479 */ 10480 EnrichedAutoComplete.prototype.initNoSuggestions = function () { 10481 var _this = this; 10482 var inputText = this.clean(this.input.value); 10483 $(this.input).keyup(function (e) { 10484 // No change so return 10485 if (_this.clean(_this.input.value) === _this.clean(inputText)) { 10486 return; 10487 } 10488 inputText = _this.clean(_this.input.value); 10489 // Ignore keys that can change the content but shouldn't close the dropdown
10490 if (_this.IgnoreKey(e.keyCode)) { 10491 return; 10492 } 10493 _this.clearDropdown(); 10494 if (!_this.canSearch(inputText) && !_this.isRecentMenu()) { 10495 _this.loadRecentSearches(); 10496 } 10497 _this.checkRecentView(); 10498 _this.refresh(); 10499 }); 10500 }; 10501 /** 10502 * Set up button to clear the search text. 10503 */ 10504 EnrichedAutoComplete.prototype.initClearSearchTextButton = function () { 10505 var _this = this; 10506 var btn = this.$form.find('.clear-search-text'); 10507 this.showOrHideClearSearchTextButton(); 10508 btn.click(function (e) { 10509 _this.input.value = ''; 10510 _this.showOrHideClearSearchTextButton(); 10511 _this.clearDropdown(); 10512 _this.loadRecentSearches(); 10513 setTimeout(function () { return _this.input.focus(); }, 500); 10514 }); 10515 }; 10516 EnrichedAutoComplete.prototype.showOrHideClearSearchTextButton = function () { 10517 var btn = this.$form.find('.clear-search-text'); 10518 var show = this.clean(this.input.value) !== ''; 10519 show ? btn.addClass("visible") : btn.removeClass("visible"); 10520 }; 10521 /** 10522 * Initialise the suggestion searches 10523 */ 10524 EnrichedAutoComplete.prototype.initSuggestions = function () { 10525 var _this = this; 10526 $(this.input).keyup(function (e) { 10527 var inputText = _this.clean(_this.input.value); 10528 // Ignore keys that can change the content but don't need to invoke a search 10529 if (_this.IgnoreKey(e.keyCode)) { 10530 return; 10531 } 10532 _this.checkRecentView(); 10533 if (!_this.canSearch(inputText)) { 10534 clearTimeout(_this.searchTimeoutId); 10535 _this.clearDropdown(); 10536 _this.loadRecentSearches(); 10537 _this.refresh(); 10538 return; 10539 } 10540 else { 10541 // If we've exceeded the minimum character count trigger number then continue 10542 _this.searchTerm(inputText, false); 10543 } 10544 }); 10545 this.checkRecentView(); 10546 }; 10547 EnrichedAutoComplete.prototype.checkRecentView = function () { 10548 var inputText = this.clean($(this.input).val()), canSearch = this.canSearch(inputText); 10549 if (!canSearch) { 10550 this.$dropdownContainer.addClass(DomSelector.StateShowRecent); 10551 this.$dropdownContainer.removeClass(DomSelector.StateIsLoading); 10552 // If no recent then hide the dropdown 10553 var kc = this.recentSearches.load(); 10554 if (0 === kc.length) { 10555 this.$dropdownContainer.addClass(DomSelector.StateNoRecent); 10556 } 10557 else { 10558 this.$dropdownContainer.removeClass(DomSelector.StateNoRecent); 10559 } 10560 } 10561 else { 10562 this.$dropdownContainer.removeClass(DomSelector.StateShowRecent); 10563 this.$dropdownContainer.removeClass(DomSelector.StateNoRecent); 10564 } 10565 }; 10566 EnrichedAutoComplete.prototype.canSearch = function (term) { 10567 // Decides if a search should be performed or not 10568 return term.length > this.autoCompleteCharCountTrigger; 10569 }; 10570 EnrichedAutoComplete.prototype.searchTerm = function (term, productsOnly, clearSelected) { 10571 var _this = this; 10572 if (clearSelected === void 0) { clearSelected = true; } 10573 this.searchInputText = term; 10574 this.showOrHideClearSearchTextButton(); 10575 // Input changed so clear any selection 10576 if (clearSelected) { 10577 this.clearSelected(); 10578 } 10579 this.resetCurrentSearchTitle(); 10580 if (this.isRecentMenu()) { 10581 this.clearDropdown(); 10582 } 10583 this.checkRecentView(); 10584 // Restart timer 10585 clearTimeout(this.searchTimeoutId); 10586 // Cancel original ajax request
10587 if (this.searchAjaxRequest && this.searchAjaxRequest.readyState != 4) { 10588 this.searchAjaxRequest.abort(); 10589 } 10590 if (this.searchCache[this.searchInputText]) { 10591 this.clearDropdown(productsOnly); 10592 this.brProcessResponse(this.searchCache[this.searchInputText], productsOnly); 10593 this.refresh(); 10594 } 10595 else { 10596 if (!this.canSearch(this.searchInputText)) { 10597 this.clearDropdown(); 10598 this.loadRecentSearches(); 10599 this.refresh(); 10600 } 10601 else { 10602 this.searchTimeoutId = setTimeout(function () { 10603 _this.checkRecentView(); 10604 if (!_this.canSearch(_this.searchInputText)) { 10605 _this.clearDropdown(); 10606 _this.loadRecentSearches(); 10607 _this.refresh(); 10608 } 10609 else { 10610 // If search has already been performed for this term then use cached results 10611 if (Next.Settings.Channel.BloomReachSearchOverrideEnabled || (Next.Settings.Channel.BloomReachSearchEnabled && 10612 typeof _this.recentSearches.monetateSearchCookie != 'undefined' && 10613 _this.recentSearches.monetateSearchCookie.indexOf(Next.Settings.Cookie.MonetateCookieBloomReachValue) === 0)) { 10614 _this.searchAjaxRequest = _this.brsearch(_this.searchInputText) 10615 .done(function (result) { 10616 _this.clearDropdown(productsOnly); 10617 // Save search to cache to prevent duplicate ajax queries 10618 _this.searchCache[_this.searchInputText] = result; 10619 _this.brProcessResponse(result, productsOnly); 10620 _this.refresh(); 10621 }) 10622 .fail(function (xhr, status, error) { 10623 if ('abort' !== status) { 10624 // If ajax request fails then it could be due to adblocker 10625 _this.clearDropdown(); 10626 _this.refresh(); 10627 } 10628 }); 10629 } 10630 } 10631 }, 100); 10632 } 10633 } 10634 }; 10635 EnrichedAutoComplete.prototype.initSelectionEvents = function () { 10636 var _this = this; 10637 $(this.input).keydown(function (e) { 10638 switch (e.keyCode) { 10639 case Keys.UpArrow: 10640 case Keys.DownArrow: 10641 e.preventDefault(); 10642 _this.keyUpDown(e.keyCode); 10643 break; 10644 case Keys.Enter: 10645 return _this.selectItem(); 10646 } 10647 }); 10648 }; 10649 /** 10650 * Which keys to ignore when search input has focus. 10651 */ 10652 EnrichedAutoComplete.prototype.IgnoreKey = function (keyCode) { 10653 if (keyCode == Keys.UpArrow) 10654 return true; 10655 if (keyCode == Keys.DownArrow) 10656 return true; 10657 if (keyCode == Keys.Escape) 10658 return true; 10659 return false; 10660 }; 10661 /** 10662 * Invoke the Ajax search for suggestions 10663 */ 10664 EnrichedAutoComplete.prototype.search = function (term) { 10665 var ajaxSettings = { 10666 url: this.url, 10667 dataType: "json", 10668 cache: true, 10669 data: { 10670 word: term 10671 } 10672 }; 10673 return $.ajax(ajaxSettings); 10674 }; 10675 /** 10676 * Parse the SLI response and return the suggestions array. 10677 */ 10678 EnrichedAutoComplete.prototype.processResponse = function (response, productsOnly) { 10679 var _this = this; 10680 var ss = response.results.search_suggestions; 10681 if (!ss) 10682 return null; 10683 if (ss.department_suggestions) { 10684 $.each(ss.department_suggestions, function (index, item) { return _this.addDepartment(item); }); 10685 } 10686 if (ss.suggestions) { 10687 // Add text suggestions 10688 $.each(ss.suggestions, function (index, text) { return _this.addSuggestion(text); }); 10689 } 10690 }; 10691 //bloomreach 10692 /** 10693 * Invoke the Ajax search for suggestions 10694 */ 10695 EnrichedAutoComplete.prototype.brsearch = function (term) { 10696 var bruid = this.recentSearches.brUIDCookie; 10697 var requestID = new Date().getTime(); 10698 var viewId = Next.Settings.Channel.BloomReachAutoSuggestViewIdEnabled ? (Next.Settings.Channel.BloomReachCountryCode ? "&view_id=" + Next.Settings.Channel.BloomReachCountryCode : "") : ""; // View_id is a parameter that needs to be present for Intl but not for UK 10699 var url = Next.Settings.Channel.BloomReachAutosuggestDomain + 10700 "?account_id=" + Next.Settings.Channel.BloomReachAccountID + 10701 "&auth_key=" + 10702 "&domain_key=" + Next.Settings.Channel.BloomReachDomainKey + 10703 viewId + 10704 "&request_id=" + requestID + 10705 "&_br_uid_2=" + bruid + 10706 "&request_type=suggest" + 10707 "&q=" + term; 10708 var ajaxSettings = { 10709 url: url, 10710 dataType: "json" 10711 }; 10712 return $.ajax(ajaxSettings); 10713 }; 10714 /** 10715 * Parse the BR response and return the suggestions array. 10716 */ 10717 EnrichedAutoComplete.prototype.brProcessResponse = function (response, productsOnly) { 10718 var _this = this; 10719 var ss = response.response; 10720 var resultsCount = 0; 10721 if (!ss) 10722 return null; 10723 if (ss.suggestions) { 10724 resultsCount = ss.suggestions.length; 10725 if (resultsCount > 0) { 10726 // Add suggestions to list 10727 if (!productsOnly) { 10728 ss.suggestions = ss.suggestions.slice(0, this.autoCompleteTermLimit); 10729 $.each(ss.suggestions, function (index, s) { return _this.addSuggestion(s.q); }); 10730 } 10731 if (ss.products) { 10732 // Add product suggestions 10733 $.each(ss.products, function (index, product) { return _this.addProductSuggestion(product); }); 10734 } 10735 this.addProductsSeeAllLink(ss.q); 10736 } 10737 } 10738 this.addCurrentSearchTitle(this.searchInputText, resultsCount); 10739 }; 10740 /** 10741 * Simulate a click on the currently selected item 10742 */ 10743 EnrichedAutoComplete.prototype.selectItem = function () { 10744 var $selected = this.$dropdown.find('li.selected'); 10745 if (!$selected.length) 10746 return true; 10747 this.input.value = $selected.text(); 10748 return false; 10749 }; 10750 EnrichedAutoComplete.prototype.keyUpDown = function (keyCode) { 10751 var $list = this.$dropdown.find('li.' + DomSelector.SearchItem); 10752 var listLength = $list.length; 10753 if (listLength == 0) 10754 return; 10755 if (listLength == 1) { 10756 $list.first().addClass(DomSelector.StateSelected); 10757 if (!this.isRecentMenu()) { 10758 this.searchTerm($li.first().text(), true, false); 10759 } 10760 return; 10761 } 10762 var $selected = $list.filter('.selected'); 10763 var index = $list.index($selected); 10764 $selected.removeClass(DomSelector.StateSelected); 10765 if (keyCode == Keys.UpArrow) { 10766 if (index-- < 0) 10767 index = listLength - 1; 10768 } 10769 if (keyCode == Keys.DownArrow) { 10770 if (++index == listLength) 10771 index = 0; 10772 } 10773 var $li = $list.eq(index); 10774 $li.addClass(DomSelector.StateSelected); 10775 if (!this.isRecentMenu()) { 10776 this.searchTerm($li.text(), true, false); 10777 } 10778 }; 10779 /** 10780 * Clear the selected list item 10781 */ 10782 EnrichedAutoComplete.prototype.clearSelected = function () { 10783 var $item = this.$dropdown.find('li.selected'); 10784 $item.removeClass(DomSelector.StateSelected); 10785 }; 10786 /** 10787 * Add the heading in the list for the recently searched 10788 */ 10789 EnrichedAutoComplete.prototype.addRecentHeading = function () { 10790 var _this = this; 10791 var $li = $("<li>") 10792 .addClass(DomSelector.RecentHeading)
10793 .text(nxt.jstranslations.autocomplete.recentsearches || "Your Recent Searches...") 10794 .append("<a>") 10795 .find("a") 10796 .addClass(DomSelector.ClearRecent) 10797 .attr("href", "#") 10798 .click(function () { return _this.deleteRecent(); }) 10799 .text(nxt.jstranslations.common.clear || "Clear") 10800 .end(); 10801 this.$dropdown.append($li); 10802 }; 10803 /** 10804 * Add the divider in the list 10805 */ 10806 EnrichedAutoComplete.prototype.addDivider = function () { 10807 var $li = $("<li>") 10808 .addClass(DomSelector.Divider); 10809 this.$dropdown.append($li); 10810 }; 10811 /** 10812 * Add the title of the search section in the list 10813 */ 10814 EnrichedAutoComplete.prototype.addSearchesTitle = function () { 10815 var $li = $("<li>") 10816 .addClass(DomSelector.Title) 10817 .text(nxt.jstranslations.autocomplete.populasearches || "Popular Searches"); 10818 this.$dropdown.append($li); 10819 }; 10820 /** 10821 * Add the title of the search in the products list header 10822 */ 10823 EnrichedAutoComplete.prototype.addCurrentSearchTitle = function (query, resultsCount) { 10824 this.$dropdownContainer.removeClass(DomSelector.StateIsLoading); 10825 if (resultsCount > 0) { 10826 this.$currentSearchHeading.html(nxt.stringFormat(nxt.jstranslations.autocomplete.topResultsFor || "Top results for '{0}'", '<strong>' + query + '</strong>')); 10827 } 10828 else { 10829 this.$currentSearchHeading.html(nxt.stringFormat(nxt.jstranslations.autocomplete.noResultsFor || "No results found for '{0}'", '<strong>' + query + '</strong>')); 10830 } 10831 }; 10832 /** 10833 * Add the title of the search in the products list header 10834 */ 10835 EnrichedAutoComplete.prototype.resetCurrentSearchTitle = function () { 10836 this.$dropdownContainer.addClass(DomSelector.StateIsLoading); 10837 this.$currentSearchHeading.html(nxt.jstranslations.autocomplete.loading || "Loading.."); 10838 }; 10839 /** 10840 * Get the recently searched cookies and add them to the dropdown list 10841 */ 10842 EnrichedAutoComplete.prototype.loadRecentSearches = function () { 10843 var _this = this; 10844 var kc = this.recentSearches.load(); 10845 if (kc.length > 0) { 10846 this.addRecentHeading(); 10847 $.each(kc, function (i, s) { return _this.addRecent(s); }); 10848 } 10849 }; 10850 /** 10851 * Refresh the state of the search drop down 10852 */ 10853 EnrichedAutoComplete.prototype.refresh = function () { 10854 var _this = this; 10855 this.manageEnrichedSuggestionsEnabledState(); 10856 if (this.haveItemsInList()) { 10857 this.$dropdownContainer.show(); 10858 this.$dropdownContainer.addClass(DomSelector.Open); 10859 $(this.input).addClass(DomSelector.Open); 10860 this.$dropdownContainer.removeClass(DomSelector.StateNoResults); 10861 } 10862 else { 10863 this.$dropdownContainer.addClass(DomSelector.StateNoResults); 10864 } 10865 // To prevent double openings 10866 setTimeout(function () { return _this.isOpening = false; }, 10); 10867 this.checkRecentView(); 10868 }; 10869 EnrichedAutoComplete.prototype.manageEnrichedSuggestionsEnabledState = function () { 10870 this.$dropdownContainer.addClass(DomSelector.StateIsEnriched); 10871 this.$form.addClass(DomSelector.StateIsEnriched); 10872 if (this.hideIfNoResults) { 10873 this.$dropdownContainer.addClass(DomSelector.StateHideIfNoResults); 10874 } 10875 if (0 === this.$dropdownContainer.find('.' + DomSelector.SearchDropdownCover).length) { 10876 this.$dropdownContainer.append($('<div class="' + DomSelector.SearchDropdownCover + '"></div>')); 10877 } 10878 }; 10879 EnrichedAutoComplete.prototype.openDropdown = function () { 10880 this.refresh();
10881 }; 10882 EnrichedAutoComplete.prototype.hideDropdown = function (duration) { 10883 duration ? this.$dropdownContainer.hide(duration, 'swing') : this.$dropdownContainer.hide(); 10884 this.$dropdownContainer.removeClass(DomSelector.Open); 10885 $(this.input).removeClass(DomSelector.Open); 10886 }; 10887 /** 10888 * Remove leading, trailing and double spaces. 10889 */ 10890 EnrichedAutoComplete.prototype.clean = function (term) { 10891 term = term.replace(/^[\s]*/, '').replace(/[\s]*$/, '').replace(/ +/, ' ').replace(/<[^>]*>?/gm, ''); 10892 return term; 10893 }; 10894 /** 10895 * The form is being submitted 10896 */ 10897 EnrichedAutoComplete.prototype.searchSubmit = function () { 10898 var inputText = this.clean(this.input.value); 10899 if (!inputText || 0 === inputText.length) { 10900 return; 10901 } 10902 // If filter is set recent searches have already been added. 10903 if (this.filter.value) { 10904 return; 10905 } 10906 var suggestion = { 10907 term: inputText, 10908 af: '', 10909 name: '' 10910 }; 10911 this.recentSearches.add(suggestion); 10912 }; 10913 /** 10914 * Set the search input control to the last searched term if on a search page. 10915 */ 10916 EnrichedAutoComplete.prototype.setLastSearch = function () { 10917 var lastSearch = this.recentSearches.getLastSearch(); 10918 if (!lastSearch) { 10919 this.loadRecentSearches(); 10920 return; 10921 } 10922 if (this.onResultsPage()) { 10923 this.input.value = lastSearch; 10924 return; 10925 } 10926 this.recentSearches.clearLastSearch(); 10927 this.loadRecentSearches(); 10928 }; 10929 /** 10930 * return true if this is the search results page. 10931 */ 10932 EnrichedAutoComplete.prototype.onResultsPage = function () { 10933 // To do when we can determine if we are a search results page. 10934 return false; 10935 }; 10936 /** 10937 * Remove all items from the list 10938 */ 10939 EnrichedAutoComplete.prototype.clearDropdown = function (productsOnly) { 10940 if (productsOnly === void 0) { productsOnly = false; } 10941 if (!productsOnly) { 10942 this.$dropdown.empty(); 10943 } 10944 this.$dropdownProducts.empty(); 10945 }; 10946 EnrichedAutoComplete.prototype.deleteRecent = function () { 10947 this.recentSearches.clear(); 10948 this.clearDropdown(); 10949 this.hideDropdown(); 10950 return false; 10951 }; 10952 EnrichedAutoComplete.prototype.dropdownItemSelected = function (item) { 10953 // If text in input box is the same as current search then simply 10954 // hide the overlay (https://next-tfs.next-uk.next.loc/Directory/NextOnline/_workitems/edit/398050) 10955 if (item.toLowerCase() === this.currentSearchTerm.toLowerCase()) { 10956 this.hideDropdown(100); 10957 return; 10958 } 10959 this.input.value = item; 10960 this.$form.submit(); 10961 }; 10962 EnrichedAutoComplete.prototype.dropdownDepartmentItemSelected = function (item) { 10963 this.input.value = item.term; 10964 this.filter.value = item.af; 10965 this.recentSearches.add(item); 10966 this.$form.submit(); 10967 }; 10968 /** 10969 * Add a department suggestion into the dropdown list. 10970 */ 10971 EnrichedAutoComplete.prototype.addDepartment = function (item) { 10972 var _this = this; 10973 var display = this.surroundTextThatMatchesInput(item.term); 10974 display += "<span class='dept-name'>" + nxt.stringFormat(nxt.jstranslations.autocomplete.autocompleteIn || "in", item.name) + "</span>"; 10975 var $li = $("<li/>", { "html": display, "class": DomSelector.SearchItem }) 10976 .click(function (e) { 10977 _this.dropdownDepartmentItemSelected(item); 10978 }); 10979 this.$dropdown.append($li); 10980 }; 10981 /** 10982 * Add a search suggestion into the dropdown list. 10983 */ 10984 EnrichedAutoComplete.prototype.addSuggestion = function (text) { 10985 var _this = this; 10986 var display = this.surroundTextThatMatchesInput(text); 10987 var $li = $("<li/>", { "html": display, "class": DomSelector.SearchItem }) 10988 .click(function (e) { 10989 _this.eventLogger.log(_this.input.value, text, 'List'); 10990 _this.dropdownItemSelected(text); 10991 }); 10992 $li.hover(function (e) { 10993 if (!_this.isRecentMenu()) { 10994 _this.searchTerm(text, true); 10995 } 10996 }); 10997 this.$dropdown.append($li); 10998 }; 10999 /** 11000 * Add a search suggestion into the dropdown list. 11001 */ 11002 EnrichedAutoComplete.prototype.addProductSuggestion = function (product) { 11003 var _this = this; 11004 var productPrice = product.sale_price; 11005 var formattedProductPrice = ''; 11006 // Rules 11007 // 1) If number is 0.01 conver to 0 (free) - this is due to a limitation of BloomReach - it doesn't allow zero numbers 11008 // 2) If number has decimal point ensure it's always 2 fixed points 11009 // 3) If number is a round number then ensure no decimal points are used 11010 // 4) If number is greater than 999 ensure it has a comma 11011 if (0.01 == productPrice) { 11012 formattedProductPrice = '0'; 11013 } 11014 else { 11015 if (Math.ceil(parseFloat(productPrice.toString())).toFixed(2) > Math.floor(parseFloat(productPrice.toString())).toFixed(2)) { 11016 formattedProductPrice = this.numberWithCommas(parseFloat(productPrice.toString()).toFixed(2)); 11017 } 11018 else { 11019 formattedProductPrice = this.numberWithCommas(Math.floor(parseFloat(productPrice.toString())).toFixed(0)); 11020 } 11021 } 11022 var $productTemplate = "\t\t<a class=\"search-dropdown-products__product\" href=\"" + product.url + "\"> \t\t <span class=\"search-dropdown-products__product-inner\"> \t\t <span class=\"search-dropdown-products__product-img\"> \t\t <img alt=\"" + product.title + "\" src=\"" + product.thumb_image.replace('AltItemZoom', 'search/224x336') + "\" /> \t\t </span> \t\t <p class=\"search-dropdown-products__product-title\">" + product.title + "</p> \t\t <span class=\"search-dropdown-products__product-price\">" + Next.Settings.Channel.CurrencySymbol + formattedProductPrice + "</span> \t\t </span> \t\t</a> \t "; 11023 var $product = $($productTemplate).click(function (e) { 11024 var currentSearch = _this.$currentSearchHeading.find('strong').text(); 11025 _this.eventLogger.log(_this.input.value, currentSearch, 'Image'); 11026 }); 11027 this.$dropdownProducts.append($product); 11028 }; 11029 EnrichedAutoComplete.prototype.numberWithCommas = function (price) { 11030 return price.replace(/\B(?=(\d{3})+(?!\d))/g, ","); 11031 }; 11032 /** 11033 * Add a link to "See all results" beneath the product lists 11034 */ 11035 EnrichedAutoComplete.prototype.addProductsSeeAllLink = function (query) { 11036 var _this = this; 11037 var $link = $("<a/>", { "html": (nxt.jstranslations.autocomplete.seeAllResultsLink || "See All Results"), "class": "search-dropdown-products__see-all", "href": "#" }) 11038 .click(function (e) { 11039 e.preventDefault(); 11040 _this.dropdownItemSelected(query); 11041 }); 11042 this.$dropdownProducts.append($link); 11043 }; 11044 /** 11045 * Add a recent search item into the dropdown list 11046 */ 11047 EnrichedAutoComplete.prototype.addRecent = function (item) { 11048 if (item.name) 11049 this.addDepartment(item); 11050 else 11051 this.addSuggestion(item.term); 11052 }; 11053 /** 11054 * Surround the portion of the text that matches the input with a span 11055 */ 11056 EnrichedAutoComplete.prototype.surroundTextThatMatchesInput = function (suggestion) { 11057 var input = this.clean(this.input.value); 11058 var term = input.replace(/ /g, "|"); 11059 var re = new RegExp(term, 'ig'); 11060 var display = suggestion.replace(re, "<span class='typed-text'>$&</span>");
11061 return display; 11062 }; 11063 EnrichedAutoComplete.prototype.isRecentMenu = function () { 11064 return this.$dropdown.find('.' + DomSelector.RecentHeading).length > 0; 11065 }; 11066 EnrichedAutoComplete.prototype.haveItemsInList = function () { 11067 return this.$dropdown.find('li.' + DomSelector.SearchItem).length > 0; 11068 }; 11069 return EnrichedAutoComplete; 11070 }()); 11071 var EventLogger = /** @class */ (function () { 11072 function EventLogger() { 11073 } 11074 EventLogger.prototype.log = function (eTerm, eSelected, eType) { 11075 // Safety checks 11076 if ('undefined' !== typeof dataLayer && dataLayer.length > 0) { 11077 var logDetail = { 11078 'autoSearchTerm': eTerm, 11079 'autoSearchSelected': eSelected, 11080 'autoSearchType': eType // 'List' or 'Image' 11081 }; 11082 dataLayer.push({ 11083 'event': 'enriched-auto-complete-search', 11084 'detail': logDetail 11085 }); 11086 } 11087 else { 11088 return; 11089 } 11090 }; 11091 return EventLogger; 11092 }()); 11093 var KeyCookies = /** @class */ (function () { 11094 function KeyCookies(maxSave) { 11095 this.recentSearches = []; 11096 this.maxSave = maxSave; 11097 this.initCookies(); 11098 } 11099 KeyCookies.prototype.initCookies = function () { 11100 if (!this.maxSave) 11101 return; 11102 var cookie = this.getCookie('recentSearches'); 11103 if (cookie) 11104 this.recentSearches = JSON.parse(cookie); 11105 var searchCookie = this.getCookie(Next.Settings.Cookie.MonetateCookieName); 11106 if (searchCookie) 11107 this.monetateSearchCookie = searchCookie; 11108 var bruidCookie = this.getCookie('_br_uid_2'); 11109 if (bruidCookie) 11110 this.brUIDCookie = bruidCookie; 11111 var backToNextBagCookie = Next.Settings.Cookie.BackToNextCookieName; 11112 if (backToNextBagCookie) 11113 this.backToNextBagCookieName = backToNextBagCookie; 11114 var backToNextUrlCookieName = Next.Settings.Cookie.BackToNextURLCookieName; 11115 if (backToNextUrlCookieName) 11116 this.backToNextUrlCookieName = Next.Settings.Cookie.BackToNextURLCookieName; 11117 }; 11118 KeyCookies.prototype.getCookie = function (value) { 11119 var cookie = ''; 11120 var c = document.cookie.split('; '); 11121 $.each(c, function (i, s) { 11122 var kv = s.split('='); 11123 if (kv[0] == value) 11124 cookie = kv[1]; 11125 }); 11126 return decodeURIComponent(cookie); 11127 }; 11128 KeyCookies.prototype.load = function () { 11129 return this.recentSearches; 11130 }; 11131 KeyCookies.prototype.any = function () { 11132 return this.recentSearches.length > 0; 11133 }; 11134 /** 11135 * Add a new search term to the recent search list or if existing term move to top of list 11136 */ 11137 KeyCookies.prototype.add = function (suggestion) { 11138 this.setLastSearch(suggestion.term); 11139 var name = suggestion.name; // department 11140 var af = suggestion.af; 11141 var term = suggestion.term.toLowerCase(); 11142 var list = [suggestion]; 11143 $.each(this.recentSearches, function (index, item) { 11144 if (item.term.toLowerCase() != term || item.name != name) 11145 list.push(item); 11146 }); 11147 this.recentSearches = list.slice(0, this.maxSave); 11148 var cookie = JSON.stringify(this.recentSearches); 11149 var dt = new Date(); 11150 dt.setMonth(dt.getMonth() + 1); 11151 var expires = "; expires=" + dt.toUTCString(); 11152 document.cookie = "recentSearches=" + encodeURIComponent(cookie) + "; path=/" + expires; 11153 }; 11154 /** 11155 * Cache the last searched term. 11156 */ 11157 KeyCookies.prototype.setLastSearch = function (term) { 11158 document.cookie = "lastSearch=" + encodeURIComponent(term) + "; path=/"; 11159 }; 11160 KeyCookies.prototype.getLastSearch = function () { 11161 return this.getCookie('lastSearch'); 11162 }; 11163 KeyCookies.prototype.clearLastSearch = function () { 11164 document.cookie = "lastSearch=; path=/; expires = Thu, 01 Jan 1970 00:00:00 GMT"; 11165 }; 11166 KeyCookies.prototype.clear = function () { 11167 document.cookie = "recentSearches=; path=/; expires = Thu, 01 Jan 1970 00:00:00 GMT"; 11168 this.recentSearches.length = 0; 11169 }; 11170 return KeyCookies; 11171 }()); 11172 var Manager = /** @class */ (function () { 11173 function Manager() { 11174 var enabled = false, enrichedSuggestionsEnabled = false, autoCompleteCharCountTrigger = 0, autoSearchRecentCount = 12, hideIfNoResults = false, autoCompleteUrl = 'v1/gb/en-gb/autocomplete'; 11175 try { 11176 var searchFeature = Next.FeatureManager.FeatureSettings.Search; 11177 var baseAddress = searchFeature.BaseAddress; 11178 var suggestionsEnabled = Next.Settings.Channel.SearchAutoComplete; 11179 var recentSearchesSaved = searchFeature.RecentSearchesSaved; 11180 var autoCompleteOverrideSwitch = Next.Settings.Channel.AutoCompleteDisabledOverride; 11181 enabled = suggestionsEnabled || recentSearchesSaved > 0;
11182 if (autoCompleteOverrideSwitch && enabled) 11183 enabled = false; 11184 enrichedSuggestionsEnabled = Next.Settings.Channel.BloomReachEnrichedAutoSearchEnabled || enrichedSuggestionsEnabled; 11185 autoCompleteCharCountTrigger = Next.Settings.Channel.BloomReachEnrichedAutoSearchCharCountTrigger || autoCompleteCharCountTrigger; 11186 autoSearchRecentCount = Next.Settings.Channel.BloomReachEnrichedAutoSearchRecentCount || autoSearchRecentCount; 11187 autoCompleteUrl = Next.Settings.Channel.BloomReachAutoCompleteUrl || autoCompleteUrl; 11188 hideIfNoResults = Next.Settings.Channel.BloomReachEnrichedAutoSearchHideIfNoResults || hideIfNoResults; 11189 recentSearchesSaved = recentSearchesSaved; 11190 } 11191 catch (e) { 11192 ConsoleLog("'Feature Manager' search features not configured. "); 11193 } 11194 if (enabled && Manager.isSearchOnPage()) { 11195 if (enrichedSuggestionsEnabled) { 11196 this.enrichedAutoComplete = new EnrichedAutoComplete(baseAddress, autoSearchRecentCount, suggestionsEnabled, autoCompleteCharCountTrigger, autoCompleteUrl, hideIfNoResults); 11197 } 11198 else { 11199 this.autoComplete = new AutoComplete(baseAddress, recentSearchesSaved, suggestionsEnabled, autoCompleteUrl); 11200 } 11201 } 11202 this.setupEnhanced(); 11203 } 11204 Manager.isSearchOnPage = function () { 11205 return $(DomSelector.Form).length !== 0; 11206 }; 11207 Manager.prototype.setupEnhanced = function () { 11208 var _this = this; 11209 // - In order to trigger 11210 // $(document).trigger('autocomplete.enableEnhanced'); 11211 $(document).on('autocomplete.enableEnhanced', function (e) { 11212 if (!Next.Settings.Channel.BloomReachEnrichedAutoSearchEnabled) { 11213 if (_this.autoComplete) { 11214 _this.autoComplete.destroy(); 11215 delete _this.autoComplete; 11216 Next.Settings.Channel.BloomReachEnrichedAutoSearchCharCountTrigger = 2; 11217 Next.Settings.Channel.BloomReachEnrichedAutoSearchRecentCount = 12; 11218 Next.Settings.Channel.BloomReachEnrichedAutoSearchEnabled = true; 11219 Next.SearchManager = new Search.Manager(); 11220 } 11221 } 11222 }); 11223 // - In order to trigger 11224 // $(document).trigger('autocomplete.disableEnhanced'); 11225 $(document).on('autocomplete.disableEnhanced', function (e) { 11226 if (Next.Settings.Channel.BloomReachEnrichedAutoSearchEnabled) { 11227 if (_this.enrichedAutoComplete) { 11228 _this.enrichedAutoComplete.destroy(); 11229 delete _this.enrichedAutoComplete; 11230 Next.Settings.Channel.BloomReachEnrichedAutoSearchEnabled = false; 11231 Next.SearchManager = new Search.Manager(); 11232 } 11233 } 11234 }); 11235 }; 11236 return Manager; 11237 }()); 11238 Search.Manager = Manager; 11239})(Search || (Search = {})); 11240$(document).on("featureSettingsReady", function () { 11241 Next.SearchManager = new Search.Manager(); 11242}); 11243//# sourceMappingURL=autocomplete.js.map; 11244$(document).on("featureSettingsReady", function () { 11245 SetRecentlyViewedDisplay(); 11246 });; 11247;(function(factory) { 11248 if (typeof define === 'function' && define.amd) { 11249 define(['jquery'], factory); 11250 } 11251 else if (typeof exports === 'object') { 11252 module.exports = factory(require("jquery")); 11253 } 11254 else { 11255 //factory(jQuery); 11256 } 11257} 11258(function($) { 11259 "use strict"; 11260 11261 var pluginName = "tinyscrollbar" 11262 , defaults = { 11263 axis : 'y'//axis 11264 , wheel : true 11265 , wheelSpeed : 20//wheel 11266 , wheelLock : true//lockscroll 11267 , touchLock : true 11268 , trackSize : false//size 11269 , thumbSize : false//sizethumb 11270 , thumbSizeMin : 20 11271 } 11272 ; 11273 11274 function Plugin($container, options) { 11275 11276 11277 /** 11278 * The options of the carousel extend with the defaults. 11279 * 11280 * @property options 11281 * @type Object 11282 */ 11283 this.options = $.extend({}, defaults, options); 11284 11285 /** 11286 * @property _defaults 11287 * @type Object 11288 * @private 11289 * @default defaults 11290 */ 11291 this._defaults = defaults; 11292 11293 /** 11294 * @property _name
11295 * @type String 11296 * @private 11297 * @final 11298 * @default 'tinyscrollbar' 11299 */ 11300 this._name = pluginName; 11301 11302 var self = this 11303 , $viewport = $container.find(".viewport") 11304 , $overview = $container.find(".overview") 11305 , $scrollbar = $container.find(".scrollbar") 11306 , $track = $scrollbar.find(".track") 11307 , $thumb = $scrollbar.find(".thumb") 11308 11309 , hasTouchEvents = ("ontouchstart" in document.documentElement) 11310 , wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel" 11311 document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel" 11312 "DOMMouseScroll" // let's assume that remaining browsers are older Firefox 11313 , isHorizontal = this.options.axis === 'x' 11314 , sizeLabel = isHorizontal ? "width" : "height" 11315 , posiLabel = isHorizontal ? "left" : "top" 11316 11317 , mousePosition = 0 11318 ; 11319 11320 /** 11321 * The position of the content relative to the viewport. 11322 * 11323 * @property contentPosition 11324 * @type Number 11325 */ 11326 this.contentPosition = 0; 11327 11328 /** 11329 * The height or width of the viewport. 11330 * 11331 * @property viewportSize 11332 * @type Number 11333 */ 11334 this.viewportSize = 0; 11335 11336 /** 11337 * The height or width of the content. 11338 * 11339 * @property contentSize 11340 * @type Number 11341 */ 11342 this.contentSize = 0; 11343 11344 /** 11345 * The ratio of the content size relative to the viewport size. 11346 * 11347 * @property contentRatio 11348 * @type Number 11349 */ 11350 this.contentRatio = 0; 11351 11352 /** 11353 * The height or width of the content. 11354 * 11355 * @property trackSize 11356 * @type Number 11357 */ 11358 this.trackSize = 0; 11359 11360 /** 11361 * The size of the track relative to the size of the content. 11362 * 11363 * @property trackRatio 11364 * @type Number 11365 */ 11366 this.trackRatio = 0; 11367 11368 /** 11369 * The height or width of the thumb. 11370 * 11371 * @property thumbSize 11372 * @type Number 11373 */ 11374 this.thumbSize = 0; 11375 11376 /** 11377 * The position of the thumb relative to the track. 11378 * 11379 * @property thumbPosition 11380 * @type Number 11381 */ 11382 this.thumbPosition = 0; 11383 11384 /** 11385 * Will be true if there is content to scroll. 11386 * 11387 * @property hasContentToSroll 11388 * @type Boolean 11389 */ 11390 this.hasContentToSroll = false; 11391 11392 /** 11393 * @method _initialize 11394 * @private 11395 */ 11396 function _initialize() { 11397 self.update(); 11398 _setEvents(); 11399 11400 return self; 11401 } 11402 11403 /** 11404 * You can use the update method to adjust the scrollbar to new content or to move the scrollbar to a certain point. 11405 * 11406 * @method update 11407 * @chainable 11408 * @param {Number|String} [scrollTo] Number in pixels or the values "relative" or "bottom". If you dont specify a parameter it will default to top 11409 */ 11410 this.update = function(scrollTo) { 11411 var sizeLabelCap = sizeLabel.charAt(0).toUpperCase() + sizeLabel.slice(1).toLowerCase(); 11412 this.viewportSize = $viewport[0]['offset'+ sizeLabelCap]; 11413 this.contentSize = $overview[0]['scroll'+ sizeLabelCap]; 11414 this.contentRatio = this.viewportSize / this.contentSize; 11415 this.trackSize = this.options.trackSize || this.viewportSize; 11416 this.thumbSize = Math.min(this.trackSize, Math.max(this.options.thumbSizeMin, (this.options.thumbSize || (this.trackSize * this.contentRatio)))); 11417 this.trackRatio = (this.contentSize - this.viewportSize) / (this.trackSize - this.thumbSize); 11418 this.hasContentToSroll = this.contentRatio < 1; 11419 11420 $scrollbar.toggleClass("disable", !this.hasContentToSroll); 11421 11422 switch (scrollTo) { 11423 case "bottom": 11424 this.contentPosition = Math.max(this.contentSize - this.viewportSize, 0); 11425 break; 11426 11427 case "relative": 11428 this.contentPosition = Math.min(Math.max(this.contentSize - this.viewportSize, 0), Math.max(0, this.contentPosition)); 11429 break; 11430 11431 default: 11432 this.contentPosition = parseInt(scrollTo, 10) || 0; 11433 } 11434 11435 this.thumbPosition = this.contentPosition / this.trackRatio; 11436 11437 _setCss(); 11438 11439 return self; 11440 }; 11441 11442 /** 11443 * @method _setCss 11444 * @private 11445 */ 11446 function _setCss() { 11447 $thumb.css(posiLabel, self.thumbPosition); 11448 $overview.css(posiLabel, -self.contentPosition); 11449 $scrollbar.css(sizeLabel, self.trackSize); 11450 $track.css(sizeLabel, self.trackSize); 11451 $thumb.css(sizeLabel, self.thumbSize); 11452 } 11453 11454 /** 11455 * @method _setEvents 11456 * @private 11457 */ 11458 function _setEvents() { 11459 if(hasTouchEvents) { 11460 $viewport[0].ontouchstart = function(event) { 11461 if(1 === event.touches.length) { 11462 event.stopPropagation(); 11463 11464 _start(event.touches[0]); 11465 } 11466 }; 11467 } 11468 else { 11469 $thumb.bind("mousedown", function(event){ 11470 event.stopPropagation(); 11471 _start(event); 11472 }); 11473 $track.bind("mousedown", function(event){ 11474 _start(event, true); 11475 }); 11476 } 11477 11478 $(window).resize(function() { 11479 self.update("relative"); 11480 }); 11481 11482 if(self.options.wheel && window.addEventListener) { 11483 $container[0].addEventListener(wheelEvent, _wheel, false); 11484 } 11485 else if(self.options.wheel) { 11486 $container[0].onmousewheel = _wheel; 11487 } 11488 } 11489 11490 /** 11491 * @method _isAtBegin 11492 * @private 11493 */ 11494 function _isAtBegin() { 11495 return self.contentPosition > 0;
11496 } 11497 11498 /** 11499 * @method _isAtEnd 11500 * @private 11501 */ 11502 function _isAtEnd() { 11503 return self.contentPosition <= (self.contentSize - self.viewportSize) - 5; 11504 } 11505 11506 /** 11507 * @method _start 11508 * @private 11509 */ 11510 function _start(event, gotoMouse) { 11511 if(self.hasContentToSroll) { 11512 $("body").addClass("noSelect"); 11513 11514 mousePosition = gotoMouse ? $thumb.offset()[posiLabel] : (isHorizontal ? event.pageX : event.pageY); 11515 11516 if(hasTouchEvents) { 11517 document.ontouchmove = function(event) { 11518 if(self.options.touchLock || _isAtBegin() && _isAtEnd()) { 11519 event.preventDefault(); 11520 } 11521 _drag(event.touches[0]); 11522 }; 11523 document.ontouchend = _end; 11524 } 11525 else { 11526 $(document).bind("mousemove", _drag); 11527 $(document).bind("mouseup", _end); 11528 $thumb.bind("mouseup", _end); 11529 $track.bind("mouseup", _end); 11530 } 11531 11532 _drag(event); 11533 } 11534 } 11535 11536 /** 11537 * @method _wheel 11538 * @private 11539 */ 11540 function _wheel(event) { 11541 if(self.hasContentToSroll) { 11542 // Trying to make sense of all the different wheel event implementations.. 11543 // 11544 var evntObj = event || window.event 11545 , wheelDelta = -(evntObj.deltaY || evntObj.detail || (-1 / 3 * evntObj.wheelDelta)) / 40 11546 , multiply = (evntObj.deltaMode === 1) ? self.options.wheelSpeed : 1 11547 ; 11548 11549 self.contentPosition -= wheelDelta * multiply * self.options.wheelSpeed; 11550 self.contentPosition = Math.min((self.contentSize - self.viewportSize), Math.max(0, self.contentPosition)); 11551 self.thumbPosition = self.contentPosition / self.trackRatio; 11552 11553 /** 11554 * The move event will trigger when the carousel slides to a new slide. 11555 * 11556 * @event move 11557 */ 11558 $container.trigger("move"); 11559 11560 $thumb.css(posiLabel, self.thumbPosition); 11561 $overview.css(posiLabel, -self.contentPosition); 11562 11563 if(self.options.wheelLock || _isAtBegin() && _isAtEnd()) { 11564 evntObj = $.event.fix(evntObj); 11565 evntObj.preventDefault(); 11566 } 11567 } 11568 } 11569 11570 /** 11571 * @method _drag 11572 * @private 11573 */ 11574 function _drag(event) { 11575 if(self.hasContentToSroll) { 11576 var mousePositionNew = isHorizontal ? event.pageX : event.pageY 11577 , thumbPositionDelta = hasTouchEvents ? (mousePosition - mousePositionNew) : (mousePositionNew - mousePosition) 11578 , thumbPositionNew = Math.min((self.trackSize - self.thumbSize), Math.max(0, self.thumbPosition + thumbPositionDelta)) 11579 ; 11580 11581 self.contentPosition = thumbPositionNew * self.trackRatio; 11582 11583 $container.trigger("move"); 11584 11585 $thumb.css(posiLabel, thumbPositionNew); 11586 $overview.css(posiLabel, -self.contentPosition); 11587 } 11588 } 11589 11590 /** 11591 * @method _end 11592 * @private 11593 */ 11594 function _end() { 11595 self.thumbPosition = parseInt($thumb.css(posiLabel), 10) || 0; 11596 11597 $("body").removeClass("noSelect"); 11598 $(document).unbind("mousemove", _drag); 11599 $(document).unbind("mouseup", _end); 11600 $thumb.unbind("mouseup", _end); 11601 $track.unbind("mouseup", _end); 11602 document.ontouchmove = document.ontouchend = null; 11603 } 11604 11605 return _initialize(); 11606 } 11607 11608 /** 11609 * @class tinyscrollbar 11610 * @constructor 11611 * @param {Object} options 11612 @param {String} [options.axis='y'] Vertical or horizontal scroller? ( x || y ). 11613 @param {Boolean} [options.wheel=true] Enable or disable the mousewheel. 11614 @param {Boolean} [options.wheelSpeed=40] How many pixels must the mouswheel scroll at a time. 11615 @param {Boolean} [options.wheelLock=true] Lock default window wheel scrolling when there is no more content to scroll. 11616 @param {Number} [options.touchLock=true] Lock default window touch scrolling when there is no more content to scroll. 11617 @param {Boolean|Number} [options.trackSize=false] Set the size of the scrollbar to auto(false) or a fixed number. 11618 @param {Boolean|Number} [options.thumbSize=false] Set the size of the thumb to auto(false) or a fixed number 11619 @param {Boolean} [options.thumbSizeMin=20] Minimum thumb size. 11620 */ 11621 $.fn[pluginName] = function(options) { 11622 return this.each(function() { 11623 if(!$.data(this, "plugin_" + pluginName)) { 11624 $.data(this, "plugin_" + pluginName, new Plugin($(this), options)); 11625 } 11626 }); 11627 }; 11628}(jQuery))); 11629; 11630; 11631(function($) { 11632 "use strict"; 11633 11634 var methods = {}; 11635 11636 methods.update = function () { 11637 var $scrollbar_plugin = this.data("plugin_tinyscrollbar");//get the plugin 11638 var hasContent = false; 11639 var $newContent = $(this);//get the new modified html content 11640 var viewportSize = $newContent.find('.viewport')[0].offsetHeight; 11641 var overviewSize = $newContent.find('.overview')[0].scrollHeight; 11642 var contentRatio = viewportSize / overviewSize; 11643 var trackSize = viewportSize; 11644 var thumbSize = trackSize * contentRatio; 11645 var trackRatio = (overviewSize - viewportSize) / (trackSize - thumbSize); 11646 11647 if (contentRatio < 1) { 11648 hasContent = true; 11649 } 11650 if (hasContent) { 11651 var maxPos = (trackSize - thumbSize) * trackRatio; 11652 var newPos = $scrollbar_plugin.contentPosition <= maxPos ? $scrollbar_plugin.contentPosition : maxPos; 11653 11654 $scrollbar_plugin.update(newPos); 11655 } 11656 else { 11657 $scrollbar_plugin.update(); 11658 }; 11659 }; 11660 11661 methods.init = function (options) { 11662 /*Bespoke code to wrap the scrollbar div, which is needed for tinyscrollbar to work!*/ 11663 this.children().wrapAll('<div class="viewport"><div class="overview"></div></div>'); 11664 this.prepend('<div class="scrollbar"><div class="track"><div class="thumb"><div class="end"></div></div></div></div>'); 11665 this.tinyscrollbar(this, options); 11666 //get the height of the bagDetail and add this to the parent as a data attribute 11667 //var totalHeight = 0; 11668 //$('.itemWrapper').each(function (i, elem) { 11669 // totalHeight = totalHeight 11670 // + $(elem).height() 11671 // + parseInt($(elem).css('padding-top').toString().replace('px', '')) 11672 // + parseInt($(elem).css('padding-bottom').toString().replace('px', '')) 11673 // + parseInt($(elem).css('margin-top').toString().replace('px', '')) 11674 // + parseInt($(elem).css('margin-bottom').toString().replace('px', '')); 11675 //});//each itemWrapper's height, padding and margin must be added together to obtain the height as padding isn't rendered in time! 11676 11677 //$(this).attr('data-bagDetailHeight', totalHeight+20); 11678 }; 11679 11680 /** 11681 * @class tinyscrollbar 11682 * @constructor 11683 * @param {Object} options 11684 @param {String} [options.axis='y'] Vertical or horizontal scroller? ( x || y ). 11685 @param {Boolean} [options.wheel=true] Enable or disable the mousewheel. 11686 @param {Boolean} [options.wheelSpeed=40] How many pixels must the mouswheel scroll at a time. 11687 @param {Boolean} [options.wheelLock=true] Lock default window wheel scrolling when there is no more content to scroll. 11688 @param {Number} [options.touchLock=true] Lock default window touch scrolling when there is no more content to scroll. 11689 @param {Boolean|Number} [options.trackSize=false] Set the size of the scrollbar to auto(false) or a fixed number. 11690 @param {Boolean|Number} [options.thumbSize=false] Set the size of the thumb to auto(false) or a fixed number 11691 @param {Boolean} [options.thumbSizeMin=20] Minimum thumb size. 11692 */ 11693 $.fn.NXtinyscrollbar = function (method) { 11694 if (methods[method]) { 11695 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 11696 } 11697 if (typeof method === 'object' || !method) { 11698 return methods.init.apply(this, arguments); 11699 } 11700 }; 11701}(jQuery)); 11702;
11703// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find 11704if (!Array.prototype.find) { 11705 Object.defineProperty(Array.prototype, 'find', { 11706 value: function (predicate) { 11707 // 1. Let O be ? ToObject(this value). 11708 if (this == null) { 11709 throw new TypeError('"this" is null or not defined'); 11710 } 11711 var o = Object(this); 11712 // 2. Let len be ? ToLength(? Get(O, "length")). 11713 var len = o.length >>> 0; 11714 // 3. If IsCallable(predicate) is false, throw a TypeError exception. 11715 if (typeof predicate !== 'function') { 11716 throw new TypeError('predicate must be a function'); 11717 } 11718 // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. 11719 var thisArg = arguments[1]; 11720 // 5. Let k be 0. 11721 var k = 0; 11722 // 6. Repeat, while k < len 11723 while (k < len) { 11724 // a. Let Pk be ! ToString(k). 11725 // b. Let kValue be ? Get(O, Pk). 11726 // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). 11727 // d. If testResult is true, return kValue. 11728 var kValue = o[k]; 11729 if (predicate.call(thisArg, kValue, k, o)) { 11730 return kValue; 11731 } 11732 // e. Increase k by 1. 11733 k++; 11734 } 11735 // 7. Return undefined. 11736 return undefined; 11737 }, 11738 configurable: true, 11739 writable: true 11740 }); 11741} 11742//# sourceMappingURL=Find.js.map; 11743if (!String.prototype.endsWith) { 11744 String.prototype.endsWith = function (search, this_len) { 11745 if (this_len === undefined || this_len > this.length) { 11746 this_len = this.length; 11747 } 11748 return this.substring(this_len - search.length, this_len) === search; 11749 }; 11750} 11751//# sourceMappingURL=EndsWith.js.map; 11752(function () { 11753 if (typeof window.CustomEvent === "function") 11754 return false; 11755 function CustomEvent(event, params) { 11756 params = params || { bubbles: false, cancelable: false, detail: undefined }; 11757 var evt = document.createEvent('CustomEvent'); 11758 evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); 11759 return evt; 11760 } 11761 CustomEvent.prototype = window.Event.prototype; 11762 window.CustomEvent = CustomEvent; 11763})(); 11764//# sourceMappingURL=CustomEvent.js.map; 11765var ModalProductPageHelper; 11766(function (ModalProductPageHelper) { 11767 var modalModeArgs = "modalMode=true&SinglePDPModalMode=true"; 11768 var headerClass = "singlePDPModalHeader"; 11769 var contentClass = "singlePDPModalContents"; 11770 var footerClass = "singlePDPModalFooter"; 11771 var modalWrapperClass = "modalWrapper"; 11772 var pageWrapperClass = "singlePDPPageWrapper"; 11773 var Config = { 11774 Contents: "", 11775 FullUrl: "", 11776 HeadingText: "PRODUCT QUICKVIEW", 11777 ChildHeading: "", 11778 Height: 563, 11779 Width: 819, 11780 onPreClose: null, 11781 onClose: function () { 11782 $('body').css('overflow', ''); 11783 $("body").css("position", ''); 11784 } 11785 }; 11786 function ShowProductPageModal(url, fullUrl) { 11787 var config = $.extend({}, Config); 11788 var urlParts = url.split("#"); 11789 var iframeUrl = urlParts[0]; 11790 var separator = iframeUrl.indexOf('?') !== -1 ? "&" : "?"; 11791 iframeUrl = iframeUrl + separator + modalModeArgs; 11792 if (urlParts.length > 0) 11793 iframeUrl += "#" + urlParts[1]; 11794 var pageContents = $("<div />").addClass(pageWrapperClass); 11795 var loadingSpinner = $("<div/>").addClass("singlePDPModalLoading"); 11796 var iFrame = $("<iframe />") 11797 .attr("src", iframeUrl) 11798 .attr("id", "relatedProductFrame") 11799 .attr("onload", "ModalProductPageHelper.FrameLoaded(this);") 11800 .attr("style", "opacity:0;"); 11801 pageContents.append(loadingSpinner); 11802 pageContents.append(iFrame);
11803 config.Contents = pageContents; 11804 config.FullUrl = fullUrl; 11805 ShowModal(config); 11806 } 11807 ModalProductPageHelper.ShowProductPageModal = ShowProductPageModal; 11808 function ModalAddToBag(itemID, optionNo, chain, quantity, pdpLocation) { 11809 NextBasket.ItemAdd(itemID, optionNo, quantity, chain, false, function () { }, "", pdpLocation.pathname); 11810 ModalPopup.close(null); 11811 if ($(window).scrollTop() != 0 && $('html').hasClass('touch')) { 11812 $(window).scrollTop(0); 11813 } 11814 } 11815 ModalProductPageHelper.ModalAddToBag = ModalAddToBag; 11816 function FrameLoaded(iframeObject) { 11817 try { 11818 var innerDoc = iframeObject.contentDocument || iframeObject.contentWindow.document; 11819 var $innerDoc = $(innerDoc); 11820 var fadeOutOverlay = true; 11821 if ($innerDoc.find(".ProductPage").length == 0) { 11822 if ($innerDoc.find(".Error404").length == 0 && $innerDoc.find(".Error500").length == 0) { 11823 fadeOutOverlay = false; 11824 var iframeUrl = iframeObject.src; 11825 var iframeUrlwithoutHash = iframeUrl.substr(0, iframeUrl.indexOf('#')); 11826 window.location = iframeUrlwithoutHash; 11827 } 11828 } 11829 else { 11830 var $continueShoppingLink = $("a.continueShopping"); 11831 var $articleStyle_1 = $innerDoc.find("article.Style"); 11832 if ($articleStyle_1.length == 1) { 11833 $continueShoppingLink.on("click", function (e) { 11834 var mpItemNo = $articleStyle_1.find(".StyleMeta .ItemNumber").text(); 11835 if (mpItemNo) { 11836 mpItemNo = mpItemNo.replace("-", "").toLowerCase().substring(0, 6); 11837 var tplUrl = (Next.Settings.Channel.IsTotalPlatformLiteClient) ? Next.Settings.Channel.SiteUrl : ""; 11838 window.location.href = tplUrl + "/search?w=" + mpItemNo; 11839 return false; 11840 } 11841 }); 11842 } 11843 } 11844 $innerDoc.on('click', '.loginPopoverLink, .favourites-sign-out-link', function () { 11845 window.location.href = $(this).attr('href'); 11846 }); 11847 if (fadeOutOverlay) { 11848 $('.singlePDPModalLoading').fadeOut('fast'); 11849 $(iframeObject).attr("style", "opacity:1;"); 11850 } 11851 setTimeout(function () { 11852 iframeObject.contentWindow.focus(); 11853 }, 100); 11854 } 11855 catch (e) { 11856 window.location.href = iframeObject.src; 11857 } 11858 } 11859 ModalProductPageHelper.FrameLoaded = FrameLoaded; 11860 function ShowModal(config) { 11861 var contents = config.Contents; 11862 var headingWrapper = $("<div />").addClass(headerClass); 11863 var closeButton = $("<a />").addClass("closeButton").text("Close"); 11864 var footerWrapper = $("<div />").addClass(footerClass); 11865 var pUrl = config.FullUrl; 11866 var urlArr = config.FullUrl.split('#'); 11867 if (urlArr.length > 1) { 11868 var pid = urlArr[1]; 11869 pUrl = ''; 11870 pUrl = Next.Settings.Channel.SiteUrl + "/search?w=" + pid; 11871 } 11872 var continueShopping = $("<a />").addClass("continueShopping").text("View Full Product Page").attr("href", pUrl); 11873 closeButton.click(function (e) { 11874 $('body').removeAttr('style'); 11875 ModalPopup.close(e); 11876 }); 11877 var heading = $("<h2 />").text(config.HeadingText); 11878 headingWrapper.append(heading); 11879 headingWrapper.append(closeButton); 11880 footerWrapper.append(continueShopping); 11881 var modalContents = $("<div />").addClass(contentClass); 11882 modalContents.append(headingWrapper); 11883 modalContents.append(contents); 11884 modalContents.append(footerWrapper); 11885 var $modal = ModalPopup.init({ 11886 overlay: true, 11887 width: config.Width, 11888 height: config.Height, 11889 onPreClose: config.onPreClose, 11890 onClose: config.onClose,
11891 lockBackgroundScroll: false, 11892 fullyLockBackgroundScroll: true 11893 }); 11894 if (isIOS) { 11895 $('body').css({ 11896 'overflow': 'hidden', 11897 'position': 'fixed' 11898 }); 11899 } 11900 else { 11901 $('body').css({ 11902 'overflow': 'hidden' 11903 }); 11904 } 11905 $modal.addClass(modalWrapperClass); 11906 $modal.empty(); 11907 $modal.append(modalContents); 11908 ModalPopup.show(); 11909 } 11910 function ModularPDPAddToBag(itemID, optionNo, chain, quantity, pdpPathname) { 11911 NextBasket.ItemAdd(itemID, optionNo, quantity, chain, false, function () { }, "", pdpPathname); 11912 ModalPopup.close(null); 11913 if ($(window).scrollTop() != 0 && $('html').hasClass('touch')) { 11914 $(window).scrollTop(0); 11915 } 11916 } 11917 ModalProductPageHelper.ModularPDPAddToBag = ModularPDPAddToBag; 11918})(ModalProductPageHelper || (ModalProductPageHelper = {})); 11919//# sourceMappingURL=ModalProductPageHelper.js.map; 11920/* 11921 * @preserve dataset polyfill for IE < 11. See https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset and http://caniuse.com/#search=dataset 11922 * 11923 * @author ShirtlessKirk copyright 2015 11924 * @license WTFPL (http://www.wtfpl.net/txt/copying) 11925 */ 11926/*global define: false, module: false */ 11927/*jslint nomen: true, regexp: true, unparam: true */ 11928(function datasetModule(global, definition) { // non-exporting module magic dance 11929 'use strict'; 11930 11931 var 11932 amd = 'amd', 11933 exports = 'exports'; // keeps the method names for CommonJS / AMD from being compiled to single character variable 11934 11935 if (typeof define === 'function' && define[amd]) { 11936 define(function definer() { 11937 return definition(global); 11938 }); 11939 } else if (typeof module === 'function' && module[exports]) { 11940 module[exports] = definition(global); 11941 } else { 11942 definition(global); 11943 } 11944}(this, function datasetPolyfill(global) { 11945 'use strict'; 11946 11947 var 11948 attribute, 11949 attributes, 11950 counter, 11951 dash, 11952 dataRegEx, 11953 document = global.document, 11954 hasEventListener, 11955 length, 11956 match, 11957 mutationSupport, 11958 test = document.createElement('_'), 11959 DOMAttrModified = 'DOMAttrModified'; 11960 11961 function clearDataset(event) { 11962 delete event.target._datasetCache; 11963 } 11964 11965 function toCamelCase(string) { 11966 return string.replace(dash, function (m, letter) { 11967 return letter.toUpperCase(); 11968 }); 11969 } 11970 11971 function getDataset() { 11972 var 11973 dataset = {}; 11974 11975 attributes = this.attributes; 11976 for (counter = 0, length = attributes.length; counter < length; counter += 1) { 11977 attribute = attributes[counter]; 11978 match = attribute.name.match(dataRegEx); 11979 if (match) { 11980 dataset[toCamelCase(match[1])] = attribute.value; 11981 } 11982 } 11983 11984 return dataset; 11985 } 11986 11987 function mutation() { 11988 if (hasEventListener) { 11989 test.removeEventListener(DOMAttrModified, mutation, false); 11990 } else { 11991 test.detachEvent('on' + DOMAttrModified, mutation); 11992 } 11993 11994 mutationSupport = true; 11995 } 11996 11997 if (test.dataset !== undefined) { 11998 return; 11999 } 12000 12001 dash = /\-([a-z])/ig; 12002 dataRegEx = /^data\-(.+)/; 12003 hasEventListener = !!document.addEventListener; 12004 mutationSupport = false; 12005 12006 if (hasEventListener) { 12007 test.addEventListener(DOMAttrModified, mutation, false); 12008 } else { 12009 test.attachEvent('on' + DOMAttrModified, mutation); 12010 } 12011 12012 // trigger event (if supported) 12013 test.setAttribute('foo', 'bar'); 12014
12015 Object.defineProperty(global.Element.prototype, 'dataset', { 12016 get: mutationSupport 12017 ? function get() { 12018 if (!this._datasetCache) { 12019 this._datasetCache = getDataset.call(this); 12020 } 12021 12022 return this._datasetCache; 12023 } : getDataset 12024 }); 12025 12026 if (mutationSupport && hasEventListener) { // < IE9 supports neither 12027 document.addEventListener(DOMAttrModified, clearDataset, false); 12028 } 12029}));; 12030/// <reference path="typings/jquery/jquery.d.ts" /> 12031var BloomReachSEOWidgets = /** @class */ (function () { 12032 function BloomReachSEOWidgets() { 12033 $('document').ready(function () { 12034 var urlpath = window.location.href.toLowerCase(); 12035 var plpHeader = "View Related Items"; 12036 // Parse document for data 12037 if (window.widgetObject) { 12038 var defaultDataLimits = { 12039 items: 8, 12040 products: 10, 12041 categories: 10 12042 }; 12043 var dataLimits = { 12044 items: (Next.Settings.Channel.BRSEOWidgetsItemsMax ? Next.Settings.Channel.BRSEOWidgetsItemsMax : defaultDataLimits.items), 12045 products: (Next.Settings.Channel.BRSEOWidgetsProductsMax ? Next.Settings.Channel.BRSEOWidgetsProductsMax : defaultDataLimits.products), 12046 categories: (Next.Settings.Channel.BRSEOWidgetsCategoriesMax ? Next.Settings.Channel.BRSEOWidgetsCategoriesMax : defaultDataLimits.categories) 12047 }; 12048 var theData = { 12049 // Trim products based on config 12050 // -------------------------------------- 12051 // View Related Items 12052 moreResults: (Array.isArray(window.widgetObject['more-results']) ? window.widgetObject['more-results'].slice(0, dataLimits.items) : window.widgetObject['more-results']), 12053 // 12054 // More Related Products 12055 relatedItem: (Array.isArray(window.widgetObject['related-item']) ? window.widgetObject['related-item'].slice(0, dataLimits.products) : window.widgetObject['related-item']), 12056 // 12057 // Categories 12058 relatedCategory: (Array.isArray(window.widgetObject['related-category']) ? window.widgetObject['related-category'].slice(0, dataLimits.categories) : window.widgetObject['related-category']), 12059 }; 12060 // Replace data lists with newly trimmed versions 12061 window.widgetObject['more-results'] = theData.moreResults; 12062 window.widgetObject['related-item'] = theData.relatedItem; 12063 window.widgetObject['related-category'] = theData.relatedCategory; 12064 } 12065 //display only View Related Items 12066 if (!Next.Settings.Channel.EnableBRSEOWidgetsDisplayAllRows) { 12067 if (document.getElementById("br-related-items-widget")) { 12068 document.getElementById("br-related-items-widget").style.display = "none"; 12069 } 12070 } 12071 //ensure bottom two rows of widgets are in mixed case 12072 var camelCaseRc = ""; 12073 $(".br_widget_wrapper .br-ri-link").each(function (i, obj) { 12074 camelCaseRc = $(this).text().toLowerCase(); 12075 $(this).text(camelCaseRc); 12076 }); 12077 $(".br_widget_wrapper .br-rc-link").each(function (i, obj) { 12078 camelCaseRc = $(this).text().toLowerCase(); 12079 $(this).text(camelCaseRc); 12080 }); 12081 $(".br_widget_wrapper .br-ri-link").addClass('CamelCase'); 12082 $(".br_widget_wrapper .br-rc-link").addClass('CamelCase'); 12083 //change header & reset left margin on plp 12084 if (urlpath.indexOf("shop") > -1 || urlpath.indexOf("search") > -1) { 12085 $(".br_widget_wrapper .br-found-heading span").text(plpHeader); 12086 $(".br_widget_wrapper .br-sf-widget-merchant-popup-maincont").addClass('PLPSEOAlterations'); 12087 } 12088 else { 12089 //Reset left margin on pdp 12090 $(".br_widget_wrapper .br-sf-widget-merchant-popup-maincont").removeClass('PLPSEOAlterations').removeClass('PLPSEOAlterationsNoFlash'); 12091 //Append pop up to parent container 12092 var seoPopupDivs = $('#br-more-results-widget .br-rp-qv-hide'); 12093 seoPopupDivs.each(function (index, v) { 12094 $("#br-more-results-widget").append(v); 12095 }); 12096 } 12097 //alter styling for IE and edge 12098 if (document.documentMode || /Edge/.test(navigator.userAgent) || (navigator.userAgent.indexOf("MSIE") != -1) || navigator.appVersion.indexOf('Trident/') > -1) { 12099 $(".br_widget_wrapper .br-sf-widget-merchant-popup-view").addClass('InternetExplorer'); 12100 } 12101 //shorten string lengths for the widgets 12102 StringLengthsFunction(".br_widget_wrapper .br-sf-widget-merchant-title a", 30); 12103 StringLengthsFunction(".br_widget_wrapper .br-sf-widget-merchant-desc", 105); 12104 StringLengthsFunction(".br_widget_wrapper .br-sf-widget-merchant-popup-title a", 105); 12105 StringLengthsFunction(".br_widget_wrapper .br-sf-widget-merchant-popup-desc", 400); 12106 StringLengthsFunction(".br_widget_wrapper .br-ri-link", 40); 12107 StringLengthsFunction(".br_widget_wrapper .br-rc-link", 40); 12108 function StringLengthsFunction(className, stringLength) { 12109 $(className).each(function (i) { 12110 var thisText = $(this).text().trim(); 12111 if (thisText.length > stringLength) { 12112 $(this).text(thisText.substring(0, stringLength).trim() + "â¦"); 12113 } 12114 }); 12115 } 12116 window.isModalVisible || false; 12117 function hideBrRpQv() { 12118 var elems = document.querySelectorAll(".br-rp-qv-show"); 12119 for (var i = 0; i < elems.length; i++) { 12120 var classStr = elems[i].getAttribute('class') + ' '; 12121 classStr = classStr.replace('br-rp-qv-show ', 'br-rp-qv-hide '); 12122 elems[i].setAttribute('class', classStr); 12123 window.isModalVisible = false; 12124 } 12125 } 12126 ; 12127 function showBrRpQv(id) { 12128 hideBrRpQv(); 12129 var classStr = document.getElementById(id).getAttribute('class') + ' '; 12130 classStr = classStr.replace('br-rp-qv-hide ', 'br-rp-qv-show '); 12131 document.getElementById(id).setAttribute('class', classStr); 12132 setTimeout(function () { 12133 window.isModalVisible = true; 12134 }, 100); 12135 } 12136 ; 12137 // Add event handler for clicking outside the modal 12138 $(document).click(function (e) { 12139 var $target = $(e.target), isModalClick = $target.parents('.br-sf-widget-merchant-popup-cont').length > 0 || $target.hasClass('br-sf-widget-merchant-popup-cont'); 12140 if (!isModalClick && window.isModalVisible) { 12141 hideBrRpQv(); 12142 } 12143 }); 12144 // Add event handler for pressing the escape key 12145 $(document).keyup(function (e) { 12146 if (27 === e.keyCode) { 12147 hideBrRpQv(); 12148 } 12149 }); 12150 // Show popup 12151 $('.js-br-sf-widget-btn').on('click', function (e) { 12152 e.preventDefault(); 12153 var brId = $(this).data('br-id'); 12154 showBrRpQv(brId); 12155 }); 12156 // Close popups 12157 $('.br-sf-widget-merchant-popup-close').on('click', function (e) { 12158 e.preventDefault(); 12159 hideBrRpQv(); 12160 }); 12161 //dataLayer for seo search pills 12162 $('#ResultBanner a').on('click', function (e) { 12163 if (dataLayer != null) { 12164 dataLayer.push({ 12165 event: 'seoPills', 12166 link_category: 'seoPills', 12167 link_id: 'click', 12168 link_name: this.innerText, 12169 link_value: '' 12170 }); 12171 } 12172 }); 12173 }); 12174 } 12175 return BloomReachSEOWidgets; 12176}()); 12177new BloomReachSEOWidgets(); 12178//# sourceMappingURL=BloomReachSEOWidgets.js.map; 12179var NumOfItemsInRow = 0; 12180var Orientation = 'Portrait'; 12181var $article; 12182var $scrollRowWrapper; 12183var imageSize = 174.5; 12184var maxPossibleWidth = 880; 12185var stdScreenWidth = 1280; 12186var itemWidth = 175; 12187var rhsContheight = 0; 12188 12189var FindSimilar = { 12190 12191 getSimilarItems: function (item) { 12192 12193 $article = $(item).parents("article.Item").first(); 12194 var $page = $article.parents("div.Page").first(); 12195 var position = $article.attr('data-itemposition'); 12196 var $findSimilarDiv = $(item).parents("div.find-similar").first(); 12197 var productNo = $findSimilarDiv.attr('data-mltId'); 12198 var responsiveLargeClass = Next.Settings.UI.HorizontalFiltersEnabled ? "responsiveLarge hf" : "responsiveLarge"; 12199 this.hideElements(); 12200 rhsContheight = $("#rhs").css("min-height"); 12201 12202 $('.showMore').not($(item)).removeClass('Hide'); 12203 12204 Orientation = $page.hasClass('Landscape') ? 'Landscape' : 'Portrait'; 12205 getNumberOfColumns(); 12206 12207 var targetUrl = "/mlt?w=" + productNo; 12208 12209 var hasColors = $article.find(".ColourExpander").length > 0;
12210 12211 if (!hasColors) { 12212 $findSimilarDiv.find('.arrow_box').addClass('colorway'); 12213 } 12214 12215 var totalWidth = 0; 12216 12217 setTimeout(function() { 12218 $.ajax({ 12219 url: targetUrl, 12220 type: "GET", 12221 cache: true, 12222 dataType: "json", 12223 success: function (data) { 12224 var mltContainer = "<div class='mltContainer'>" + "<div class='mainWrapper'>" + "<div class='mltClose'>" + 12225 "<svg width='20px' height='15px' viewBox='0 0 22 20' version='1.1'><g><g transform='translate(-964.000000, -789.000000)' stroke='#525252' stroke-width='2.3'>" + 12226 "<g transform='translate(965.000000, 789.000000)'><path d='M0,0 L20,20'></path>" + 12227 "<path d='M20,0 L0,20'></path></g></g></g></svg></div> <div class='scrollRowWrapper'>" + 12228 "<div class='scrollControl left'><a href='#' class='scrollNav previous disabled'><</a></div>" + 12229 "<div class='clip'>" + "<div class='mltItemContainer'><ul class='mlt_animate'></ul></div></div>" + 12230 "<div class='scrollControl right'><a href='#' class='scrollNav next'>></a></div></div></div>"; 12231 12232 var itemPosition = parseInt(position); 12233 var numberOfSiblingsInRow = getNumberOfSiblingsInCurrentRow(); 12234 var $endOfRow = (numberOfSiblingsInRow > 0) ? $article.nextAll('#i' + (itemPosition + numberOfSiblingsInRow) + 12235 ", #campaignArticleWide, #campaignArticleNarrow").filter(function () { 12236 if ($(this).css("display") == "none") { 12237 return false; 12238 } 12239 return true; 12240 }).first() : $article; 12241 12242 $(mltContainer).insertAfter($endOfRow); 12243 12244 switch (Orientation) { 12245 case 'Landscape': 12246 $('.mltContainer').addClass('Landscape'); 12247 switch (NumOfItemsInRow) { 12248 case 2: 12249 $('.mltContainer').addClass('responsiveSmall'); 12250 break; 12251 case 3: 12252 $('.mltContainer').addClass(responsiveLargeClass); 12253 break; 12254 case 4: 12255 $('.mltContainer').addClass('responsiveXL'); 12256 break; 12257 } 12258 break; 12259 case 'Portrait': 12260 $('.mltContainer').addClass('Portrait'); 12261 switch (NumOfItemsInRow) { 12262 case 3: 12263 $('.mltContainer').addClass('responsiveSmall'); 12264 break; 12265 case 4: 12266 $('.mltContainer').addClass(responsiveLargeClass); 12267 break; 12268 case 5: 12269 $('.mltContainer').addClass('responsiveXL'); 12270 break; 12271 } 12272 break; 12273 } 12274 12275 var $ul = $(".mlt_animate"); 12276 12277 $.each(data.MltSearchResults, function (i, mltItem) { 12278 $("<li/>").attr('class', 'mlt_item').attr("data-image", mltItem.SearchImage).attr("data-itemURL", mltItem.Url).attr("data-price", mltItem.Price).appendTo($ul); 12279 }); 12280 12281 $ul.find("li.mlt_item").last().addClass("noPaddingRight"); 12282 12283 $findSimilarDiv.find('.arrow_box').css('display', 'block'); 12284 $findSimilarDiv.find('.showLess').removeClass('Hide'); 12285 $findSimilarDiv.find('.showMore').addClass('Hide'); 12286 12287 totalWidth += data.MltSearchResults.length * imageSize; 12288 12289 $('.mltItemContainer').width(totalWidth);
12290 12291 if (Next.Settings.UI.HorizontalFiltersEnabled && $(".mltContainer .clip").length > 0) { 12292 maxPossibleWidth = $(".mltContainer .clip").outerWidth(); 12293 totalWidth = Math.floor(totalWidth); 12294 } 12295 if (totalWidth <= maxPossibleWidth) { 12296 $("a.next").addClass("disabled"); 12297 } 12298 12299 if ($('.mlt_animate').length > 0) { 12300 12301 $('.mlt_animate').find("li").each(function (i) { 12302 if (i < (NumOfItemsInRow + 1) * 2) { 12303 var $li = $(this); 12304 $li.addClass("databound"); 12305 var url = $li.attr("data-itemURL"); 12306 var imageUrl = $li.attr("data-image"); 12307 var price = $li.attr("data-price"); 12308 var $itemDiv = $("<div/>").appendTo($li); 12309 var $pdp = $("<a/>").attr('href', url).appendTo($itemDiv); 12310 $("<img />").attr('src', imageUrl).attr('class', 'mlt_image').appendTo($pdp); 12311 $("<div class='mlt_price'></div>").html(price).appendTo($pdp); 12312 } 12313 }); 12314 } 12315 12316 $scrollRowWrapper = $(".scrollRowWrapper").first(); 12317 $scrollRowWrapper.css({"border-bottom": "1px solid #c7c5c5"}); 12318 12319 Results.MltBusy = false; 12320 12321 //function to fix the gap that appears between MLT box and arrow when there is a longer article element in the same row 12322 FindSimilar.arrowGapFix(); 12323 12324 //check if MLT panel is out of view port if so scroll into view 12325 var isInViewport = $('.mltItemContainer').isVisibleInViewPort(); 12326 if (!isInViewport) { 12327 var elementHeight = $findSimilarDiv.height(); 12328 var viewportHeight = $(window).height(); 12329 $([document.documentElement, document.body]).animate({ 12330 scrollTop: $findSimilarDiv.offset().top - ((viewportHeight - elementHeight) / 2) 12331 }, 1000); 12332 } 12333 if (Next.Settings.UI.HorizontalFiltersEnabled && $("#ContentArea").outerHeight() < $(window).height()) { 12334 $("#rhs").css("min-height", $(window).height()); 12335 if ($("body").hasClass("touchMode")) { 12336 $("body").css("overflow-y", "hidden"); 12337 } 12338 } 12339 $("a.scrollNav.previous").click(function () { 12340 FindSimilar.scroll(1, this, $('.scrollRowWrapper')); 12341 if (!$(this).hasClass("disabled")) { 12342 TrackGAEvent("Desktop MLT", "Clicked on the 'previous' scroll control arrow", "Clicked on the 'previous' scroll control arrow for Item Number " + productNo, "ID: " + productNo + " - Previous"); 12343 } 12344 return false; 12345 }); 12346 $("a.scrollNav.next").click(function () { 12347 FindSimilar.scroll(-1, this, $('.scrollRowWrapper')); 12348 if (!$(this).hasClass("disabled")) { 12349 TrackGAEvent("Desktop MLT", "Clicked on the 'next' scroll control arrow", "Clicked on the 'next' scroll control arrow for Item Number " + productNo, "ID: " + productNo + " - Next"); 12350 } 12351 return false; 12352 }); 12353 $(".mltClose").click(function () { FindSimilar.closeMltContainer(this); return false; }); 12354 }, 12355 error: function() { 12356 Results.MltBusy = false; 12357 } 12358 12359 }); 12360 }, 0); 12361 TrackGAEvent("Desktop MLT", "Click on 'More Like This' link", "Clicked on 'More Like This' link for Item Number " + productNo, "ID: " + productNo + " - Open MLT"); 12362 12363 }, 12364 closeMltContainer: function() { 12365 FindSimilar.hideElements(); 12366 $('.showMore').removeClass('Hide'); 12367 if (Next.Settings.UI.HorizontalFiltersEnabled && $("#ContentArea").outerHeight() < $(window).height()) { 12368 $("#rhs").css("min-height", $(window).height()); 12369 if ($("body").hasClass("touchMode")) { 12370 $("body").css("overflow-y", "hidden"); 12371 } 12372 } 12373 if (Next.Settings.UI.HorizontalFiltersEnabled) { 12374 $("#rhs").css("min-height", rhsContheight); 12375 if ($("body").hasClass("touchMode")) { 12376 $("body").css("overflow-y", "scroll"); 12377 } 12378 } 12379 }, 12380 hideElements: function() { 12381 $('.mltContainer').remove(); 12382 $('.showLess').addClass("Hide"); 12383 $('.arrow_box').css('display', 'none'); 12384 $(".mltClose").addClass("Hide"); 12385 }, 12386 scroll: function(direction, element, parent) { 12387 12388 var mltContainer = parent.find(".mltItemContainer"); 12389 12390 if ($(element).hasClass("disabled") || 12391 mltContainer.hasClass("locked") || 12392 mltContainer.position().left === 0 && direction === 1 || 12393 mltContainer.find("li").length < NumOfItemsInRow) { 12394 return false; 12395 } 12396 12397 var liCount = mltContainer.find("li").length; 12398 12399 var currentOffset = mltContainer.css("margin-left").replace('px', ''); 12400 12401 var mltItems = (Orientation == 'Landscape') ? (parseInt(NumOfItemsInRow) + 2) : (parseInt(NumOfItemsInRow) + 1); 12402 12403 var pageSize = itemWidth * mltItems; 12404 var listLen = liCount * itemWidth; 12405 var distance = pageSize * direction; 12406 12407 var newOffset = parseInt(currentOffset) + parseInt(distance);
12408 var maxOffset = (listLen - pageSize) * direction; 12409 12410 if ((newOffset < maxOffset) && direction === -1) { 12411 distance = maxOffset - parseInt(currentOffset); 12412 } 12413 12414 if ((newOffset > 0) && direction === 1) { 12415 distance = 0 - parseInt(currentOffset); 12416 } 12417 12418 //Safety check 12419 if (distance > pageSize) { 12420 distance = pageSize; 12421 } 12422 12423 if (distance < -pageSize) { 12424 distance = -pageSize; 12425 } 12426 12427 //ie8 and below don't like animating from one css class to another - so, set the animation time to 0 12428 var transitionSpeed = 200; 12429 12430 mltContainer.addClass("locked").stop().animate({ marginLeft: "+=" + distance },500,function() { 12431 12432 if (direction === -1) { 12433 $('.scrollRowWrapper').find("a.scrollNav.previous").removeClass("disabled", transitionSpeed); 12434 } 12435 12436 var left = parseInt($(mltContainer).css('margin-left')); 12437 12438 if (left === 0) { 12439 $('.scrollRowWrapper').find("a.scrollNav.previous").addClass("disabled", transitionSpeed); 12440 } 12441 12442 if ((left + mltContainer.width()) < pageSize) { 12443 $('.scrollRowWrapper').find("a.scrollNav.next").addClass("disabled", transitionSpeed); 12444 } else { 12445 $('.scrollRowWrapper').find("a.scrollNav.next").removeClass("disabled", transitionSpeed); 12446 } 12447 12448 mltContainer.removeClass("locked"); 12449 12450 $('.mlt_animate').find("li").each(function () { 12451 var i = 0; 12452 if (!$(this).hasClass("databound")) { // Check if image is already loaded 12453 if (i < (NumOfItemsInRow + 1) * 2) { 12454 var $li = $(this); 12455 $li.addClass("databound"); 12456 var url = $li.attr("data-itemURL"); 12457 var imageUrl = $li.attr("data-image"); 12458 var price = $li.attr("data-price"); 12459 12460 var $itemDiv = $("<div/>").appendTo($li); 12461 var $pdp = $("<a/>").attr('href', url).appendTo($itemDiv); 12462 $("<img />").attr('src', imageUrl).attr('class', 'mlt_image').appendTo($pdp); 12463 $("<div class='mlt_price'></div>").html(price).appendTo($pdp); 12464 i++; 12465 } 12466 } 12467 }); 12468 }); 12469 return false; 12470 }, 12471 arrowGapFix: function () { 12472 var $arrow = $article.find(".arrow_box").first(); 12473 12474 var scrollRowWrapperTop = $scrollRowWrapper.offset().top; 12475 12476 $arrow.offset({ top: (scrollRowWrapperTop + 1) }); 12477 } 12478}; 12479$(function () { 12480 if (Next.Settings.Channel.EnableFindSimilar) { 12481 window.onresize = function() { 12482 var rows = (Orientation !== 'Landscape' ? 3 : 2); 12483 if (Next.Settings.UI.HorizontalFiltersEnabled) { 12484 rows = 4;//Number of items in row. 12485 } 12486 if ((NumOfItemsInRow > rows && window.innerWidth < stdScreenWidth) || 12487 (NumOfItemsInRow <= rows && window.innerWidth > stdScreenWidth)) { 12488 FindSimilar.hideElements(); 12489 $('.showMore').removeClass('Hide'); 12490 } 12491 } 12492 $.fn.isVisibleInViewPort = function () { 12493 var $t = $(this), 12494 $w = $(window), 12495 viewTop = $w.scrollTop(),
12496 viewBottom = viewTop + $w.height(), 12497 top = $t.offset().top, 12498 bottom = top + $t.height(); 12499 12500 return ((bottom <= viewBottom) && (top >= viewTop)); 12501 }; 12502 } 12503 12504}); 12505 12506function getNumberOfSiblingsInCurrentRow() { 12507 var i = 0; 12508 var $articleSiblings = $article.nextAll("article"); 12509 $articleSiblings.each(function () { 12510 if ($(this).css("display") == "none") { 12511 return true; 12512 } 12513 if ($(this).offset().top > $article.offset().top) { 12514 return false; 12515 } 12516 else { 12517 i++; 12518 } 12519 }); 12520 return i; 12521} 12522 12523function getNumberOfColumns() { 12524 if (window.innerWidth >= stdScreenWidth) { 12525 if (Orientation === 'Landscape') { 12526 NumOfItemsInRow = Next.Settings.UI.HorizontalFiltersEnabled ? 4 : 3; 12527 } 12528 else { 12529 NumOfItemsInRow = Next.Settings.UI.HorizontalFiltersEnabled ? 5 : 4; 12530 } 12531 } 12532 else { 12533 if (Orientation === 'Landscape') { 12534 NumOfItemsInRow = Next.Settings.UI.HorizontalFiltersEnabled ? 3 : 2; 12535 } 12536 else { 12537 NumOfItemsInRow = Next.Settings.UI.HorizontalFiltersEnabled ? 4 : 3; 12538 } 12539 } 12540}; 12541var nextBagCookieName = Next.Settings.Cookie.BackToNextCookieName; 12542var fromNextURLCookieName = Next.Settings.Cookie.BackToNextURLCookieName; 12543var shortCompanyName = Next.Settings.Channel.ShortCompanyName; 12544var currentUrl = window.location.href.toLowerCase(); 12545var isEntry = currentUrl.indexOf('?fromnext') > 0; 12546var bagTextShowName = "showb2nBagText"; 12547var nextBagCookieVal = readCookie(nextBagCookieName); 12548var nextBagUrlVal = readCookie(fromNextURLCookieName); 12549var userDeviceType = (dataLayer.length >= 2 && dataLayer[1].device_type) ? (dataLayer[1].device_type).toLowerCase() : ""; 12550var url = "//www.next.co.uk"; 12551if ((isEntry || nextBagCookieVal == "True") && (Next.Settings.Channel.ChannelTheme != "")) { 12552 if (Next.Settings.Channel.EnableBackToNextBanner) { 12553 if (Next.Settings.Channel.BackToNextURL) 12554 url = Next.Settings.Channel.BackToNextURL; 12555 switch (userDeviceType) { 12556 case "desktop": 12557 insertBannerToHTMLDesktop(); 12558 break; 12559 case "mobile": 12560 insertBannerToHTMLMobile(); 12561 break; 12562 } 12563 if (nextBagCookieVal != "True") 12564 createCookie(nextBagCookieName, "True", null, location.host); 12565 if (nextBagUrlVal == null) { 12566 if (Next.Settings.Channel.StateConvergence != null && Next.Settings.Channel.StateConvergence.Domains != null && Next.Settings.Channel.StateConvergence.Domains.some(function (x) { return x > 0; })) 12567 url = Next.Settings.Channel.StateConvergence.Domains.find(function (x) { return !x.includes("mobile.") || !x.includes("m."); }); 12568 createCookie(fromNextURLCookieName, url, null, location.host); 12569 } 12570 ; 12571 } 12572} 12573if (isEntry) { 12574 var bagCheck = setInterval(function () { 12575 if (NextBasket.Loaded) { 12576 if (nextBagCookieVal == "True") { 12577 if (NextBasket.Data["ItemCount"] > 0 && Next.Settings.Channel.StateConvergence.IsEnabled) { 12578 sessionStorage.setItem(bagTextShowName, "true"); 12579 displayBackToNextBagText(); 12580 } 12581 clearInterval(bagCheck); 12582 } 12583 } 12584 }, 200); 12585} 12586else if (sessionStorage.getItem(bagTextShowName) == "true") { 12587 displayBackToNextBagText(); 12588} 12589function displayBackToNextBagText() { 12590 $("#backToNext-shoppingBagText").css("display", "inline-block"); 12591} 12592var backToNextBanner = $("#backToNextBanner"); 12593//Only run code if the banner has been added to the DOM 12594if (backToNextBanner.length) { 12595 var btnbHeight = backToNextBanner.height(); 12596 //DESKTOP - Initial setup of the margin adjustments for elements across different pages 12597 $(".PageHeader").ready(function () { 12598 //Used across the site 12599 var height = $(".PageHeader").height(); 12600 if (nextBagCookieVal == "True") { 12601 height += btnbHeight; 12602 } 12603 $(".HeaderSpacer").height(height + "px"); 12604 //Tablet 2.0 Header 12605 var headerHeight = parseInt($("#header_upper-nav_nav").css("height")) + parseInt($("#header_lower-nav_nav").css("height")); 12606 $("#nav-headers-spacer").height(headerHeight); 12607 //Used on PLP Page 12608 $(".filterControls").css("margin-top", (parseInt($(".filterControls").css("margin-top")) + btnbHeight) + "px"); 12609 $(".ResultCount").css("margin-top", (parseInt($(".ResultCount").css("margin-top")) + btnbHeight) + "px"); 12610 $(".ResultCount").attr("data-btnb", "true"); 12611 if (!Next.Settings.UI.HorizontalFiltersEnabled) { 12612 $("#ResultHeader").css("margin-top", (parseInt($("#ResultHeader").css("margin-top")) + btnbHeight) + "px"); 12613 } 12614 //Tablet 2.0 12615 // horizontal brands page 12616 $(".bp-wrapper").addClass("backToNextActive"); 12617 });
12618 //MOBILE 12619 var filterHeader = parseInt($("#filter-header").css("top")); 12620 var filterHeaderwithBtnb = filterHeader + btnbHeight; 12621 var plp_filterWrapperHeader = parseInt($("#filter-wrapper").css("top")); 12622 var plp_filterWrapperHeaderwithBtnb = plp_filterWrapperHeader + btnbHeight; 12623 var plp_sortMoreWrapperHeader = parseInt($("#sort-wrapper").css("margin-top")); 12624 var plp_sortMoreWrapperHeaderwithBtnb = plp_sortMoreWrapperHeader + btnbHeight; 12625 var plp_moreViewsWrapperHeader = parseInt($("#more-wrapper").css("margin-top")); 12626 var plp_moreViewsWrapperHeaderwithBtnb = plp_moreViewsWrapperHeader + btnbHeight; 12627 var tablet_upperHeader = $("#header_upper-nav_nav"); 12628 var tablet_lowerNav = $("#header_lower-nav_nav"); 12629 var tablet_baseNav = $(".navbar"); 12630 var tablet_headerSpacer = $("#nav-headers-spacer"); 12631 var origNavHeadersSpacer = $("#nav-headers-spacer").height(); 12632 var newNavHeadersSpacer = origNavHeadersSpacer + btnbHeight; 12633 $("#filter-header").css('top', filterHeaderwithBtnb + "px"); 12634 $("#filter-wrapper").css('top', plp_filterWrapperHeaderwithBtnb + "px"); 12635 $("#sort-wrapper").css('margin-top', plp_sortMoreWrapperHeaderwithBtnb + "px"); 12636 $("#more-wrapper").css('margin-top', plp_moreViewsWrapperHeaderwithBtnb + "px"); 12637 tablet_lowerNav.addClass("backToNextActive"); 12638 tablet_upperHeader.addClass("backToNextActive"); 12639 tablet_baseNav.addClass("backToNextActive"); 12640 tablet_headerSpacer.addClass("backToNextActive"); 12641 //Store Locator 12642 $("#searchTextField, #viewToggleButton").attr('style', "top: " + ($("#nav-headers").height() + 6) + "px !important"); 12643 //CLOSE BANNER 12644 $("#backToNextClose").click(function (e) { 12645 e.preventDefault(); 12646 var btnbHeight = backToNextBanner.height(); 12647 if (dataLayer[1].device_type == "mobile") { 12648 TrackGTMEvent("back_to_next", "tap", "Banner_backtonextclosed"); 12649 } 12650 else if (dataLayer[1].device_type == "desktop") { 12651 TrackGAEvent("back_to_next", "click", "Banner_backtonextclosed"); 12652 } 12653 backToNextBanner.remove(); 12654 $(".HeaderSpacer").height($(".PageHeader").height() + "px"); 12655 document.cookie = fromNextURLCookieName + "=; path=/; domain=" + location.host + "; expires = Thu, 01 Jan 1970 00:00:00 GMT"; 12656 document.cookie = nextBagCookieName + "=; path=/; domain=" + location.host + "; expires = Thu, 01 Jan 1970 00:00:00 GMT"; 12657 sessionStorage.removeItem(bagTextShowName); 12658 //DESKTOP 12659 //Used on PLP Page 12660 $(".filterControls").css("margin-top", (parseInt($(".filterControls").css("margin-top")) - btnbHeight) + "px"); 12661 $(".ResultCount").css("margin-top", (parseInt($(".ResultCount").css("margin-top")) - btnbHeight) + "px"); 12662 $(".ResultCount").attr("data-btnb", "false"); 12663 if (!Next.Settings.UI.HorizontalFiltersEnabled) { 12664 $("#ResultHeader").css("margin-top", (parseInt($("#ResultHeader").css("margin-top")) - btnbHeight) + "px"); 12665 } 12666 // horizontal brands page 12667 $(".bp-wrapper").removeClass("backToNextActive"); 12668 //MOBILE 12669 //Tablet 2.0 12670 tablet_lowerNav.removeClass("backToNextActive"); 12671 tablet_upperHeader.removeClass("backToNextActive"); 12672 tablet_baseNav.removeClass("backToNextActive"); 12673 tablet_headerSpacer.removeClass("backToNextActive"); 12674 //Site wide for header 12675 $("#nav-headers-spacer").css('height', $("#nav-headers").height() + 'px'); 12676 //PLP 12677 $("#filter-header").css('top', filterHeader + "px"); 12678 $("#filter-wrapper").css('top', plp_filterWrapperHeader + "px"); 12679 $("#sort-wrapper").css('margin-top', plp_sortMoreWrapperHeader + "px"); 12680 $("#more-wrapper").css('margin-top', plp_moreViewsWrapperHeader + "px"); 12681 //Store Locator 12682 $("#searchTextField, #viewToggleButton").attr('style', "top: " + ($("#nav-headers").height() + 6) + "px !important"); 12683 }); 12684 //SELECT 'BACK TO NEXT' 12685 $("#backToNextLink").on("click", function (e) { 12686 document.cookie = fromNextURLCookieName + "=; path=/; domain=" + location.host + "; expires = Thu, 01 Jan 1970 00:00:00 GMT"; 12687 document.cookie = nextBagCookieName + "=; path=/; domain=" + location.host + "; expires = Thu, 01 Jan 1970 00:00:00 GMT"; 12688 sessionStorage.removeItem(bagTextShowName); 12689 if (dataLayer[1].device_type == "mobile") { 12690 TrackGTMEvent("back_to_next", "tap", "Banner_backtonextclicked"); 12691 } 12692 else if (dataLayer[1].device_type == "desktop") { 12693 TrackGAEvent("back_to_next", "click", "Banner_backtonextclicked"); 12694 } 12695 });
12696} 12697function insertBannerToHTMLDesktop() { 12698 var insertString = '<div id="backToNextBanner"><span id="backToNext">'; 12699 insertString += '<a id="backToNextLink" href="' + url + '" data-link-id="back_to_next" data-link-name="Banner_backtonextclicked">'; 12700 insertString += '<img id="backToNextArrow" src="/Images/Childsplay/back_arrow_black.svg"><p>Back to</p><img id="backToNextIcon" src="/Images/LittleLabel/Next-Logo.svg"></a></span><div id="backToNext-shoppingBagText"><div class="backToNextHeartIcon"><img src="/Images/Childsplay/brands-heart.svg"></div><span class="backToNext-Text">Luxury Kids Brands from BOSS to Versace</span></div>'; 12701 insertString += '<span id="backToNextClose" data-link-id="back_to_next" data-link-name="Banner_backtonextclosed"><img class="backToNextCloseIcon" src="/Images/Childsplay/close_icon_black.svg"></span></div>'; 12702 $(".PageHeader").prepend(insertString); 12703} 12704function insertBannerToHTMLMobile() { 12705 var colxsname = Next.Settings.Channel.EnableUpgradedBootstrap ? "col" : "col-xs"; 12706 var insertString = '<div class="' + colxsname + '-12" id="backToNextBanner">'; 12707 insertString += '<div class="backToNext"><div id="backToNext-left"><a id="backToNextLink" href="' + url + '" data-link-id="back_to_next" data-link-name="Banner_backtonextclicked"><span id="backToNextLogo-arrowImg"></span><span id="backToNextLogo-backtoText">Back to</span><span id="backToNextLogo-nextLogo"></span></a></div>'; 12708 insertString += '<div id="backToNext-shoppingBagText"><div class="backToNextHeartIcon"><img src="/Images/Childsplay/brands-heart.svg"></div><span class="backToNext-Text">Luxury Kids Brands from BOSS to Versace</span></div><span id="backToNextClose" data-link-id="back_to_next" data-link-name="Banner_backtonextclosed"><img class="backToNextCloseIcon" src="/Images/Childsplay/close_icon_black.svg"/></span></div></div>'; 12709 $("#navigationheader").prepend(insertString); 12710 $("#header_upper-nav_nav").prepend(insertString); 12711} 12712//# sourceMappingURL=BackToNext.js.map; 12713/* 12714 * Search A/B Testing within Cache 12715 * 12716 * Created:January 2020 12717 * 12718 * A cookie is generated based on the value of the Bloomreach ID (BRUID), 12719 * this allows for the switching between different cached pages on the Akamai servers. 12720 * 12721 * For further information, please consult the following document, 12722 * "Search AB Testing Cache Implementation v_2_0.docx" 12723 */ 12724 12725const PartsLength = 4; 12726const UidParts = 2; 12727const UidLength = 2; 12728const FullRange = 100; 12729 12730try { 12731 var useCloudHeader = Next.Settings.Channel.EnableModernisedHeader; 12732 var isEnabled = (Next.Settings.Channel.BloomReachCachingEnabled && !useCloudHeader); 12733 var bruidCookie = readCookie(Next.Settings.Channel.BloomReachCachingIdentifier); 12734 var cookieJar = Next.Settings.Channel.BloomReachCachingCookieList.split(','); 12735 var cookieName = Next.Settings.Channel.BloomReachCachingCookieName; 12736 var numberOfDays = Next.Settings.Channel.BloomReachCachingCookieLifetime; 12737 12738 if (isEnabled && bruidCookie !== null && bruidCookie !== undefined && cookieJar !== null && cookieJar !== undefined && cookieJar.length > 1 && cookieName !== null && cookieName !== undefined && cookieName.length > 1) { 12739 12740 var parts = decodeURIComponent(bruidCookie).split(':'); 12741 12742 // Extract the Bloomreach user identifier (uid). 12743 if (parts !== null && parts !== undefined && parts.length == PartsLength) { 12744 12745 var uid = parts[0].split('='); 12746 if (uid !== null && uid !== undefined && uid.length == UidParts && uid[1].length > UidLength) { 12747 // Check the last 2 digits of the UID and select the appropriate cookie. 12748 var check = parseInt(uid[1].substr(uid[1].length - UidLength)); 12749 var segment = Math.floor(FullRange / cookieJar.length); 12750 var index = Math.floor(check / segment); 12751 12752 if (index <= 0 || index >= cookieJar.length) index = 0; 12753 12754 createCookie(cookieName, cookieJar[index], numberOfDays, ''); 12755 } 12756 12757 } 12758 } 12759 else if (!useCloudHeader) { 12760 eraseCookie(cookieName); 12761 } 12762} catch (e) { 12763 console.log(e); 12764};
12765var GAFirebase; 12766(function (GAFirebase) { 12767 var GAFirebaseAnalytics = /** @class */ (function () { 12768 function GAFirebaseAnalytics() { 12769 } 12770 GAFirebaseAnalytics.prototype.ga = function (command, hitType, eventCategory, eventAction, eventLabel, eventValue, fieldsObject) { 12771 var win = window; 12772 if (win.AnalyticsWebInterface) { 12773 // Call Android interface 12774 if (typeof hitType === "string") { 12775 win.AnalyticsWebInterface.logEvent(command, hitType, eventCategory, eventAction, eventLabel, eventValue, JSON.stringify(fieldsObject)); 12776 } 12777 else { 12778 win.AnalyticsWebInterface.logEvent(command, null, null, null, null, null, JSON.stringify(fieldsObject)); 12779 } 12780 } 12781 else if (win.webkit 12782 && win.webkit.messageHandlers 12783 && win.webkit.messageHandlers.firebase) { 12784 // Call iOS interface 12785 if (typeof hitType === "string") { 12786 var message = { 12787 command: command, 12788 hitType: hitType, 12789 eventCategory: eventCategory, 12790 eventAction: eventAction, 12791 eventLabel: eventLabel, 12792 eventValue: eventValue, 12793 fieldsObject: fieldsObject 12794 }; 12795 win.webkit.messageHandlers.firebase.postMessage(JSON.stringify(message)); 12796 } 12797 else { 12798 var altMessage = { 12799 command: command, 12800 fieldsObject: hitType 12801 }; 12802 win.webkit.messageHandlers.firebase.postMessage(JSON.stringify(altMessage)); 12803 } 12804 } 12805 else { 12806 ga(command, hitType, eventCategory, eventAction, eventLabel, eventValue, fieldsObject); 12807 console.log(ga.q); 12808 } 12809 }; 12810 GAFirebaseAnalytics.prototype.push = function (gtmParams) { 12811 var win = window; 12812 if (win.AnalyticsWebInterface) { 12813 // Call Android interface 12814 win.AnalyticsWebInterface.push(JSON.stringify(gtmParams)); 12815 } 12816 else if (win.webkit 12817 && win.webkit.messageHandlers 12818 && win.webkit.messageHandlers.firebase) { 12819 win.webkit.messageHandlers.firebase.postMessage(JSON.stringify(gtmParams)); 12820 } 12821 else { 12822 if (win.dataLayer !== undefined) { 12823 win.dataLayer.push(gtmParams); 12824 } 12825 } 12826 }; 12827 GAFirebaseAnalytics.prototype.updateFirebasePushNotificationIdentifier = function (firebasePushNotificationIdentifier) { 12828 var win = window; 12829 if (win.AnalyticsWebInterface) { 12830 // Call Android interface 12831 win.AnalyticsWebInterface.updateFirebasePushNotificationIdentifier(firebasePushNotificationIdentifier); 12832 } 12833 else if (win.webkit 12834 && win.webkit.messageHandlers 12835 && win.webkit.messageHandlers.firebase) { 12836 var message = { 12837 pushNotificationIdentifier: firebasePushNotificationIdentifier 12838 }; 12839 win.webkit.messageHandlers.firebase.postMessage(JSON.stringify(message)); 12840 } 12841 else { 12842 } 12843 }; 12844 GAFirebaseAnalytics.prototype.updateLoginStatus = function (loginstatus) { 12845 var win = window; 12846 if (win.AnalyticsWebInterface) { 12847 // Call Android interface 12848 win.AnalyticsWebInterface.updateLoginStatus(loginstatus); 12849 } 12850 else if (win.webkit 12851 && win.webkit.messageHandlers 12852 && win.webkit.messageHandlers.firebase) { 12853 var message = { 12854 LoginStatus: loginstatus 12855 }; 12856 win.webkit.messageHandlers.firebase.postMessage(JSON.stringify(message)); 12857 } 12858 else { 12859 } 12860 };
12861 GAFirebaseAnalytics.prototype.exponeaTrack = function (exponea, eventname, parameters) { 12862 var win = window; 12863 if (win.AnalyticsWebInterface) { 12864 // Call Android interface 12865 win.AnalyticsWebInterface.track(eventname, parameters); 12866 } 12867 else if (win.webkit 12868 && win.webkit.messageHandlers 12869 && win.webkit.messageHandlers.exponea) { 12870 var message = { 12871 eventName: eventname, 12872 eventParameters: parameters 12873 }; 12874 win.webkit.messageHandlers.exponea.postMessage(JSON.stringify(message)); 12875 } 12876 else { 12877 if (exponea !== undefined) { 12878 exponea.track(eventname, parameters); 12879 } 12880 } 12881 }; 12882 GAFirebaseAnalytics.prototype.exponeaIdentify = function (exponea, identifier) { 12883 var win = window; 12884 if (win.AnalyticsWebInterface) { 12885 // Call Android interface 12886 win.AnalyticsWebInterface.identify(identifier); 12887 } 12888 else if (win.webkit 12889 && win.webkit.messageHandlers 12890 && win.webkit.messageHandlers.exponea) { 12891 var message = { 12892 eventName: 'identify', 12893 eventParameters: identifier 12894 }; 12895 win.webkit.messageHandlers.exponea.postMessage(JSON.stringify(message)); 12896 } 12897 else if (exponea !== undefined) { 12898 exponea.identify('registered', identifier); 12899 } 12900 }; 12901 GAFirebaseAnalytics.prototype.transferWebEventToApp = function (eventName, messageContent) { 12902 var win = window; 12903 if (win.AnalyticsWebInterface && win.AnalyticsWebInterface.transferWebEventToApp) { 12904 // Call Android interface 12905 win.AnalyticsWebInterface.transferWebEventToApp(eventName, JSON.stringify(messageContent)); 12906 } 12907 else if (win.webkit 12908 && win.webkit.messageHandlers 12909 && win.webkit.messageHandlers.webEventBridge) { 12910 var message = { 12911 eventName: eventName, 12912 messageContent: JSON.stringify(messageContent) 12913 }; 12914 win.webkit.messageHandlers.webEventBridge.postMessage(message); 12915 } 12916 }; 12917 return GAFirebaseAnalytics; 12918 }()); 12919 GAFirebase.GAFirebaseAnalytics = GAFirebaseAnalytics; 12920})(GAFirebase || (GAFirebase = {})); 12921var firebase = new GAFirebase.GAFirebaseAnalytics(); 12922function firebaseEvent(event, category, id, name, value) { 12923 if (value === void 0) { value = 0; } 12924 if (firebase != undefined) { 12925 firebase.push({ 12926 "event": event, 12927 "link_category": category, 12928 "link_id": id, 12929 "link_name": name, 12930 "link_value": value 12931 }); 12932 } 12933} 12934//# sourceMappingURL=GAFirebase.js.map;
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.