1var products = { 2 id: -1, 3 searchId: "", 4 product: {}, 5 popularItems: [], 6 data: [], 7 results: [], 8 brand: -1, 9 category: -1, 10 options: null, 11 total: 0, 12 order: ['xxs', 'xs', 's', 'm', 'l', 'xl', 'xxl'], 13 filter: { 14 brand: -1, 15 category: -1, 16 collection: -1, 17 search: "", 18 grouped: false, 19 size: 50, 20 cost: -1, 21 page: 1, 22 stock: -1, 23 complete: -1, 24 live: -1, 25 sort: "name", 26 sortType: "asc", 27 admin: false 28 }, 29 all: function() { 30 products.filter.stock = 1; 31 products.filter.live = 1; 32 products.filter.size = 10000; 33 products.filter.status = "complete" 34 35 service.call("products", products.filter, products.allReturned); 36 }, 37 allReturned: function(e) { 38 $(".loader").removeClass("open"); 39 if (e.error) growler.error(e.error); 40 else { 41 products.data = e.data; 42 products.total = e.total; 43 context.set("allproducts", products.data); 44 } 45 }, 46 alerts: function() { 47 products.filter.stock = 0; 48 products.filter.cost = 0; 49 products.filter.live = 1; 50 service.call("products", products.filter, products.getReturned); 51 }, 52 getSizes: function() { 53 service.call("product/sizes", {}, function(e) { 54 context.set("sizes", e); 55 }); 56 }, 57 getWidths: function() { 58 service.call("product/widths", {}, function(e) { 59 context.set("widths", e); 60 }); 61 }, 62 popular: function(sort) { 63 service.call("popular", { sort: sort }, products.popularReturned); 64 }, 65 popularReturned: function(e) { 66 if (e.error) growler.error(e.error); 67 else { 68 products.popularItems = e; 69 context.set("popular", products.popularItems); 70 } 71 }, 72 get: function() { 73 if (products.filter.admin!=null&&products.filter.admin==true) { 74 service.call("products", products.filter, products.getReturned); 75 } else { 76 service.call("products-search", products.filter, products.getReturned); 77 } 78 }, 79 getReturned: function(e) { 80 $(".loader").removeClass("open"); 81 if (e.error) { 82 growler.error(e.error); 83 products.data = []; 84 products.total = 0; 85 var contextId = "products"+products.searchId; 86 if (products.filter.admin!=null&&products.filter.admin==true) contextId = "admin"+contextId; 87 context.set(contextId, products.data); 88 } else { 89 products.data = e.data; 90 products.total = e.total; 91 var contextId = "products"+products.searchId; 92 if (products.filter.admin!=null&&products.filter.admin==true) contextId = "admin"+contextId; 93 context.set(contextId, products.data); 94 } 95 }, 96 searchView: function(filter) { 97 if (filter.trim().length>0) { 98 service.call("product/view/search", {search:filter}, () => {}); 99 } 100 }, 101 productView: function(id) { 102 service.call("productView", {id:id}, () => {}); 103 }, 104 details: function(id) { 105 service.call("product", { id: id }, products.detailsReturned); 106 }, 107 detailsReturned: function(e) { 108 if (e.error) growler.error(e.error); 109 else { 110 products.product = e; 111 112 if (page.productDetails) page.productDetails(products.product); 113 else context.set("product", products.product); 114 } 115 }, 116 import: function(sku) { 117 service.call("import", { sku: sku }, products.detailsReturned); 118 }, 119 add: function(sku, name, description, cost, brand, color, shipping, categories, inStorePurchase, inStorePickup, showOnSite, autoPrice, isFeatured, isOnSale, inventory, images) { 120 var params = products.filter; 121 params.sku = sku; 122 params.name = name; 123 params.description = description; 124 params.cost = cost; 125 params.brand = brand; 126 params.color = color; 127 params.shipping = shipping; 128 params.categories = categories; 129 params.inStorePurchase = inStorePurchase; 130 params.inStorePickup = inStorePickup; 131 params.showOnSite = showOnSite; 132 params.autoPrice = autoPrice; 133 params.isFeatured = isFeatured; 134 params.isOnSale = isOnSale; 135 params.inventory = inventory; 136 params.images = images; 137 service.call("product/add", params, products.getReturned); 138 }, 139 save: function(id, name, description, blurb, cost, shipping, categories, collections, images, isInStoreOnly, isInStorePurchaseOnly, isLive, isAuto, isOnSale, isFeatured, isPrimary, color, brand, metaTitle, metaDescription, metaTags, metaAlt,relayStatus,dateReleased,adminUpdate) { 140 var params = { 141 name: name, 142 description: description, 143 blurb: blurb, 144 cost: cost, 145 shipping: shipping, 146 brand: brand, 147 color: color, 148 id: id, 149 categories: categories, 150 collections: collections, 151 images: images, 152 isInStoreOnly: isInStoreOnly, 153 isInStorePurchaseOnly: isInStorePurchaseOnly, 154 isLive: isLive, 155 isAuto: isAuto, 156 isOnSale: isOnSale, 157 isFeatured: isFeatured, 158 isPrimary: isPrimary, 159 metaTitle: metaTitle, 160 metaDescription: metaDescription, 161 metaTags: metaTags, 162 metaAlt: metaAlt, 163 relayStatus:relayStatus, 164 dateReleased:dateReleased, 165 adminUpdate: adminUpdate 166 }; 167 service.call("productSave", params, products.saveComplete); 168 }, 169 getSize: function(size,url) { 170 var items = url.split('/'); 171 var multiplier = 1; 172 if (window.devicePixelRatio) { 173 multiplier = window.devicePixelRatio; 174 } 175 if (url.indexOf("http")==0) return url; 176 else { 177 if (cdnOn!=null && cdnOn) return products.getSized(size,url); 178 else return "/images/"+(size*multiplier).toFixed(0)+"/"+items[items.length-1]; 179 } 180 }, 181 getSized: function(size,url) { 182 var items = url.split('/'); 183 var multiplier = 1; 184 if (window.devicePixelRatio) { 185 multiplier = window.devicePixelRatio; 186 } 187 if (url.indexOf("http")==0) return url; 188 else { 189 let cdnUrl = "https://cdn.runfree.run/images/products/"+products.getBucket(url)+"/"+(size*multiplier).toFixed(0)+"/"+items[items.length-1]; 190 return cdnUrl; 191 } 192 }, 193 getBucket: function(url) { 194 var items = url.split('/'); 195 let fileName = items[items.length-1]; 196 let bucket = ""; 197 if (url.indexOf("/products/")>0) { 198 var items = fileName.split('_'); 199 if (items.length>2) { 200 bucket = items[0]; 201 } else { 202 bucket = "sync"; 203 } 204 } else if (url.indexOf("/meta/")>0) { 205 bucket = "meta"; 206 } 207 return bucket; 208 }, 209 saveComplete: function(e) { 210 $(".loader").removeClass("open"); 211 growler.success("Product Information has been saved"); 212 if (page && page.get) page.get(); 213 }, 214 saveMultiInStorePurchase: function(ids, inStorePurchase) { 215 var params = Object.assign({}, products.filter); 216 params.ids = ids; 217 params.inStorePurchase = inStorePurchase; 218 service.call("multiInStorePurchase", params, products.getReturned); 219 }, 220 saveMultiInStorePickup: function(ids, inStorePickup) { 221 var params = Object.assign({}, products.filter); 222 params.ids = ids; 223 params.inStorePickup = inStorePickup; 224 service.call("multiInStorePickup", params, products.getReturned); 225 }, 226 saveMultiSaleToggle: function(ids, onSale) { 227 var params = Object.assign({}, products.filter); 228 params.ids = ids; 229 params.onSale = onSale; 230 service.call("multiSaleToggle", params, products.getReturned); 231 }, 232 addToCollection: function(ids, collectionId) { 233 var params = Object.assign({}, products.filter); 234 params.ids = ids; 235 params.collectionId = collectionId; 236 service.call("addToCollection", params, products.getReturned); 237 }, 238 saveMultiSale: function(ids, onSale, auto, price) { 239 var params = Object.assign({}, products.filter); 240 params.ids = ids; 241 params.onSale = onSale; 242 params.auto = auto; 243 params.price = price; 244 service.call("multiSale", params, products.getReturned); 245 }, 246 saveMultiAutoUpdate: function(ids, auto) { 247 var params = Object.assign({}, products.filter); 248 params.ids = ids; 249 params.auto = auto; 250 service.call("multiAutoUpdate", params, products.getReturned); 251 }, 252 saveMultiSalePrice: function(ids, price) { 253 var params = Object.assign({}, products.filter); 254 params.ids = ids; 255 params.price = price; 256 service.call("multiSalePrice", params, products.getReturned); 257 }, 258 saveMultiShipping: function(ids, templateId) { 259 var params = Object.assign({}, products.filter); 260 params.ids = ids; 261 params.shipping = templateId; 262 service.call("multiShipping", params, products.getReturned); 263 }, 264 saveMultiSeo: function(ids, metaTitle, metaTags, metaDescription, metaAlt) { 265 var params = Object.assign({}, products.filter); 266 params.ids = ids; 267 params.metaTitle = metaTitle; 268 params.metaTags = metaTags; 269 params.metaDescription = metaDescription; 270 params.metaAlt = metaAlt; 271 service.call("multiSeo", params, products.getReturned); 272 }, 273 saveSkus: function(ids, sku) { 274 var params = Object.assign({}, products.filter); 275 params.ids = ids; 276 params.sku = sku; 277 service.call("sku", params, products.getReturned); 278 }, 279 saveMultiDescs: function(ids, desc) { 280 var params = Object.assign({}, products.filter); 281 params.ids = ids; 282 params.desc = desc; 283 service.call("multiDescs", params, products.getReturned); 284 }, 285 saveMultiOffline: function(ids) { 286 var params = Object.assign({}, products.filter); 287 params.ids = ids; 288 service.call("multiOffline", params, products.getReturned); 289 }, 290 saveMultiOnline: function(ids) { 291 var params = Object.assign({}, products.filter); 292 params.ids = ids; 293 service.call("multiOnline", params, products.getReturned); 294 }, 295 metaSync: function(ids) { 296 var params = Object.assign({}, products.filter); 297 params.ids = ids; 298 service.call("sync/meta", params, products.getReturned); 299 }, 300 setBlurb: function(ids, value) { 301 var params = Object.assign({}, products.filter); 302 params.ids = ids; 303 params.blurb = value; 304 service.call("multiBlurbs", params, products.getReturned); 305 }, 306 setCategories: function(ids, catsToAdd, catsToRemove) { 307 var params = Object.assign({}, products.filter); 308 params.ids = ids; 309 params.catsToAdd = catsToAdd; 310 params.catsToRemove = catsToRemove; 311 service.call("productCats", params, products.getReturned); 312 }, 313 setNames: function(ids, name) { 314 var params = Object.assign({}, products.filter); 315 params.ids = ids; 316 params.name = name; 317 service.call("productNames", params, products.getReturned); 318 }, 319 appendNames: function(ids, prependValue, appendValue) { 320 var params = Object.assign({}, products.filter); 321 params.ids = ids; 322 params.prependValue = prependValue; 323 params.appendValue = appendValue; 324 service.call("appendNames", params, products.getReturned); 325 }, 326 setColors: function(ids, color) { 327 var params = Object.assign({}, products.filter); 328 params.ids = ids; 329 params.color = color;
330 service.call("productColors", params, products.getReturned); 331 }, 332 setRelayStauses: function(ids, status) { 333 var params = Object.assign({}, products.filter); 334 params.ids = ids; 335 params.status = status; 336 service.call("productRelayStatuses", params, products.getReturned); 337 }, 338 setFeatures: function(ids, featured) { 339 var params = Object.assign({}, products.filter); 340 params.ids = ids; 341 params.featured = featured; 342 service.call("productFeatures", params, products.getReturned); 343 }, 344 undeleteMulti: function(ids) { 345 var params = Object.assign({}, products.filter); 346 params.ids = ids; 347 params.operation = "un"; 348 service.call("products", params, products.deleteReturned, "DELETE"); 349 }, 350 deleteMulti: function(ids) { 351 var params = Object.assign({}, products.filter); 352 params.ids = ids; 353 params.operation = "delete"; 354 service.call("products", params, products.deleteReturned, "DELETE"); 355 }, 356 getCleaned: function(value) { 357 var toClear = [",", "'"]; 358 value = value.trim(); 359 value = value.toLowerCase(); 360 for (var i=0; i<toClear.length; i++) { 361 value = value.split(toClear[i]).join(''); 362 } 363 value = value.replace(/\W/g, ' '); 364 while (value.indexOf(" ")>=0) { 365 value = value.split(' ').join(' '); 366 } 367 value = value.split(' ').join('-'); 368 return value; 369 }, 370 getCanonical: function(product) { 371 var url = "/product/"+product.productId; 372 373 if (product.brandName) { 374 url += "/"+products.getCleaned(product.brandName); 375 } 376 377 url += "/"+products.getCleaned(product.name); 378 return url; 379 }, 380 delete: function(id) { 381 var params = Object.assign({}, products.filter); 382 params.id = id; 383 params.operation = "delete"; 384 service.call("product", params, products.deleteReturned, "DELET
384E"); 385 }, 386 undelete: function(id) { 387 var params = Object.assign({}, products.filter); 388 params.id = id; 389 params.operation = "un"; 390 service.call("product", params, products.deleteReturned, "DELETE"); 391 }, 392 deleteReturned: function(e) { 393 $(".loader").removeClass("open"); 394 if (e.error) growler.error(e.error); 395 else { 396 products.data = e.data; 397 products.total = e.total; 398 context.set("adminproducts", products.data); 399 } 400 }, 401 deleteImage: function(id) { 402 service.call("image", { id: id }, (e) => {}, "DELETE"); 403 }, 404 inventory: function(type, value) { 405 service.call("inventory", { type: type, value: value }, products.inventoryReturned); 406 }, 407 inventoryReturned: function(e) { 408 $(".loader").removeClass("open"); 409 if (e.error) growler.error(e.error); 410 else { 411 products.results = e; 412 context.set("ricsProduct", products.results); 413 } 414 }, 415 getOptions: function(sku, id, relay) { 416 service.call("options", { sku: sku, id: id, relay: relay }, products.storeOptionsReturned); 417 }, 418 getAllProductIds: function () { 419 return new Promise((resolve, reject) => { 420 const customFilter = JSON.parse(JSON.stringify(products.filter)); 421 customFilter.size = 10000; 422 // Query all products and strip out the Ids 423 service.call("products", customFilter, function (response) { 424 if (response.error) { 425 reject(response.error); 426 } else { 427 const productIds = response.data.map(product => product.productId); 428 resolve(productIds); 429 } 430 }); 431 }); 432 }, 433 storeOptionsReturned: function(e) { 434 if (e.error) growler.error(e.error); 435 else { 436 if (page.inventoryReturned) page.inventoryReturned(e); 437 else context.set("productinventory", e); 438 } 439 }, 440 getAdminOptions: function(sku, id, relay) { 441 service.call("adminoptions", { sku: sku, id: id, relay: relay }, products.storeAdminOptionsReturned); 442 }, 443 storeAdminOptionsReturned: function(e) { 444 if (e.error) growler.error(e.error); 445 else context.set("admininventory", e); 446 }, 447 refreshFromSource: function(sku) { 448 service.call("sync", { sku: sku }, products.storeAdminOptionsReturned); 449 }, 450 refreshDoc: function(id) { 451 service.call("sync/description", { id: id }, products.doRefresh); 452 }, 453 refreshPhoto: function(id) { 454 service.call("sync/images", { id: id }, products.doRefresh); 455 }, 456 doRefresh: function(e) { 457 location.reload(); 458 }, 459 sourceReturned: function(e) { 460 if (e.error) growler.error(e.error); 461 else context.set("source", e); 462 }, 463 refreshOptions: function(id) { 464 service.call("synconhand", { id: id }, products.optionsReturned); 465 }, 466 optionsReturned: function(e) { 467 if (e.error) growler.error(e.error); 468 else { 469 products.options = e; 470 for (var i=0; i<products.options.length; i++) { 471 if (!isNaN(Number(products.options[i].size.trim()))) { 472 products.options[i].osize = products.options[i].size; 473 products.options[i].size = Number(products.options[i].size).toFixed(1); 474 } 475 } 476 if (products.options.length>0) { 477 if (isNaN(Number(products.options[0].size))) { 478 products.options.sort((a, b) => (products.order.indexOf(a.size.toLowerCase()) > products.order.indexOf(b.size.toLowerCase())) ? 1 : -1); 479 } else { 480 products.options.sort((a, b) => (Number(a.size) > Number(b.size)) ? 1 : (Number(a.size) === Number(b.size)) ? ((a.sizeAppend > b.sizeAppend) ? 1 : -1) : -1 ); 481 } 482 } 483 context.set("options", products.options); 484 } 485 }, 486 next: function() { 487 if (!isLast()) products.filter.page++; 488 else products.filter.page = 1; 489 products.get(); 490 }, 491 prev: function() { 492 if (products.filter.page>1) products.filter.page--; 493 else products.filter.page = Math.ceil(products.total/products.filter.size); 494 products.get(); 495 }, 496 isLast: function() { 497 return (products.total<=(products.filter.page*products.filter.size)); 498 }, 499 isFirst: function() { 500 return products.filter.page==1; 501 }, 502 pageTotal: function() { 503 return Math.ceil(products.total/products.filter.size); 504 }, 505 goto: function(page) { 506 //if (page<=products.pageTotal()&&page>=1) { 507 products.filter.page = page; 508 products.get(); 509 //} 510 }, 511 setGoogleProductSchema(product) { 512 var images = []; 513 for (var i = 0; i < product.images.length; i++) { 514 var imageItem = product.images[i]; 515 var img = `${window.location.origin + imageItem.url}`; 516 if (imageItem.url.indexOf("http")==0) img = imageItem.url; 517 images.push(img); 518 } 519 var description = product.description || ""; 520 var priceValidUntil = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); 521 var schema = 522 { 523 "@context": "https://schema.org/", 524 "@type": "Product", 525 "name": product.name, 526 "image": images, 527 "description": description.replace(/<[^>]*>?/gm, ''), 528 "sku": product.sku, 529 //"mpn": "925872", 530 "brand": { 531 "@type": "Brand", 532 "name": product.brandName 533 }, 534 "offers": { 535 "@type": "Offer", 536 "priceCurrency": "USD", 537 "price": product.cost.toFixed(2) + '', 538 "priceValidUntil": priceValidUntil.toISOString(), 539 "itemCondition": "https://schema.org/NewCondition", 540 "availability": "https://schema.org/InStock" 541 } 542 } 543 544 if (product.reviewCount && product.reviewCount > 0) { 545 schema.aggregateRating = { 546 "@type": "AggregateRating", 547 "ratingValue": product.reviewAgg + '', 548 "reviewCount": product.reviewCount + '' 549 } 550 } 551 var script = document.createElement('script'); 552 script.type = 'application/ld+json'; 553 script.text = JSON.stringify(schema, null, 2); 554 document.head.appendChild(script); 555 } 556}
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.