1var wishlist = { 2 data: [], 3 isLoaded: false, 4 isShared: false, 5 ownerName: null, 6 shareUrl: null, 7 8 init: function() { 9 // Check if viewing a shared wishlist 10 var urlParams = new URLSearchParams(window.location.search); 11 var sharedToken = urlParams.get('shared'); 12 13 if (sharedToken) { 14 // Load shared wishlist (no auth required) 15 wishlist.loadShared(sharedToken); 16 } else if (user && user.data && user.data.memberId) { 17 // Load user's own wishlist 18 wishlist.load(); 19 } 20 }, 21 22 load: function(callback) { 23 service.call("wishlist", {}, function(response) { 24 if (response.error) { 25 wishlist.data = []; 26 wishlist.isLoaded = true; 27 } else { 28 wishlist.data = response.wishlist || []; 29 wishlist.isLoaded = true; 30 wishlist.isShared = false; 31 } 32 if (callback) callback(wishlist.data); 33 context.set("wishlist", wishlist.data); 34 }); 35 }, 36 37 loadShared: function(token, callback) { 38 // Load someone else's wishlist via share token 39 $.ajax({
40 url: '/api/wishlist/shared?shared=' + encodeURIComponent(token), 41 type: 'GET', 42 success: function(response) { 43 if (response.error) { 44 growler.error(response.error); 45 wishlist.data = []; 46 wishlist.isLoaded = true; 47 wishlist.isShared = false; 48 } else { 49 wishlist.data = response.wishlist || []; 50 wishlist.isLoaded = true; 51 wishlist.isShared = true; 52 wishlist.ownerName = response.ownerName; 53 } 54 if (callback) callback(wishlist.data); 55 }, 56 error: function() { 57 growler.error("Failed to load shared wishlist"); 58 wishlist.data = []; 59 wishlist.isLoaded = true; 60 if (callback) callback([]); 61 } 62 }); 63 }, 64 65 getShareUrl: function(callback) { 66 // Get share URL for current user's wishlist 67 $.ajax({
68 url: '/api/wishlist/share', 69 type: 'GET', 70 success: function(response) { 71 if (response.error) { 72 growler.error(response.error); 73 if (callback) callback(null); 74 } else { 75 wishlist.shareUrl = response.shareUrl; 76 if (callback) callback(response.shareUrl); 77 } 78 }, 79 error: function() { 80 growler.error("Failed to generate share link"); 81 if (callback) callback(null); 82 } 83 }); 84 }, 85 86 toggle: function(productId, callback) { 87 service.call("wishlist/toggle", { productId: productId }, function(response) { 88 if (response.error) { 89 if (callback) callback({ error: response.error }); 90 } else { 91 // Update local data based on server response 92 if (response.isAdded) { 93 wishlist.addToLocal(productId); 94 95 // Send Google Analytics event for add to wishlist 96 wishlist.sendGtagEvent('add_to_wishlist', productId, response.product); 97 98 // Send Facebook Pixel event for add to wishlist 99 wishlist.sendFbqEvent('AddToWishlist', response.product); 100 101 // Send Blueshift event for add to wishlist 102 if (window.blueshift != null && response.product) { 103 var imageUrl = ''; 104 if (response.product.url) { 105 var imageParts = response.product.url.split('/'); 106 imageUrl = 'https://' + window.location.hostname + '/images/300/' + imageParts[imageParts.length - 1]; 107 } 108 109 var blueshiftData = { 110 product_id: response.product.sku || productId, 111 sku: response.product.sku, 112 title: response.product.name, 113 price: response.product.cost, 114 brand: response.product.brandName, 115 image: imageUrl, 116 category: response.product.categories, 117 in_stock: response.product.inStock || false, 118 stock_quantity: (response.product.stock || 0) + (response.product.relayCount || 0) 119 }; 120 121 // Add color variant if available 122 if (response.product.color) { 123 blueshiftData.color = response.product.color; 124 } 125 126 // Add user email if available 127 if (user && user.data && user.data.emailAddress) { 128 blueshiftData.email = user.data.emailAddress; 129 } 130 131 blueshift.track("add_to_wishlist", blueshiftData); 132 } 133 } else { 134 wishlist.removeFromLocal(productId); 135 136 // Send Google Analytics event for remove from wishlist 137 wishlist.sendGtagEvent('remove_from_wishlist', productId, response.product); 138 139 // Send Facebook Pixel event for remove from wishlist (custom event) 140 wishlist.sendFbqEvent('RemoveFromWishlist', response.product); 141 142 // Send Blueshift event for remove from wishlist 143 if (window.blueshift != null && response.product) { 144 var imageUrl = ''; 145 if (response.product.url) { 146 var imageParts = response.product.url.split('/'); 147 imageUrl = 'https://' + window.location.hostname + '/images/300/' + imageParts[imageParts.length - 1]; 148 } 149 150 var blueshiftData = { 151 product_id: response.product.sku || productId, 152 sku: response.product.sku, 153 title: response.product.name, 154 price: response.product.cost, 155 brand: response.product.brandName, 156 image: imageUrl, 157 category: response.product.categories, 158 in_stock: response.product.inStock || false, 159 stock_quantity: (response.product.stock || 0) + (response.product.relayCount || 0) 160 }; 161 162 // Add color variant if available 163 if (response.product.color) { 164 blueshiftData.color = response.product.color; 165 } 166 167 // Add user email if available 168 if (user && user.data && user.data.emailAddress) { 169 blueshiftData.email = user.data.emailAddress; 170 } 171 172 blueshift.track("remove_from_wishlist", blueshiftData); 173 } 174 } 175 176 context.set("wishlist", wishlist.data); 177 if (callback) callback({ 178 success: true, 179 isAdded: response.isAdded, 180 count: response.count 181 }); 182 } 183 }); 184 }, 185 186 remove: function(productId, callback) { 187 service.call("wishlist/toggle", { productId: productId }, function(response) { 188 if (response.error) { 189 if (callback) callback({ error: response.error }); 190 } else { 191 wishlist.data = wishlist.removeFromLocal(productId); 192 context.set("wishlist", wishlist.data); 193 if (callback) callback({ 194 success: true, 195 count: response.count 196 }); 197 } 198 }); 199 }, 200 201 has: function(productId) { 202 return wishlist.data.indexOf(parseInt(productId)) !== -1; 203 }, 204 205 count: function() { 206 return wishlist.data.length; 207 }, 208 209 addToLocal: function(productId) { 210 var id = parseInt(productId); 211 if (wishlist.data.indexOf(id) === -1) { 212 wishlist.data.push(id); 213 } 214 return wishlist.data; 215 }, 216 217 removeFromLocal: function(productId) { 218 var id = parseInt(productId); 219 var index = wishlist.data.indexOf(id); 220 if (index !== -1) { 221 wishlist.data.splice(index, 1); 222 } 223 return wishlist.data; 224 }, 225 226 sendGtagEvent: function(eventName, productId, product) { 227 // Only send if trackEvent is available 228 if (typeof trackEvent === 'undefined') { 229 return; 230 } 231 232 // Try to get product details from current context if not provided 233 if (!product) { 234 // Check if we have product data in global scope 235 if (typeof page !== 'undefined' && page.product && page.product.productId == productId) { 236 product = page.product; 237 } 238 // Check if we're on a product list page 239 else if (typeof products !== 'undefined' && products.data) { 240 for (var i = 0; i < products.data.length; i++) { 241 if (products.data[i].productId == productId) { 242 product = products.data[i]; 243 break; 244 } 245 } 246 } 247 } 248 249 // If we found product details, send enhanced event 250 if (product) { 251 var gtagItem = { 252 item_id: product.productId, 253 item_name: product.name, 254 item_brand: product.brandName || '', 255 item_category: product.categories && product.categories[0] ? product.categories[0].name : '', 256 price: product.price || product.cost || 0, 257 quantity: 1 258 }; 259 260 // Add variant if color is available 261 if (product.color) { 262 gtagItem.item_variant = product.color; 263 } 264 265 // Add item list context if available (from page context) 266 if (typeof page !== 'undefined' && page.listName) { 267 gtagItem.item_list_name = page.listName; 268 gtagItem.item_list_id = page.listId || page.listName; 269 } 270
271 var eventParams = { 272 currency: 'USD', 273 value: product.price || product.cost || 0, 274 items: [gtagItem] 275 }; 276 277 // Add user ID if available 278 if (user && user.data && user.data.memberId) { 279 eventParams.user_id = user.data.memberId.toString(); 280 } 281 282 trackEvent(eventName, eventParams); 283 } else { 284 // Send basic event without product details 285 trackEvent(eventName, { 286 item_id: productId 287 }); 288 } 289 }, 290 291 sendFbqEvent: function(eventName, product) { 292 // Only send if fbq is available (Facebook Pixel is loaded) 293 if (typeof fbq === 'undefined') { 294 return; 295 } 296 297 try { 298 if (product) { 299 var fbData = { 300 content_name: product.name, 301 content_category: product.categories && product.categories[0] ? product.categories[0].name || product.categories[0] : 'All Categories', 302 content_ids: [product.sku || product.productId], 303 content_type: 'product', 304 value: product.cost || product.price || 0, 305 currency: 'USD' 306 }; 307 308 // Add contents array with detailed product info 309 fbData.contents = [{ 310 id: product.sku || product.productId, 311 quantity: 1, 312 item_price: product.cost || product.price || 0 313 }]; 314 315 fbq('track', eventName, fbData); 316 } else { 317 // Send basic event without product details 318 fbq('track', eventName); 319 } 320 } catch (e) { 321 console.error('Facebook Pixel error:', e); 322 } 323 } 324}; 325 326// Auto-initialize when user data is available 327if (typeof context !== 'undefined') { 328 context.addListener("user", function() { 329 if (user && user.data && user.data.memberId && !wishlist.isLoaded) { 330 wishlist.load(); 331 } 332 }); 333}
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.