1window.catalog = window.catalog || {}; 2catalog.alternateParts = catalog.alternateParts || []; 3 4window.catalog.ProductPreview = function (data) { 5 var self = this; 6 7 self.exists = data.exists; 8 self.warehouseId = data.warehouse_id; 9 self.warehouseForUrl = data.warehouse_for_url; 10 self.partNumber = data.part_number; 11 self.customerPartNumber = ko.observable(data.customerPartNumber); 12 self.newCustomerPartNumber = ko.observable(""); 13 self.slug = data.slug; 14 self.cartComments = data.cartComments; 15 self.partIdForUrl = data.part_id_for_url; 16 self.url = data.url; 17 self.upc = data.upc; 18 self.title = data.title; 19 self.salesDept = data.salesDept; 20 self.isNew = data.new_status && data.new_status.start && data.new_status.start !== ""; 21 self.newAsOf = self.isNew ? data.new_status.start : ""; 22 self.shortDescription = data.short_description; 23 self.longDescription = data.long_description; 24 self.HTMLDescription = data.HTML_description; 25 self.descriptionForUrl = data.description_for_url; 26 self.thumbnails = data.thumbnails; 27 self.defaultCategory = data.default_category; 28 self.manufacturer = data.manufacturer; 29 self.customFields = data.custom_fields; 30 self.stock = data.stock; 31 self.statuses = data.statuses; 32 self.isOnSale = false; 33 self.onSaleStatuses = []; 34 self.price = parseFloat(data.price); 35 self.priceText = "$" + data.price; 36 self.list_price = parseFloat(data.list_price); 37 self.listPriceText = "$" + data.list_price; 38 self.qty = $('#grid-list-view-toggle-container div.list-view img.list-view-on').is(':visible') && $('div.add-all-to-cart').is(':visible') ? ko.observable("") : ko.observable(1); 39 self.qtyFieldId = "qty_" + data.warehouse_id + "_" + data.part_number; 40 self.qtyFieldName = "shopping_cart[add][" + data.warehouse_id + "][" + data.part_number + "][quantity]"; 41 self.uoms = data.uoms; 42 self.defaultUom = ko.observable(data.default_uom); 43 self.uomFieldName = "shopping_cart[add][" + data.warehouse_id + "][" + data.part_number + "][uom]"; 44 self.is_quote_only = data.is_quote_only; 45 self.showAddToCartForQuotableProducts = Quote.showAddToCartForQuotableProducts; 46 self.selectedUOM = ko.observable(data.default_uom); 47 self.isSelectedForCompare = data.isSelectedForCompare; 48 self.addons = data.addons; 49 self.isDigitalProduct = data.isDigitalProduct; 50 self.partDiagrams = data.partDiagrams; 51 self.hasPartDiagrams = data.partDiagrams.length > 0 ? 1 : 0; 52 self.packSize = data.packSize; 53 self.isPriceVisible = data.isPriceVisible; 54 self.isAddToCartVisible = data.isAddToCartVisible; 55 self.isKittedProduct = data.isKittedProduct; 56 self.reorderPoint = data.reorderPoint; 57 self.minimumOrderQty = data.minimumOrderQty; 58 self.promotion = data.promotion; 59 self.isTrainingCourse = data.isTrainingCourse; 60 self.trainingCourses = data.trainingCourses; 61 self.yearToDateGrossSales = data.yearToDateGrossSales; 62 self.images = data.images; 63 self.orderTemplates = ko.observable(); 64 self.alternateParts = !_.isUndefined(data.alternate_parts) ? data.alternate_parts : []; 65 self.preOrderPricing = data.preOrderPricing; 66 self.displayDefaultUomOnly = window.displayDefaultUomOnly; 67 self.productUomDisplaySetting = window.productUomDisplaySetting; 68 self.favouriteData = data.favouriteData; 69 self.preloadedFavourite = !_.isUndefined(data.favouriteData) && data.favouriteData.isPreloaded === "1"; 70 self.productSelectorGroup = data.productSelectorGroup; 71 self.productSelectorGroupData = data.productSelectorGroupData; 72 self.onlyDisplayAlternates = onlyDisplayAlternates; 73 self.displayAlternateField = displayAlternateField; 74 self.isOnSaleNotification = ko.observable(false); 75 self.isInStockNotification = ko.observable(false); 76 self.relatedFiles = _.values(data.related_files); 77 self.isPurchasable = data.isPurchasable; 78 self.isFavourite = data.isFavourite; 79 self.equipmentRegistryDetails = !_.isUndefined(data.equipmentRegistryDetails) ? data.equipmentRegistryDetails : []; 80 self.differentActiveWarehouse = !_.isUndefined(data.differentActiveWarehouse) ? data.differentActiveWarehouse : false;
81 self.inlineProductAssociations = data.inlineProductAssociations; 82 83 self.hasDiscount = function () { 84 var listPrice = parseFloat(data.list_price.replace(/,/g, "")); 85 var price = parseFloat(data.price.replace(/,/g, "")); 86 87 return (listPrice > price) ? true : false; 88 }; 89 90 self.outputProductStatusClasses = function () { 91 var productStatus = ""; 92 _.forEach(self.statuses, function (status) { 93 if (status.name === "on_sale") { 94 productStatus = prepProductStatusClass(status.name); 95 } else if (productStatus === "") { 96 productStatus = prepProductStatusClass(status.name); 97 } 98 }); 99 100 return productStatus; 101 }; 102 103 var prepProductStatusClass = function (status) { 104 var classes = "product_status_icon "; 105 var string = String(status).replace(/,/g, " "); 106 107 return classes + string; 108 }; 109 110 self.outputProductStatusImage = function () { 111 var statusImage = ""; 112 _.forEach(self.statuses, function (status) { 113 if (status.name === "on_sale") { 114 statusImage = status.image; 115 } else if (statusImage === "") { 116 statusImage = status.image; 117 } 118 }); 119 120 return statusImage; 121 }; 122 123 self.hasStatus = function (name) { 124 return _.some(self.statuses, function(status) { 125 return (name == status.name); 126 }); 127 }; 128 129 self.hasManufacturer = function () { 130 return self.manufacturer.id > 0; 131 }; 132 133 self.hasCustomField = function (name) { 134 return _.some(self.customFields, function(field) { 135 return (name == field.name); 136 }); 137 }; 138 139 self.getCustomField = function (name) { 140 var customField; 141 _.forEach(self.customFields, function (field) { 142 if (name == field.name) { 143 customField = field; 144 } 145 }); 146 147 return customField; 148 }; 149 150 self.selectedUomObj = ko.computed(function () { 151 var selectedUOM = self.selectedUOM(); 152 var found = null; 153 $.each(self.uoms, function (k, uom) { 154 if (uom.code == selectedUOM) { 155 found = uom; 156 } 157 }); 158 159 return found; 160 }); 161 162 self.showQuoteButton = ko.computed(function () { 163 return _.result(_.find(self.uoms, function (uom) { 164 return uom.code == self.selectedUOM(); 165 }), "is_quote_only"); 166 }); 167 168 self.showPrice = ko.computed(function () { 169 return self.isPriceVisible && 170 (!self.showQuoteButton() || self.showAddToCartForQuotableProducts) && 171 self.selectedUomObj() && self.selectedUomObj().price != 0; 172 }); 173 174 self.canAddToCart = function () { 175 var visible = self.isAddToCartVisible ? true : false; 176 177 if (visible && self.isPriceVisible && self.selectedUomObj().price === 0) { 178 visible = false; 179 } 180 181 return visible; 182 }; 183 184 self.hasUomWithPrice = function () { 185 return _.find(self.uoms, function (uom) { return uom.price != 0; }) ? true : false; 186 }; 187 188 self.isQuotable = function () { 189 return _.find(self.uoms, 'is_quote_only') ? true : false; 190 }; 191 192 self.showUomAndQty = function () { 193 if ((self.isAddToCartVisible && self.isPriceVisible && self.hasUomWithPrice()) || self.isQuotable()) { 194 return true; 195 } 196 197 return false; 198 }; 199 200 self.prep_alternate_parts = function (product) { 201 if (product.alternate_parts !== undefined && product.alternate_parts !== null) { 202 var alternateParts = product.alternate_parts; 203 204 if (alternateParts.alts !== undefined && alternateParts.alts !== null && alternateParts.alts.length > 0) { 205 catalog.alternateParts[product.slug] = alternateParts; 206 } 207 } 208 }; 209 210 self.prep_alternate_parts(data); 211 212 self.prepUnitsOfMeasure = function (defaultUom) { 213 var unitsOfMeasure = []; 214 var defaultUnitOfMeasure = null; 215 216 _.forEach(self.uoms, function (uom) { 217 if (uom.code === defaultUom) { 218 uom.is_default = true; 219 defaultUnitOfMeasure = uom; 220 } else if (uom.isSellUom) { 221 uom.is_default = false;
222 unitsOfMeasure.push(uom); 223 } 224 }); 225 226 if (defaultUnitOfMeasure !== null) { 227 unitsOfMeasure.unshift(defaultUnitOfMeasure); 228 } 229 230 self.uoms = unitsOfMeasure; 231 }; 232 233 self.prepUnitsOfMeasure(data.default_uom); 234 235 self.getImage = function (key, size) { 236 return data.images[key][size]; 237 }; 238 239 self.defaultImage = ko.observable(self.getImage(0, "medium")); 240 241 var defaultAltText = data.images[0].altText ? data.images[0].altText : data.title; 242 self.defaultImage.width = ko.observable(data.images[0].medium.width); 243 self.defaultImage.height = ko.observable(data.images[0].medium.height); 244 self.defaultImage.src = ko.observable(data.images[0].medium.src); 245 self.defaultImage.srcSet = ko.observable(data.images[0].medium.srcSet); 246 self.defaultImage.sizes = ko.observable(data.images[0].medium.sizes); 247 self.defaultImage.altText = ko.observable(defaultAltText); 248 self.enlargeImage = ko.observable(self.getImage(0, "large")); 249 250 self.setDetailsImage = function (index, image) { 251 self.defaultImage(image); 252 self.defaultImage.width(data.images[index].medium.width); 253 self.defaultImage.height(data.images[index].medium.height); 254 self.defaultImage.src(data.images[index].medium.src); 255 self.defaultImage.srcSet(data.images[index].medium.srcSet); 256 self.defaultImage.sizes(data.images[index].medium.sizes); 257 self.defaultImage.altText(data.images[index].altText); 258 self.enlargeImage(self.getImage(index, "large")); 259 }; 260 261 self.humanize = function (value) { 262 temp = $.trim(value).toLowerCase(); 263 temp = temp.replace(/[_-]+/g, ' '); 264 265 return temp.charAt(0).toUpperCase() + temp.slice(1); 266 }; 267 268 self.machinize = function (value) { 269 temp = $.trim(value).toLowerCase(); 270 271 return temp.replace(/\s+/g, '_'); 272 }; 273 274 self.buildProductRatings = function (rating) { 275 var self = this; 276 var fullStarsArray = []; 277 var halfStarsArray = []; 278 var emptyStarsArray = []; 279 280 if (!_.isUndefined(rating) && rating != null) { 281 var i = 0; 282 283 for (i = 0; i < rating.full_stars; i++) { 284 fullStarsArray[i] = i; 285 } 286 287 for (i = 0; i < rating.half_stars; i++) { 288 halfStarsArray[i] = i; 289 } 290 291 for (i = 0; i < rating.empty_stars; i++) { 292 emptyStarsArray[i] = i; 293 } 294 295 self.rating = { 296 "averageRating": rating.average_rating, 297 "reviewCount": rating.review_count, 298 "maxReviewRating": rating.max_review_rating, 299 "fullStars": fullStarsArray, 300 "halfStars": halfStarsArray, 301 "emptyStars": emptyStarsArray 302 }; 303 } 304 }; 305 306 self.buildProductRatings(data.rating); 307 308 self.buildQuantityBreaks = function (quantityBreaks) { 309 var availableRanges = []; 310 var qbcount = self.findQuantityBreakCount(quantityBreaks); 311 $.each(quantityBreaks, function (uom, quantityBreak) { 312 var length = _.size(quantityBreak); 313 var ranges = []; 314 var liClass = "li_" + uom + " "; 315 var divClass = "qb_" + uom + " quantity_break_container "; 316 317 liClass += (self.defaultUom() == uom || qbcount == 1) ? "selected" : ""; 318 divClass += (self.defaultUom() == uom || qbcount == 1) ? " show" : " hide"; 319 320 $.each(quantityBreak, function (range, value) { 321 var thisRange = { 322 "lowerBound": value.lowerBound, 323 "rangeName": range, 324 "actualPrice": value.formatted_actual_price 325 }; 326 327 ranges.push(thisRange); 328 }); 329 330 var thisUom = { 331 "liClass": liClass, 332 "divClass": divClass, 333 "name": uom, 334 "ranges": ranges 335 }; 336 337 availableRanges.push(thisUom); 338 }); 339 340 self.quantityBreaks = { 341 "availableRanges": availableRanges, 342 "quantityBreaksAvailable": (availableRanges.length > 0) ? true : false 343 }; 344 }; 345 346 self.findQuantityBreakCount = function (qbs) { 347 var count = 0; 348 for (var k in qbs) { 349 if (qbs.hasOwnProperty(k)) { 350 count++; 351 } 352 } 353 return count; 354 }; 355 356 self.doesUomHaveQuantityBreaks = function() { 357 return _.find(self.quantityBreaks.availableRanges, {name: self.selectedUOM()}); 358 }; 359 360 self.getCartCommentForUom = function () { 361 return (self.selectedUOM() in self.cartComments) ? self.cartComments[self.selectedUOM()] : ""; 362 }; 363 364 self.buildQuantityBreaks(data.quantity_breaks); 365
366 self.displayQuickViewModal = function () { 367 $("#quick-view-modal-" + self.slug).modal("show"); 368 }; 369 370 self.getTrainingCourseLocation = function () { 371 return self.trainingCourses[0].location; 372 }; 373 374 self.loadOrderTemplates = function (data) { 375 slug = data.product.slug; 376 orderTemplates.loadOrderTemplates(slug, data.product.selectedUOM()).done(function (data) { 377 self.orderTemplates(data); 378 $("#order-template-modal-" + slug).modal(); 379 }); 380 }; 381 382 self.getTitleForOrderTemplateModal = function () { 383 if (hidePartNumber) { 384 return self.title; 385 } 386 387 return self.partNumber + ' - ' + self.title; 388 }; 389 390 self.removeFavourite = function(id, e) { 391 if (confirmFavouriteDelete()) { 392 $(".ajax-load-overlay").show(); 393 394 $.ajax({ 395 url: base_index_url + "my_account/favourites/delete", 396 type: "POST", 397 data: { 398 id: id 399 }, 400 success: function( data ) { 401 $(e).closest(".product_preview").fadeOut(100, function() { 402 $(this).parent().remove(); 403 404 if ($(".product_preview").length === 0) { 405 $("#favourites-products").remove(); 406 $(".no-favourites").show(); 407 } 408 }); 409 410 Notifications.success(data); 411 }, 412 error: function(jqXHR, textStatus, errorThrown) { 413 Notifications.fail(jqXHR.responseText); 414 }, 415 complete: function(data) { 416 $(".ajax-load-overlay").hide(); 417 } 418 }); 419 } 420 }; 421 422 self.getProductsArray = function (data) { 423 return [{"product": data}]; 424 }; 425 426 self.displayAlternatesModal = function () { 427 $("#alternate-products-modal-" + self.slug).modal(); 428 }; 429 430 self.hasAlternatesForDisplay = function () { 431 if (self.onlyDisplayAlternates === "1" && doesProductHaveAlternateCustomField()) { 432 return true; 433 } 434 435 return false; 436 }; 437 438 var doesProductHaveAlternateCustomField = function () { 439 var hasField = false; 440 _.forEach(self.customFields, function (field) { 441 if (field.name === self.displayAlternateField) { 442 hasField = true; 443 } 444 }); 445 446 return hasField; 447 }; 448 449 var extractAlternatePartNums = function (data) { 450 return _.map(data.alts, "pn"); 451 }; 452 453 self.getAlternates = function (data) { 454 $.ajax({ 455 url: base_index_url + "alternate-parts", 456 data: { 457 "alternatePartNumbers": extractAlternatePartNums(data.alternateParts), 458 }, 459 dataType: "json", 460 type: "POST", 461 success: function (data) { 462 _.forEach(data, function (preview) { 463 var detailProduct = new window.catalog.ProductPreview(preview); 464 var element = document.getElementById("product-preview-modal-" + detailProduct.slug); 465 466 ko.cleanNode(element); 467 ko.applyBindings(detailProduct, element); 468 }); 469 } 470 }); 471 472 }; 473 474 self.checkIsGridView = function () { 475 return true; 476 }; 477 478 self.displayPreOrderPricing = function () { 479 $(".pre-order-pricing-button").popover({ 480 container: "body", 481 html: true, 482 content: function() { 483 popoverContent = $(this).parent().find(".pre-order-pricing").html(); 484 return popoverContent; 485 } 486 }) 487 .on('shown.bs.popover',function(e){ 488 $(".popover").css("min-width","600px"); 489 }); 490 }; 491 492 self.hasPreOrderPricing = function () { 493 return self.preOrderPricing.length > 0; 494 }; 495 496 self.showCustomerPartNumberModal = function (data) { 497 self.showModal("#customer-part-number-modal-" + self.slug); 498 }; 499 500 self.showModal = function (id) { 501 $(id).modal(); 502 }; 503 504 self.hideModal = function (id) { 505 $(id).modal("hide"); 506 }; 507
508 self.hasCustomerPartNumber = ko.computed(function () { 509 return !_.isEmpty(self.customerPartNumber()); 510 }); 511 512 self.saveCustomerPartNumber = function () { 513 var customerPartNumberError = $("#customer-part-number-error"); 514 515 customerPartNumberError.addClass("hide"); 516 517 if (!isAlphanumericDashUnderscore(self.newCustomerPartNumber())) { 518 customerPartNumberError.removeClass("hide"); 519 } else { 520 $.ajax({ 521 url: base_index_url + "customer-part-number", 522 type: "POST", 523 data: { 524 partNum: self.partNumber, 525 customerPartNumber: self.newCustomerPartNumber() 526 }, 527 dataType: "json", 528 success: function(response) { 529 self.updateCustomerPartNumber(self.newCustomerPartNumber()); 530 self.hideModal("#customer-part-number-modal-" + self.slug); 531 532 Notifications.success(response.message); 533 }, 534 error: function(jqXHR, textStatus, errorThrown) { 535 Notifications.fail(jqXHR.responseText); 536 } 537 }); 538 } 539 }; 540 541 self.deleteCustomerPartNumber = function () { 542 $.ajax({ 543 url: base_index_url + "customer-part-number/delete", 544 type: "POST", 545 data: { 546 partNum: self.partNumber, 547 }, 548 dataType: "json", 549 success: function(response) { 550 self.updateCustomerPartNumber(""); 551 self.hideModal("#customer-part-number-modal-" + self.slug); 552 553 Notifications.success(response.message); 554 }, 555 error: function(jqXHR, textStatus, errorThrown) { 556 Notifications.fail(jqXHR.responseText); 557 } 558 }); 559 }; 560 561 self.updateCustomerPartNumber = function (value) { 562 self.customerPartNumber(value); 563 self.newCustomerPartNumber(""); 564 }; 565 566 self.loadNotifyMeTemplates = function (vm, data, email) { 567 if (email) { 568 $.ajax({ 569 url: base_index_url + "notify-me", 570 type: "POST", 571 data: { 572 partNum: self.partNumber, 573 warehouse: self.warehouseId, 574 email: email 575 }, 576 dataType: "json", 577 success: function(response) { 578 self.isOnSaleNotification(_.includes(response,"OnSale")); 579 self.isInStockNotification(_.includes(response,"BackInStock")); 580 }, 581 error: function(jqXHR, textStatus, errorThrown) { 582 Notifications.fail(jqXHR.responseText); 583 } 584 }); 585 } 586 587 self.showModal("#notify-me-template-modal-" + self.slug); 588 }; 589 590 self.updateNotifications = function () { 591 var email = $("#notify-email-" + self.slug).val(); 592 593 $.ajax({ 594 url: base_index_url + "notify-me/create", 595 type: "POST", 596 data: { 597 partNum: self.partNumber, 598 warehouse: self.warehouseId, 599 email: email, 600 conditions : { 601 OnSale : self.isOnSaleNotification, 602 BackInStock : self.isInStockNotification 603 } 604 }, 605 dataType: "json", 606 success: function(response) { 607 Notifications.success(response.message); 608 }, 609 error: function(jqXHR, textStatus, errorThrown) { 610 Notifications.fail(jqXHR.responseText); 611 } 612 }); 613 614 self.hideModal("#notify-me-template-modal-" + self.slug); 615 }; 616 617 self.showSelectedUomOnSaleEndDate = function (uom, price) { 618 var roundedSaleprice = roundPrice(price); 619 var roundedPrice = roundPrice(self.price); 620 621 return uom == self.selectedUOM() && roundedSaleprice > roundedPrice; 622 }; 623 624 self.formatDate = function (date, format) 625 { 626 return moment(date).format(format); 627 }; 628 629 self.hasEquipmentRegistryDetails = function () { 630 return !_.isEmpty(self.equipmentRegistryDetails); 631 }; 632 633 self.equipmentRegistryId = function() { 634 return self.hasEquipmentRegistryDetails() ? self.equipmentRegistryDetails.equipmentRegistryId : ""; 635 }; 636
637 self.showPartDiagramsCondensed = function () { 638 return self.hasPartDiagrams && self.partDiagrams.length > 5; 639 }; 640 641 self.showPartDiagram = function () { 642 if (!_.isEmpty(self.chosenDiagram)) { 643 window.location = self.chosenDiagram.url; 644 } 645 }; 646 647 self.hasRelatedFiles = function () { 648 return self.relatedFiles.length > 0; 649 }; 650 651 self.hasInlineProductAssociations = function () { 652 return self.inlineProductAssociations.length > 0 ? true : false; 653 }; 654 655 var roundPrice = function (price) { 656 return Math.round((price + Number.EPSILON) * 100) / 100; 657 }; 658 659 (function () { 660 _.forEach(self.statuses, function (status) { 661 if (status.name === "on_sale") { 662 self.onSaleStatuses.push(status); 663 } 664 }); 665 666 _.forEach(self.statuses, function (status) { 667 if (status.name === "on_sale") { 668 self.isOnSale = true; 669 } 670 }); 671 })(); 672 673 $("#search-container").on("mouseenter", ".product_list .product_preview, .product_list .product_preview_list", function() { 674 $(this).find(".quick-view-trigger").fadeIn("fast"); 675 }); 676 677 $("#search-container").on("mouseleave", ".product_list .product_preview, .product_list .product_preview_list", function() { 678 $(this).find(".quick-view-trigger").fadeOut("fast"); 679 }); 680};
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.