const script_version = 'v4' var xmlhttp = new XMLHttpRequest(); const url2 = 'https://marketing-api3.shutterfly.com/mktg/beacon-pixel/posts-aurora' const cookies_logic = [{ 'condition': () => {return dataLayer[1]?.event === "view_item"}, 'key': 'is_pip_visited', 'value': 'true', 'exp': 5, }]; // Prepare request xmlhttp.open("POST", url2, true); xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xmlhttp.setRequestHeader("Web-Pixel-Version", 'v3'); xmlhttp.setRequestHeader("Script-Pixel-Version", script_version); xmlhttp.setRequestHeader("Sfly-Web-Domain", document.domain); xmlhttp.setRequestHeader("Sfly-Web-Uri", document.documentURI); xmlhttp.setRequestHeader("Sfly-Web-Title", document.title); let pixel_browsing_uid = "undefined"; if (typeof(Storage) !== "undefined") { pixel_browsing_uid = window.localStorage.getItem("Sfly-Browsing-Uid"); } if (pixel_browsing_uid !== "undefined"){ xmlhttp.setRequestHeader("Sfly-Browsing-Uid", pixel_browsing_uid); } xmlhttp.withCredentials = true; xmlhttp.timeout = 30000; // 30 seconds // Copy Master Tms Udo data if (typeof MasterTmsUdo !== 'undefined' && MasterTmsUdo != null) { pixel_data = JSON.parse(JSON.stringify(MasterTmsUdo)); } else { pixel_data = {}; } // Create postProspectRequest method postProspectRequest = function postProspectReq(data, target, callback) { let prefix_url = ''; if (window.location.href.includes(".dev.")) { prefix_url = 'https://beacon-pixel.stage.shutterfly.com/'; } else if (window.location.href.includes(".stage.")) { prefix_url = 'https://beacon-pixel.stage.shutterfly.com/'; } else if (window.location.href.includes(".beta.")) { prefix_url = 'https://beacon-pixel.stage.shutterfly.com/'; } else if (window.location.href.includes(".kappa.")) { prefix_url = 'https://beacon-pixel.stage.shutterfly.com/'; } else /* if prod */ { prefix_url = 'https://beacon-pixel.internal.shutterfly.com/'; } let httpReq = new XMLHttpRequest(); let url3 = prefix_url + 'posts-prospect'; if (target) { data['target'] = target } if (callback) { httpReq.onreadystatechange = callback; } httpReq.open("POST", url3, true); httpReq.send(JSON.stringify(data)); } // See pixel_tag_v3_readme.md for design goals and detailed behaviour. function __build_pixel_page_data(dataLayer) { function getCookieValue(name) { try { if (typeof document === 'undefined' || !document.cookie) { return ''; } var match = document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)'); return match ? match.pop() : ''; } catch (e) { return ''; } } var MARKETING_PARAMS = { 'gclid':1,'gclsrc':1,'gad_campaignid':1,'gad_source':1,'gbraid':1,'wbraid':1,'dclid':1,'gid':1,'hl':1,'gl':1, 'fbclid':1,'msclkid':1,'msockid':1,'ttclid':1,'epik':1,'rdt_cid':1,'rtbhc':1, 'cjevent':1,'cjdata':1,'publisher':1, 'clickid':1,'programid':1,'ranmid':1,'raneaid':1,'ransiteid':1, '$3p':1,'$deeplink_path':1,'is_retargeting':1,'deep_link_value':1,'af_reengagement_window':1, 'af_click_lookback':1,'af_r':1,'af_xp':1,'drive_engagement':1,'conversion':1, 'mobile-device-model-code':1,'shortlink':1, '_branch_match_id':1,'_branch_referrer':1,'et_rid':1,'speedsize':1, 'icid':1,'esch':1,'rilt':1,'mpch':1,'cid':1,'uid':1,'sid':1,'pid':1,'aid':1,'otsc':1, 'source':1,'campaignname':1,'partner':1,'channel':1,'interceptsource':1,'lid':1, 'ad_placement_id':1,'categorycode':1,'cd':1,'ct':1,'tnpip':1,'ump':1,'test':1, 'searchterm':1,'recommendedterm':1,'ueh':1,'referrer':1,'referer':1,'ref':1,'refid':1,'rid':1, 'rtype':1,'olref':1,'oppref':1,'spid':1,'spvid':1,'feeds':1,'ibx_source':1,'externalid':1, 'company':1,'lang':1,'keyword':1,'job-id':1 }; function getMarketingParams() { var out = {}; try { if (typeof window === 'undefined' || !window.location || !window.location.search) { return out; } var pairs = window.location.search.replace(/^\?/, '').split('&'); for (var i = 0; i < pairs.length; i++) { if (!pairs[i]) { continue; } var eq = pairs[i].indexOf('='); var key = eq >= 0 ? pairs[i].slice(0, eq) : pairs[i]; if (!MARKETING_PARAMS[key.toLowerCase()]) { continue; } var rawVal = eq >= 0 ? pairs[i].slice(eq + 1) : ''; try { out[key] = decodeURIComponent(rawVal.replace(/\+/g, ' ')); } catch (e) { out[key] = rawVal; } } } catch (e) {} return out; } function getUtmParams() { var utm = {}; try { if (typeof window === 'undefined' || !window.location || !window.location.search) { return utm; } var pairs = window.location.search.replace(/^\?/, '').split('&'); for (var i = 0; i < pairs.length; i++) { if (!pairs[i]) { continue; } var eq = pairs[i].indexOf('='); var key = eq >= 0 ? pairs[i].slice(0, eq) : pairs[i]; if (key.toLowerCase().indexOf('utm') !== 0) { continue; } var rawVal = eq >= 0 ? pairs[i].slice(eq + 1) : ''; try { utm[key] = decodeURIComponent(rawVal.replace(/\+/g, ' ')); } catch (e) { utm[key] = rawVal; } } } catch (e) {} return utm; } var pageData = { sfly_fpid: '', user_agent: '', referer: '', page_url: '', data_layer: null }; try { pageData.sfly_fpid = getCookieValue('sfly_fpid') || getCookieValue('sfly_fpid_stage'); if (typeof navigator !== 'undefined' && navigator && navigator.userAgent) { pageData.user_agent = navigator.userAgent; } if (typeof document !== 'undefined' && document) { pageData.referer = document.referrer || ''; } if (typeof window !== 'undefined' && window.location && window.location.pathname) { pageData.page_url = window.location.pathname + (window.location.search || ''); } var utmParams = getUtmParams(); if (Object.keys(utmParams).length > 0) { pageData.utm = utmParams; } var marketingParams = getMarketingParams(); if (Object.keys(marketingParams).length > 0) { pageData.marketing = marketingParams; } if (Object.prototype.toString.call(dataLayer) === '[object Array]') { var keys = ['ecommerce', 'user', 'page', 'session']; var merged = null; var bestIdForKey = { ecommerce: -Infinity, user: -Infinity, page: -Infinity, session: -Infinity }; var topEl = null; var topId = -Infinity; var purchaseEl = null; var bestPurchaseId = -Infinity; for (var i = 0; i < dataLayer.length; i++) { var el = dataLayer[i]; if (!el || typeof el !== 'object') { continue; } if (el.event === null || el.event === undefined) { continue; } var id = (typeof el['gtm.uniqueEventId'] === 'number') ? el['gtm.uniqueEventId'] : -1; var contributed = false; if (typeof el.event === 'string' && el.event.toLowerCase() === 'purchase' && el.ecommerce != null && id > bestPurchaseId) { bestPurchaseId = id; purchaseEl = el; } for (var k = 0; k < keys.length; k++) { var key = keys[k]; if (el[key] != null) { contributed = true; if (id > bestIdForKey[key]) { if (merged === null) { merged = {}; } merged[key] = el[key]; bestIdForKey[key] = id; } } } if (contributed && id > topId) { topId = id; topEl = el; } } if (merged !== null && topEl !== null) { merged.event = topEl.event; merged['gtm.uniqueEventId'] = topEl['gtm.uniqueEventId']; } if (purchaseEl !== null) { if (merged === null) { merged = {}; } merged.ecommerce = purchaseEl.ecommerce; merged.event = purchaseEl.event; merged['gtm.uniqueEventId'] = purchaseEl['gtm.uniqueEventId']; } // Decide whether to keep the full items array (purchase / order // confirmation) or fall back to the default items[0] truncation. var keepAllItems = false; try { var pageUrl = (typeof window !== 'undefined' && window.location && window.location.href) ? String(window.location.href).toLowerCase() : ''; var isOrderConfirmation = pageUrl.indexOf('cart/orderconfirmation') !== -1; var isPurchase = merged !== null && typeof merged.event === 'string' && merged.event.toLowerCase() === 'purchase' && merged.ecommerce != null; keepAllItems = isOrderConfirmation || isPurchase; } catch (e) {} if (merged !== null && merged.ecommerce && Object.prototype.toString.call(merged.ecommerce.items) === '[object Array]' && merged.ecommerce.items.length > 0) { if (!keepAllItems) { var ecommerceCopy = {}; for (var prop in merged.ecommerce) { if (Object.prototype.hasOwnProperty.call(merged.ecommerce, prop)) { ecommerceCopy[prop] = merged.ecommerce[prop]; } } ecommerceCopy.items = [merged.ecommerce.items[0]]; merged.ecommerce = ecommerceCopy; } // keepAllItems === true -> leave merged.ecommerce untouched (full items array) } pageData.data_layer = merged; } } catch (e) {} return pageData; } // Browser/URL context: captured now (page-load), not deferred to send time. A SPA that // rewrites or strips the query string before the 2s send would otherwise silently drop // this. dataLayer-derived content (identity/user/product fields, ecommerce merge) is // captured separately in __capture_data_layer_fields(), called at send time — see // pixel_tag_v3_readme.md § Payload build timing. try { pixel_data.escapedPagename = window.location.pathname || "unknown page name"; pixel_data.pageName = window.location.pathname || ''; pixel_data.referrer = window.location.href || ''; pixel_data.page_data = __build_pixel_page_data(dataLayer); } catch (ex_var) { console.warn('error copying fields from SFLY3_DTL'); } // Captures everything derived from the GTM dataLayer. Called at send time (just before // xmlhttp.send, after the 2000ms delay) rather than at script-parse time, so it reflects // events GTM pushes after the tag executes (e.g. a `purchase` event on order // confirmation) instead of a stale snapshot. See pixel_tag_v3_readme.md § Payload build // timing and § Known issue: purchase snapshot race (fixed). function __capture_data_layer_fields() { try { // Main-payload identity: read from dataLayer[0].user with the legacy guest gate. customer_id = dataLayer[0]?.user?.customer_id; login_method = dataLayer[0]?.user?.login_method; if (customer_id && login_method !== 'guest') { pixel_data.uid = customer_id; pixel_data.userId = customer_id; pixel_data.visitorId = customer_id; } // Enriched user model: prefer the user object carried by the most-recent `user_load` // event (not guaranteed at dataLayer[0]); fall back to dataLayer[0].user. let user_obj = null; if (Object.prototype.toString.call(dataLayer) === '[object Array]') { for (let ui = 0; ui < dataLayer.length; ui++) { const uel = dataLayer[ui]; if (uel && typeof uel === 'object' && typeof uel.event === 'string' && uel.event.toLowerCase() === 'user_load' && uel.user != null) { user_obj = uel.user; // last match wins -> most recent user_load } } } if (user_obj == null) { user_obj = dataLayer[0]?.user; } if (user_obj != null) { pixel_data.user = user_obj; // send the full user model as-is } pixel_data.categoryCode = dataLayer[1]?.ecommerce?.item_list_id; pixel_data.pageType = dataLayer[1]?.event; pixel_data.productId = '' if (dataLayer[1]?.event === 'view_item'){ pixel_data.productId = dataLayer[1]?.ecommerce?.items[0]?.item_productCode; } pixel_data.productName = dataLayer[1]?.ecommerce?.items[0]?.item_name; pixel_data.spId = dataLayer[1]?.ecommerce?.items[0]?.spId || ''; pixel_data.spvId = dataLayer[1]?.ecommerce?.items[0]?.spvId || ''; pixel_data.skuCode = dataLayer[1]?.ecommerce?.items[0]?.item_sku; pixel_data.page_category = dataLayer[1]?.ecommerce?.items[0]?.item_category; pixel_data.productType = dataLayer[1]?.ecommerce?.items[0]?.item_category3; pixel_data.thumbnail_url = dataLayer[1]?.ecommerce?.items[0]?.item_thumbnail_url || ''; // Refresh the dataLayer merge (ecommerce/user/page/session) against the current // state of dataLayer, so a `purchase` event pushed after page-load is captured. // __build_pixel_page_data is called again (kept SELF-CONTAINED per its own // design goal) and only its data_layer result is taken here. var refreshed = __build_pixel_page_data(dataLayer); if (pixel_data.page_data) { pixel_data.page_data.data_layer = refreshed.data_layer; } else { pixel_data.page_data = refreshed; } } catch (ex_var) { console.warn('error copying fields from SFLY3_DTL'); } } function setCookie(cname, cvalue, exdays) { const d = new Date(); d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); let expires = "expires=" + d.toUTCString(); document.cookie = cname + "=" + cvalue + ";" + expires + ";SameSite=None; secure; path=/"; if (typeof(Storage) !== "undefined") { window.localStorage.setItem(cname, cvalue); } } xmlhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { try { var cookie_name = 'Sfly-Browsing-Uid' let ret_cookie = xmlhttp.getResponseHeader(cookie_name); if (ret_cookie) { setCookie(cookie_name, ret_cookie, 730); for (let i=0;i < cookies_logic.length;i++) { if (cookies_logic[i].condition()) { setCookie(cookies_logic[i].key, cookies_logic[i].value, cookies_logic[i].exp); } } } else { console.warn('beacon-pixel: ' + cookie_name + ' cant be found ' + ret_cookie); } } catch (ex_var) { console.warn('beacon-pixel: Failed to set ' + cookie_name + '.'); } } }; // send request setTimeout(function () { __capture_data_layer_fields(); xmlhttp.send(JSON.stringify(pixel_data)); }, 2000); //v2022-08-22v2