
<!DOCTYPE html>

<!--[if IE]><![endif]-->
<!--[if IE 9]> <html class="no-js ie9 lt-ie10" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<!-- include files here -->


<head>
    
<link rel="preconnect" href="https://fonts.gstatic.com/" />

  <link href="https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700; Roboto&display=swap" rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'"/>  	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=2">
	
	

	<title>VAN DYK Recycling Solutions</title>
	<meta name="description" content="">
	<meta name="keywords" content="">
		<meta name="robots" content="noindex, nofollow, noimageindex">
    <link rel="canonical" href="https://vandyk3.cimproduction.com/" />
<link rel="stylesheet" href="templates/fa/css/custom-font-awesome.min.css">
	<link rel="shortcut icon" type="image/x-icon" href="https://d1pq1nir82e5zy.cloudfront.net/images/favicon.ico?v=3973847574">
	<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="16x16" />
	<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="32x32" />
		<link rel="preload" href="../css/bootstrap-retail.css?44cc304" as="style" onload="this.onload=null;this.rel='stylesheet'">
	    <link rel="preload" href="../css/retail.css?44cc304" as="style" onload="this.onload=null;this.rel='stylesheet'">
		<link rel="preload" href="../css/focus.css?44cc304" as="style" onload="this.onload=null;this.rel='stylesheet'">
	

    <style>

        /* 
        ### Skins CSS Overrides
        */

                /* Retail Template
                --------------------------------------------------------------- */
                h1, h2, h3, h4, h5, h6,
                input, button, select, textarea,
                body, body.retail {
                    font-family: Open Sans
                }

                html,
                body,
                p {
                    color: #555555;
                }
                
                a { 
                    color: #013267; 
                }

                .text-warning,
                a.text-warning {
                    color: #8a6d3b;
                }

                .text-warning:hover,
                a.text-warning:hover {
                    color: #66512c;
                }

                .text-danger,
                a.text-danger {
                    color: #a94442;
                }

                .text-danger:hover,
                a.text-danger:hover {
                    color: #843534;
                }

                .text-success,
                a.text-success {
                    color: #3c763d;
                }

                .text-success:hover,
                a.text-success:hover {
                    color: #2b542c;
                }

                .text-info,
                a.text-info {
                    color: #31708f;
                }

                .text-info:hover,
                a.text-info:hover {
                    color: #245269;
                }

                a:hover,
                a:focus { 
                    color: rgb(26,76,128); 
                }

                h1, h2, h3, h4, h5, h6, 
                .formblock-heading, 
                legend, 
                .checkout-section-heading, 
                .checkout-section.active .checkout-section-heading { 
                    color: #013267; 
                }

                .pace .pace-progress {
                    background: #17ADE5;
                }

                /* Retail Topbar
                --------------------------------------------------------------- */

                [class*="retail-header"] .topbar {
                    background-color: #17ADE5;
                }

                /* Retail Buttons
                --------------------------------------------------------------- */

                .btn-link {
                    color: #013267;
                }

                .btn-link:not([disabled]):hover, 
                .btn-link:not([disabled]):focus, 
                .btn-link:not([disabled]):active, 
                .btn-link:not([disabled]).active {
                    color: rgb(26,76,128);
                }

                /* Primary buttons */
                .btn-primary, .btn-primary.button {
                    background-color: #17ADE5;
                    border-color: #17ADE5;
                    color: #FFFFFF;
                }

                .btn-primary:not([disabled]):hover, 
                .btn-primary:not([disabled]):focus, 
                .btn-primary:not([disabled]):active, 
                .btn-primary:not([disabled]).active, 
                .btn-primary:not([disabled]).button:hover, 
                .btn-primary:not([disabled]).button:focus, 
                .btn-primary:not([disabled]).button:active, 
                .btn-primary:not([disabled]).button.active {
                    background-color: #FFFFFF;
                    border-color: #FFFFFF;
                    color: #17ADE5;
                }

                /* Secondary buttons */
                .btn:not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info), 
                .btn:not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info).button {
                    background-color: #FFFFFF;
                    border-color: rgb(204,204,204);
                    color: #013267;
                }

                .btn:not([disabled]):not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info):hover, 
                .btn:not([disabled]):not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info):focus, 
                .btn:not([disabled]):not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info):active, 
                .btn:not([disabled]):not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info).active, 
                .btn:not([disabled]):not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info).button:hover, 
                .btn:not([disabled]):not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info).button:focus, 
                .btn:not([disabled]):not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info).button:active, 
                .btn:not([disabled]):not(.btn-primary):not(.btn-error):not(.btn-danger):not(.btn-warning):not(.btn-success):not(.btn-info).button.active {
                    background-color: #FFFFFF;
                    border-color: rgb(204,204,204);
                    color: #000d1b;
                }

                .list-group-item.active, 
                .list-group-item.active:hover, 
                .list-group-item.active:focus {
                    color: #FFFFFF !important;
                    border-color: #17ADE5 !important;
                    background-color: #17ADE5 !important;
                }

                /************* 
                ** Unused button settings
                .btn-secondary,
                .btn-inverse {
                    color: #FFFFFF;
                    border-color: #013267;
                    background-color: #013267;
                }

                .btn-secondary:hover,
                .btn-secondary:focus,
                .btn-inverse:hover,
                .btn-inverse:focus {
                    background-color: rgb(26,76,128);
                    border-color: rgb(26,76,128);
                }

                .btn-secondary:active,
                .btn-inverse:active {
                    background-color: rgb(16,65,118);
                    border-color: rgb(16,65,118);
                } 

                .btn-success {
                    color: #468847;
                    border-color: #468847;
                    background-color: #468847;
                }

                .btn-success:hover,
                .btn-success:focus {
                    background-color: rgb(96,162,96);
                    border-color: rgb(96,162,96);
                }

                .btn-success:active {
                    background-color: rgb(85,151,86);
                    border-color: rgb(85,151,86);
                }

                .btn-danger {
                    color: #FFFFFF;
                    border-color: #B94A48;
                    background-color: #B94A48;
                }

                .btn-danger:hover,
                .btn-danger:focus {
                    background-color: rgb(210,100,98);
                    border-color: rgb(210,100,98);
                }

                .btn-danger:active {
                    background-color: rgb(200,89,87);
                    border-color: rgb(200,89,87);
                }
                *************/

                .toast-success {
                    background-color: #17ADE5;
                }


                /* Breadcrumbs
                --------------------------------------------------------------- */

                body.retail.focus-mode:not(.account) .breadcrumb.breadcrumb-cart li.active {
                    background: #17ADE5;
                    color: #222222;
                }

                 body.retail.focus-mode:not(.account) .breadcrumb.breadcrumb-cart li.active:after {
                    border-color: transparent;
                    border-left-color: #17ADE5;
                    border-width: 20px;
                }


                /* Retail Template 1
                --------------------------------------------------------------- */

                /* Topbar */
                .topbar #retail-topbar-left-menu>li>a:hover, 
                .topbar #retail-topbar-left-menu>li>a:focus, 
                .topbar #retail-topbar-left-menu>li.open>a {
                    background-color: rgb(48,198,254);
                    color: #FFFFFF;
                }

                @media (max-width: 980px) {
                    .topbar #retail-topbar-left-menu>li>a:hover, 
                    .topbar #retail-topbar-left-menu>li>a:focus, 
                    .topbar #retail-topbar-left-menu>li.open>a {
                        background-color: #17ADE5;
                        color: #FFFFFF;
                    }
                }

                .topbar #retail-topbar-left-menu>li ul {
                    background-color: #FFFFFF;
                }

                .topbar #retail-topbar-left-menu .retail-topbar-left-menu__a-1 {
                    color: #FFFFFF !important;
                }

                @media (max-width: 1024px) {
                    .topbar #retail-topbar-left-menu .retail-topbar-left-menu__a-1 {
                        color: #555555 !important;
                    }
                }

                .topbar #retail-topbar-left-menu .retail-topbar-left-menu__a-1:hover, 
                .topbar #retail-topbar-left-menu .retail-topbar-left-menu__a-1:focus {
                    color: #FFFFFF !important;
                }

                @media (max-width: 1024px) {
                    .topbar #retail-topbar-left-menu .retail-topbar-left-menu__a-1:hover, 
                    .topbar #retail-topbar-left-menu .retail-topbar-left-menu__a-1:focus {
                        color: #FFFFFF !important;
                        background: #17ADE5 !important;
                    }
                 }


                .topbar #retail-topbar-left-menu>li ul a:hover, 
                .topbar #retail-topbar-left-menu>li ul a:focus {
                    background-color: rgb(230,230,230);
                }

                @media (max-width: 980px) {
                    .topbar #retail-topbar-left-menu {
                        background-color: #FFFFFF;
                    }
                }

                /* Header */
                
                .focus-header,
                [class*="retail-header"] .logo-bar {
                    background-color: #FFFFFF;
                }

                .focus-header__exit-wrapper {
                    border-color: rgb(230,230,230);
                }

                .focus-header__exit {
                    color: #E16F42;
                }
                
                .focus-header__exit:focus,
                .focus-header__exit:active,
                .focus-header__exit:hover {
                    color: #E16F42 !important;
                    background-color:  rgb(230,230,230);
                }


                [class*="retail-header"] .header__prodcat__toggle,
                [class*="retail-header"] .header__signin-open {
                    color: #E16F42 !important;
                }

                [class*="retail-header"] #header__user-menu>li.link-parent ul,
                [class*="retail-header"] #header__prodcat>li.link-parent ul {
                    background-color: #FFFFFF;
                }

                [class*="retail-header"] .topbar__menu-toggle a,
                [class*="retail-header"] .logo-bar__right .linkset > li > a {
                    color: #E16F42 !important;
                }

                [class*="retail-header"] .topbar__menu-toggle a:hover,
                [class*="retail-header"] .topbar__menu-toggle a:focus {
                    background: rgb(0,148,204) !important;
                }

                @media (max-width: 980px) {
                    [class*="retail-header"] .logo-bar__right .linkset > li > a {
                        background-color: #17ADE5;
                        color: #FFFFFF !important;
                    }
                }

                [class*="retail-header"] .logo-bar__right .linkset > li > a:hover, 
                [class*="retail-header"] .logo-bar__right .linkset > li > a:active {
                    color: #000d1b !important;
                }
                @media (max-width: 980px) {
                    [class*="retail-header"] .logo-bar__right .linkset > li > a:hover, 
                    [class*="retail-header"] .logo-bar__right .linkset > li > a:focus, 
                    [class*="retail-header"] .logo-bar__right .linkset > li > a:active {
                         color: #FFFFFF !important;
                         background: rgb(0,148,204) !important;
                    }
                }

                [class*="retail-header"] .logo-bar__right .linkset ul>li>a:hover, 
                [class*="retail-header"] .logo-bar__right .linkset ul>li>a:focus {
                    background-color: rgb(230,230,230);
                }

                [class*="retail-header"] #header__prodcat>li.link-parent li.link-parent.open>a {
                    background-color: #17ADE5;
                    color: #FFFFFF;
                }

                [class*="retail-header"] #header__prodcat>li>ul {
                    border-top: 1px solid #17ADE5;
                }

                [class*="retail-header"] #header__prodcat>li>ul ul {
                    border-left: 5px solid #17ADE5;
                }

                [class*="retail-header"] .header__hero-search {
                    background-image: url('https://d1pq1nir82e5zy.cloudfront.net/images/default-hero-background-light.jpg?v=3973847574');
                }

                /* Introduction */

                .home-description {
                    background-color: #17ADE5;
                    color: #FFFFFF;
                }

                .home-description:after {
                    border-color: #17ADE5 transparent transparent transparent;
                }


                /* Featured */

                .trending-products__title:after {
                        background-color: #17ADE5;
                }

                /* Reseller */

                .footer-reseller__col-1 {
                    background-image: url('https://d1pq1nir82e5zy.cloudfront.net/images/logo.jpg?v=3973847574');
                }
                
                .footer-reseller__col-2 {
                    background-color: #17ADE5;
                }

                .footer-reseller__col-2:before {
                    border-color: transparent #17ADE5 transparent transparent;
                }

                .footer-reseller__wrap,
                .footer-reseller__wrap p {
                    color: #FFFFFF;
                }

                .footer-reseller h2:after {
                    background-color: #013267;
                }

                /* Retail Products
                --------------------------------------------------------------- */

                .prod-info {
                    color: #555555;
                }

                .prod-desc {
                    color: #013267;
                }
                
                .prod-desc p {
                    color: #013267;
                }
                
                .specials_nm {
                    color: #013267;
                }

                .specials_nm:hover,
                .specials_nm:focus {
                    color: rgb(26,76,128);
                }

                /* Retail Footer
                --------------------------------------------------------------- */

                [class*="retail-footer"] {
                    color: #ffffff; 
                    background-color: #013267; 
                }

                [class*="retail-footer"] .bottom-bar {
                    color: rgba(1,50,103,0.5); 
                    background-color: #ffffff; 
                }

                [class*="retail-footer"] .bottom-bar__colophon a:hover {
                    color: rgba(1,50,103,0.8); 
                }
        
                
        
    </style>

<script>
    // This function is needed for JS hooks.
    function getOriginalPageName() {
        return 'default.asp';
    }

    function getUrlPath() {
        return '/Default.asp';
    }

    function getUrl() {
        return '/Default.asp?';
    }

    function handleImageError(img, noImagePath){
        if(!noImagePath) {
            noImagePath = 'images/no-image.png';
        }
        if ($(img).attr('src') !== noImagePath) {
            $(img).attr('src', noImagePath);
        }    
    }
    var sitename = "vandyk3";
    var isWorkerDomain = false;
    var processPageTitle = isWorkerDomain && !false;
    var bValidIp = true;
    var sOfUrl   = 'https://vandyk3.cimproduction.com';
</script>

        <!-- Latest Google Analytics (GA4) -->
        <!-- Global site tag (gtag.js) - Google Analytics -->
        <script async src="https://www.googletagmanager.com/gtag/js?id=G-WDZ47CK49T"></script>
        <script>
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());

        gtag('config', 'G-WDZ47CK49T',{'debug_mode':true});
        </script>

<script>
    var utils = {};

    utils.isCrossOriginFrame = function (parentContext) {
        try {
            if(!parentContext) parentContext = window;
            if (parentContext === window) return false;

            return (document.location.hostname !== parentContext.location.hostname);
        } catch (e) {
            return true;
        }
    }

    utils.getParameter = function (param, context) {
        var value;
        if(!context) context = window;
        
        // return empty string if cross origin (can't access params)
        if(utils.isCrossOriginFrame(context)) return '';

        var parameters = context.location.search.replace('?', '');

        if (parameters) {
            var pattern = new RegExp('\\b' + param + '=([^;&]+)', 'gi');
            value = parameters.split(pattern)[1];
        }

        return value || '';
    };

    utils.buildImagePath = function(image){
        if(image && image.indexOf("http") > -1){
            imageUrl = image.replace(/http:/i, 'https:');
        }else{
            image = image.replace(oConfig.storefrontUrl, oConfig.sessionData.cdnUrl).toLowerCase();
            if(!image){
                imageUrl = oConfig.noImagePath || oConfig.sessionData.cdnUrl.replace(/\/+$/, '') + '/images/' + oConfig.defaultImage.replace(/^\/+/, '');
            }
            else if(image.indexOf("/") > -1){
                imageUrl = oConfig.sessionData.cdnUrl.replace(/\/+$/, '') + '/' + image.replace(/^\/+/, '');
            }
            else{
                imageUrl = oConfig.sessionData.cdnUrl.replace(/\/+$/, '') + '/images/' + image.replace(/^\/+/, '');
            }
        }
        return imageUrl + '?v=3973847574';
    };

    utils.pageUrl = location.href.replace(/.+\//, '');
    utils.loginUrl = 'security_logon.asp?autopage=' + encodeURIComponent(utils.pageUrl);

    utils.pageName = function(){
        return "default.asp";
    }

    utils.pageType = function(){
        var pageType = "";
        if(utils.pageUrl == '' && pageType == ''){
            pageType = 'home';
        }else if(pageType != ''){
            switch(pageType){
                case 'prodcat':
                case 'product':
                    pageType = 'catalog';
                    break;
                case 'page-section':
                case 'webpage':
                    pageType = 'content';
                    break;
                default:
                    pageType = 'other';
                    break;
            }
        }else{
            switch(getOriginalPageName()){
                case 'pc_product_detail.asp':
                case 'pc_combined_results.asp':
                case 'largest_spend_products.asp':
                case 'largest_qty_ordered_products.asp': 
                case 'recently_ordered_products.asp': 
                case 'frequently_ordered_products.asp':
                    pageType = 'catalog';
                    break;
                case 'showcart.asp':
                case 'account.asp':
                    pageType = 'checkout';
                    break;
                default:
                    pageType = 'other';
                    break;

            }
        }
        return pageType;
    }

    utils.htmlEncode = function(value){
        return $('<textarea/>').text(value).html();
    }

    utils.htmlDecode = function(value){
        return $("<textarea/>").html(value).text();
    }

    utils.pageSubType = function(){
        pageSubType = "default";
        if("" != ''){
            pageSubType = "";
        }
        return pageSubType;
    }

    utils.scrollTo = function (elementArg) {
        var element = $(elementArg).first();
        if (!element.length) return;

        // If the element is in an inactive tab, make the tab active.
        utils.activateTab(element);

        $('html, body').animate({
                scrollTop: $(element[0]).offset().top - 120 //offset to account for header bar
        }, 500);

    };
    
    utils.activateTab = function (element) {
        var tab = element.hasClass('.tab-pane') ? element : element.closest('.tab-pane');
        if (!tab.length || tab.hasClass('active')) return;
        var id = tab.attr('id');
        tab
            .closest('.tabbable')
            .find('.nav-tabs > li > a')
            .filter('[href="#' + id + '"], [data-target="#' + id + '"]')
            .tab('show');
    }

    utils.popToastr = function(title, subtext, config) {

        var confirmToastrConfig = config ? config : {
            'closeButton': true,
            'newestOnTop': true,
            'positionClass': 'toast-top-right',
            'preventDuplicates': false,
            'showDuration': 500,
            'hideDuration': 1000,
            'tapToDismiss': false,
            'timeOut': 5000,
            'extendedTimeOut': 1000
        };

        toastr.success(
            subtext,
            title,
            confirmToastrConfig
        );
    };

    utils.popToastrError = function(title, subtext, config) {
        var errorToastrConfig = {
            'closeButton': true,
            'newestOnTop': true,
            'positionClass': 'toast-top-right',
            'preventDuplicates': false,
            'showDuration': 500,
            'hideDuration': 1000,
            'tapToDismiss': true,
            'timeOut': 5000,
            'extendedTimeOut': 1000
        }

        $.extend(errorToastrConfig, config);

        toggleLoadingWidget(false);

        toastr.error(
            subtext,
            title,
            errorToastrConfig
        );
    }

    utils.decimalPlacesOnUnitPrices = parseInt(2) || 2;
    utils.decimalPlacesOnTotals = parseInt('2') || 2;
    utils.decimalPlacesAllowedOnProductQty = 0;
    utils.defaultQtyIncrement = 1;

    /*
     * 2024-06-07 - Client-side version (Server-side in Global Functions)
     * Standard implementation of quantity validation
     * calculation logic.
     * This assumes all values passed in already account
     * for the uom conversion factor if applicable.
     */
     utils.getValidProductQty = function(inputQty, minQty, maxQty, step, allowZeroValue) {

        // init as success result
        var qtyValidationResult = {
            inputQty: inputQty,
            validQty: inputQty,
            error: ''
        }

        if(typeof allowZeroValue === 'undefined') allowZeroValue = false;

        //- set defaults
        if(typeof step === 'undefined') step = utils.defaultQtyIncrement; //- Defined in site_config_overrides.asp
        if(typeof minQty === 'undefined') minQty = step;
        if(typeof maxQty === 'undefined') maxQty = 0;

        //- Ensure all values are numeric
        inputQty = parseFloat(inputQty);
        minQty = parseFloat(minQty);
        maxQty = parseFloat(maxQty);
        step = parseFloat(step);

        //- zero is valid when validating qty inputs elements
        //- when zero indicates the product is not selected
        //- for add to cart, like on input-qty view with a single
        //- add to cart button
        if(inputQty == 0 && allowZeroValue) {
            return qtyValidationResult;
        }

        //- retun min if inputQty is not passed in, is 0 or less than minQty.
        if( !inputQty || (minQty > 0 && inputQty < minQty)){
            qtyValidationResult.validQty = minQty;
            qtyValidationResult.error = 'min';
            return qtyValidationResult;
        }

        //- DecimalPlacesAllowedOnProductQty defined in site_config_overrides.asp
        //- and assigned to utils.decimalPlacesAllowedOnProductQty in global_scripts_top.asp
        //- It can be 0 so we add 1 to it so we have a multiplier of at least 10.
        //- floatFixMultiplier is used to account for precision issues with floating point
        //- arithmatic on base-10 decimal numbers.
        var floatFixMultiplier = Math.pow(10, (parseFloat(utils.decimalPlacesAllowedOnProductQty) + 1));

        var PadDifferenceFromMin = ( (inputQty * floatFixMultiplier) - (minQty * floatFixMultiplier) );
        var padStep = step * floatFixMultiplier;
        var padMinQty = minQty * floatFixMultiplier;
        var padMaxQty = maxQty * floatFixMultiplier;

        //- Force max to valid value
        if(maxQty < minQty) maxQty = 0;
        if(maxQty > 0) {
            var maxStepMod = (padMaxQty - padMinQty) % padStep;
            if(maxStepMod != 0) {
                var maxAdjust = Math.floor( (padMaxQty - padMinQty) / padStep ) * padStep;

                maxQty = (padMinQty + maxAdjust) / floatFixMultiplier;
            }
        }

        //- return max if inputQty is greater than max
        if(maxQty > 0 && inputQty > maxQty){
            qtyValidationResult.validQty = maxQty;
            qtyValidationResult.error = 'max';
            return qtyValidationResult;
        }

        // 1 isn't a valid default for qty when using
        // fractional quantities without a defined increment
        // or if it's off when it's simple sales uom, so
        // we set increment/step to 0
        var stepMod = step ? ( parseFloat( PadDifferenceFromMin % padStep ) / floatFixMultiplier ) : 0;

        if(stepMod != 0){

            padValidQty = (Math.ceil( PadDifferenceFromMin / padStep ) * padStep) + padMinQty

            var validQty = Math.round(padValidQty) / floatFixMultiplier;

            qtyValidationResult.validQty = validQty;
            if(maxQty > 0 && validQty > maxQty) qtyValidationResult.validQty = maxQty;
            if(validQty < minQty) qtyValidationResult.validQty = minQty;
            qtyValidationResult.error = 'inc';
            return qtyValidationResult;
        }

        return qtyValidationResult;
    }

</script>
<script>
    var cimcloud = {
        helpers: {
            url: getUrl,
            urlPath: getUrlPath,
            loginUrl: utils.loginUrl,
            pageType: utils.pageType,
            pageSubType: utils.pageSubType,
            pageName: utils.pageName,
            pageKey: '',
            urlParameter: utils.getParameter,
            buildImagePath: utils.buildImagePath,
            environment: "production"
        },
        session: {
            accountNumber: "",
            accountName: "",
            username: "",
            email: "",
            firstName: "",
            lastName: "",
            sitename: "vandyk3",
            isLoggedIn: false,
            isImpersonation: false,
            parentSession: {
                username: "",
                email: "",
                firstName: "",
                lastName: ""
            },
            appliedRights: "VANDYK3-CUSTOM-SETTINGS,SHIPPING-ADDRESS-SETTINGS,APPLICATION-DEFAULTS-RMA-RMA-DETAIL-SETTINGS-SETTINGS,SEARCH-FIELDS,BASE-PUNCHOUTS-CXML,QUOTE-SETTINGS,PAY-INVOICES-100,APPLICATION-DEFAULTS-MODIFICATIONS-SETTINGS,APPLICATION-DEFAULTS-CUSTOM_CREDIT_HOLD_MESSAGE-SETTINGS,APPLICATION-DEFAULTS-QUOTE-SETTINGS-SETTINGS,SHOW_BO_ON_ORDER_CONFIRMATION_EMAIL,CUSTOM-CUSTOMER CONFIGURATIONS,PRODUCT-IMAGES,CUSTOM-GLOBAL_PRODUCT_ALIAS_TYPE-FT,APPLICATION-DEFAULTS-BASE_ORDERS-AND-SHIPMENTS-SETTINGS,PAYMENT-CREDIT-INVOICE-MAPPINGS,CUSTOM-SHOW-OPT1-ON RMA ENTRY,SHIPPING-SETTINGS,SET-ACCOUNT-NUMBER-PREFIX,S100-CIMCLOUD-B2B-A,PC-INV-IDP,APPLICATION-DEFAULTS-CUSTOM-HASMAT-LABEL-SETTINGS,APPLICATION-DEFAULTS-CART-CHECKOUT-SETTINGS-SETTINGS,APPLICATION-DEFAULTS-POWER-CUSTOMERS-SETTINGS,BASE-PUNCHOUTS-CXML-EXTRINSIC-FIELDS,ADV-PRICE-DISPLAY-SETTINGS,APPLICATION-DEFAULTS-SHIPPING-ADDRESS-SETTINGS-SETTINGS,APPLICATION-DEFAULTS-SHOW_FREIGHT_INVOICE_DETAIL-SETTINGS,GLOBAL-OVERRIDES,RMA-FEATURE-SETTING-RIGHT-ADD,BASE_INVOICES,APPLICATION-DEFAULTS-RMA-ADD-RMA-SETTINGS-SETTINGS,APPLICATION-DEFAULTS-CUSTOM-GLOBAL_PRODUCT_ALIAS_TYPE-SETTINGS,SHOW-PRICE-BREAKS,EMAIL-A-FRIEND,APPLICATION-DEFAULTS-CUSTOM-RMA-SHOWDATEMODIFIED-SETTINGS,DS-PROD,MODIFICATIONS,PRODUCT-CATEGORIES,RMA-FEATURE-SETTING-RIGHT-MANAGE,APPLICATION-DEFAULTS-CUSTOM-RETAIL-DASHBOARD-TEMPLATE-SETTINGS-SETTINGS,PUBLIC_PRODUCTS,QUOTES-WITH-CONV-LOGS,APPLICATION-DEFAULTS-RMA-RMA-OTHER-SETTINGS-SETTINGS,APPLICATION-DEFAULTS-CREDIT-LIMIT-B-SETTINGS,APPLICATION-DEFAULTS-RMA-RMA-MANAGE-SETTINGS-SETTINGS,QUOTES-SAVED-CARTS,APPLICATION-DEFAULTS-SC-STOCKWARN-SETTINGS,RMA-100,TOGGLE-RETAIL-TEMPLATE,INV-ERP-100,APPLICATION-DEFAULTS-PC-PROMO-SETTINGS,APPLICATION-DEFAULTS-PROJECT-PICKER-SETTINGS,APPLICATION-DEFAULTS-PRODUCT-GALLERY-SETTINGS,CUSTOM-TURN-OFF-CHECKOUT-WAREHOUSE-DISPLAY,APPLICATION-DEFAULTS-S100-CIMCLOUD-B2B-A-SETTINGS,APPLICATION-DEFAULTS-SHIPPING-SETTINGS-SETTINGS,PROJECT-PICKER,GLOBAL-GLOBAL-GLOBAL_SHOW_PRODUCT_DISCOUNT-0,PLACEHOLDER-NO RIGHT FUNCTION,PRODUCT-DESC,PC-PROMO,RMA-FEATURE-SETTING-RIGHT-DETAIL,APPLICATION-DEFAULTS-CUSTOM-PRODUCT-CONFIG-SETTINGS,SHIP-FLATFEE,APPLICATION-DEFAULTS-BASE-PUNCHOUTS-CXML-EXTRINSIC-FIELDS-SETTINGS,APPLICATION-CUSTOM-SETTINGS,ACUMATICA-SETTINGS,UPLOAD-ATC,APPLICATION-DEFAULTS-BASE-SHOW_SHIPPING_ADDRESSES-SETTINGS,CUSTOM-PRODUCT-CONFIG,APPLICATION-DEFAULTS-CUSTOM-SHOW-OPT1-ON RMA ENTRY-SETTINGS,RECENTLY-VIEWED-PRODUCTS,RMA,CUSTOM_CREDIT_HOLD_MESSAGE,COMING-SOON-SPARE-PARTS-CATALOG,ORDER-PT-CXML,CART-OPTIONS,PRODUCT-PRICE-LIST,PRODUCT-DOCS,APPLICATION-DEFAULTS-CATALOG-ENABLE-PRODUCT-DISPLAY-FIELDS-SETTINGS,APPLICATION-DEFAULTS-PRODUCT-CATEGORIES-SETTINGS,CUSTOM-TURN-OFF-PENDING-ORDER-SALES-ORDER-PAGE,APPLICATION-DEFAULTS-GOOGLEFEED-SETTINGS,BASE-SHIPPING_ADDRESSES,APPLICATION-DEFAULTS-CUSTOM-CUSTOMER CONFIGURATIONS-SETTINGS,BASE_PAYMENTS-AND-CREDITS,PROD-EXPLODED-VIEW,BASE_ORDERS-AND-SHIPMENTS,PC-CHILD-SEARCH-ROLLUP,RELATED-PRODUCTS,APPLICATION-DEFAULTS-SET-ACCOUNT-NUMBER-PREFIX-SETTINGS,APPLICATION-DEFAULTS-PAY-INVOICES-100-SETTINGS,APPLICATION-DEFAULTS-BASE-ADVANCED_CUSTOMER_PRICE_SETTINGS-SETTINGS,SC-STOCKWARN,APPLICATION-DEFAULTS-CUSTOM-RETAIL-DASHBOARD-BANNERS-SETTINGS,SITEMAPGEN,TOGGLE-CUSTOM-BANNER-SETTINGS,APPLICATION-DEFAULTS-GLOBAL-OVERRIDES-SETTINGS,APPLICATION-DEFAULTS-CATALOG-ALLOW-ATC-SETTINGS,APPLICATION-DEFAULTS-PROD-EXPLODED-VIEW-SETTINGS,APPLICATION-DEFAULTS-PC-INV-IDP-SETTINGS,BULK-ADD-TO-CART,RMA-FEATURE-SETTING-RIGHT-OTHER,POWER-CUSTOMERS-IMPERSONATE,APPLICATION-DEFAULTS-BASE_INVOICES-SETTINGS,POWER-CUSTOMERS,TAXEXEMPT-C1,ALIAS-B,SHOW_FREIGHT_INVOICE_DETAIL,BASE-SHOW_PRODUCT_ACCOUNT_HISTORY,CART-CHECKOUT-SETTINGS,GOOGLEFEED,PRODUCT-GALLERY,CREDIT-LIMIT-B,CATALOG-ALLOW-ATC,APPLICATION-DEFAULTS-BASE-PUNCHOUTS-CXML-SETTINGS,SHIP-UPSFREIGHT".split(',')
        },
        catalog: {}
    }

    var viewModels = viewModels || {};
</script>



<script src="/js/bundles/coreTop.js?44cc3041f2e241b7e0b54b8792ca5b6a23a7b367" ></script>


<script src="/js/bundles/corePlugins.js?44cc3041f2e241b7e0b54b8792ca5b6a23a7b367" ></script>


<script src="/js/bundles/coreVendors.js?44cc3041f2e241b7e0b54b8792ca5b6a23a7b367" ></script>
<script>
    cimcloud.session.sessionKey = "82BB8AB2C7D34A9E9DE80C164FD176D0"
</script>

      <a id="autoLoginModal" class="global-modal d-none" href="security_logon.asp" data-backdrop="static">
        <img src="https://d1pq1nir82e5zy.cloudfront.net/images/template/vdrs-lockup-mod-8-19-22-350.png?v=3967289411"
           alt="Van Dyk Recycling Solutions Logo">
      </a>
      <script>
        document.addEventListener("DOMContentLoaded", function() {
          var modalLink = document.getElementById("autoLoginModal");
          if (modalLink) {
            modalLink.click();
          }
        });
      </script>

<script>
document.addEventListener("DOMContentLoaded", function() {
  const modal = document.getElementById("global_modal");
  if (!modal) return;

  const observer = new MutationObserver((mutations, obs) => {
    if (
      modal.style.display === "block" ||
      modal.classList.contains("in") ||
      modal.classList.contains("show")
    ) {
      const closeBtn = modal.querySelector(".modal-header .close");
      if (closeBtn) closeBtn.style.display = "none";

      // Only add once
      if (!modal.querySelector(".vdrs-logo")) {
        const header = modal.querySelector(".modal-header");
        if (!header) return;

        const container = document.createElement("div");
        container.className = "vdrs-header-content text-center";
        container.innerHTML = `
          <img 
            src="https://d1pq1nir82e5zy.cloudfront.net/images/template/vdrs-lockup-mod-8-19-22-350.png?v=3967289411"
            alt="Van Dyk Recycling Solutions Logo"
            class="vdrs-logo"
            style="display:block; margin:0 auto 10px auto; padding:0 30px;"
          >
          <span style="margin:0; font-weight:600; color:#003366; font-size:40px;">Order Parts</span>
          <p style="margin:5px 0 15px 0; color:#444;">Get a quote, order, account information.</p>
        `;
        header.insertAdjacentElement("afterend", container);
      }

      // Stop observing after success
      obs.disconnect();
    }
  });

  observer.observe(modal, { attributes: true, attributeFilter: ["style", "class"] });
});
</script>

<style>
  .retail-header-0 {
    border-bottom: 4px solid #17ADE5;
  }

  .d-none {
  display: none !important;
  }

  body.retail .well {
  border: none !important;
  } 

  .well {
  background-color: transparent !important;
  }

  .btn-primary, .btn-primary.button {
    background: linear-gradient(to bottom, #C22811, #9B210D);
   }
   .btn.btn-primary.custom-account-login__submit {
    width: 322px;
    height: 48px;
    font-size: 16px;
    font-weight: 600;
    text-transform: uppercase;
    color: #fff;
    border: none;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  }

  .btn.btn-primary.custom-account-login__submit:hover {
  color: #9B210D;
  }

  .login-links {
    text-align: center;
    
  }

  .contact-login.well.well-large{
    padding-bottom:0;
    margin-bottom:0;
  }
</style>

<style>
  .top-page__banner {
    display: block !important;
  }
  
  .page-main>.container-fluid {
    min-height: 0;
  }

  .top-page__banner {
    display: none;
  }

  .retail-main-nav__li-1.link-parent.drop:hover .retail-main-nav__ul-2 {
    display: block;
    z-index: 9999;
  }

  .retail-main-nav__li-2.link-parent.drop:hover .retail-main-nav__ul-3 {
    display: block;
    z-index: 9999;
  }

  .retail-main-nav.linkset li a {
    text-transform: uppercase;
  }

  .retail-main-nav.linkset ul li {
    line-height: 16px;
  }

  .retail-main-nav__li-1 ul.retail-main-nav__ul-2 {
    padding: 1rem 1.5rem;
    background-color: #E16F42;
    column-count: 1;
  }

  .retail-main-nav__li-2 ul.retail-main-nav__ul-3 {
    padding: 1rem 1.5rem;
    background-color: #E16F42;
    column-count: 2;
  }

  .retail-main-nav.linkset .link-parent a:after {
    content: '' !important;
  }

  .retail-main-nav__li-3,
  .retail-main-nav li.sub-link {
    color: #ffffff;
    background-color: transparent;
    padding: 0;
  }

  .retail-main-nav__li-3 .sub-link a,
  .retail-header-1 .logo-bar__right .linkset .sub-link a,
  [class*="retail-header"] .logo-bar__right .linkset>li.sub-link>a {
    color: #FFFFFF !important;
    background-color: transparent;
    padding: 8px 0;
  }

  .retail-main-nav__li-3 .sub-link a:hover,
  .retail-header-1 .logo-bar__right .linkset .sub-link a:hover,
  [class*="retail-header"] .logo-bar__right .linkset>li.sub-link>a:hover {
    color: #013267 !important;
    background-color: transparent;
  }

  body .retail-header-1 .header__hero-search__container {
    max-width: 640px;
    margin: auto;
    display: inline-flex;
    align-items: center;
    width: 100%;
  }

  body .retail-header-1 .header__hero-search__header {
    color: #444444;
    font-size: 16px;
    text-shadow: none;
    text-transform: none;
    white-space: nowrap;
    margin-right: 1rem;
  }

  .retail-header-1 .logo-bar__logo {
    min-height: auto;
    padding: 0;
  }

  .retail-header-1 .logo-bar__logo .logo img {
    padding: 10px 0;
  }

  body .retail-header-1 .header__hero-search {
    padding: 10px 20px;
  }

  .retail-header-1 .header__hero-search:before {
    opacity: 0;
  }

  .retail-header-1 .header__hero-search,
  [class*="retail-header"] .header__hero-search {
    background-color: #d0d0d0;
    background-image: none !important;
  }

  .retail-header-1 .header__hero-search .u-search-text .input-search {
    border-radius: 0;
  }

  .retail-header-1 .logo-bar__right {
    margin-left: 0;
  }

  .retail-header-1 .header__hero-search .u-search-btn button.btn {
    background-color: #444444;
    border: #444444;
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
  }

  .retail-header-1 .header__hero-search .u-search-btn button.btn:hover {
    background-color: #E16F42;
    border: #E16F42;
    color: #FFFFFF;
  }

  .home-cat-tiles {
    padding-top: 40px;
  }

  .home-cat-tiles h1 {
    color: #013267;
    font-size: 24px;
  }

  .home-cat-tiles p {
    color: #030910;
    font-size: 16px;
    margin-bottom: 2rem;
  }

  .home-cat-tiles .home-categories-row {
    margin-bottom: 2rem;
  }

  .home-cat-tiles .home-categories-row-header {
    display: flex;
  }

  .home-cat-tiles .home-categories-row-header div {
    text-align: right;
    flex: 1;
    border-bottom: 2px solid #013267;
  }

  .home-cat-tiles .home-categories-row-header div:first-child {
    max-width: fit-content;
    border-bottom: 2px solid red;
  }

  .home-cat-tiles .home-categories-row-header div.grid-item-content,
  .home-cat-tiles .home-categories-row-header a {
    color: #707071;
    font-weight: bold;
    font-size: 18px;
    text-decoration: none;
    padding: 0 1rem;
  }

  @media(min-height:981px) {
    .retail-main-nav__li-2 ul.retail-main-nav__ul-3 {
      column-count: 2 !important;
    }

    .home-cat-tiles .row-fluid {
      padding: 1rem 0;
    }

    .retail-header-1 .logo-bar__logo .logo img {
      max-height: 60px;
    }
  }

  .home-cat-tiles .prod-listings,
  .home-cat-tiles .grid-container {
    width: 100%;
    margin-top: 1rem;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
  }

  /* Use a responsive basis to control the number of columns */
  .home-cat-tiles .grid-container a,
  .home-cat-tiles .prod-card,
  .home-cat-tiles .grid-item {
    position: relative;
    flex: 0 1 calc(25% - 1rem);
    /* 4 columns with gap compensation */
    background-color: #ccc;
    border-radius: 0px;
    border-top-right-radius: 25px;
    border-bottom-left-radius: 25px;
    overflow: hidden;
    font-weight: bold;
    margin: 0;
    background-size: auto 100%;
    background-position: center;
    aspect-ratio: 1 / 1;
  }

  /* Maintain 1:1 aspect ratio using the padding trick */
  .home-cat-tiles .prod-card::before,
  .home-cat-tiles .grid-item::before {
    content: "";
    display: block;
    padding-top: 100%;
    /* 1:1 ratio */
  }

  .home-cat-tiles .grid-item img {
    position: absolute;
    top: 45%;
    transform: translate(12%, -50%);
    width: 80%;
  }

  .home-cat-tiles .grid-item a {
    text-decoration: none;
    padding-left: 1.5rem;
  }

  .home-cat-tiles h3 {
    font-size: 18px;
  }

  .home-cat-tiles .prod-details,
  .home-cat-tiles .grid-item-content {
    position: absolute;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: flex-start;
    justify-content: left;
    font-size: 1.2rem;
    color: #ffffff;
    background: linear-gradient(90deg, #14243E, #1B457B);
    padding: 1rem 2rem;
  }

  .home-cat-tiles .grid-container a:hover h3,
  .home-cat-tiles .grid-item-content:hover h3 {
    color: #E16F42;
  }

  .home-cat-tiles h1 {
    color: #013267;
    font-size: 24px;
  }

  .home-cat-tiles h3 {
    color: #FFFFFF;
    font-family: 'Roboto', sans-serif;
    font-weight: 500;
    text-transform: none;
    margin: 0;
  }

  .home-cat-tiles .prod-details h3 {
    color: #FFFFFF;
    font-size: 24px;
    text-transform: none;
  }

  .home-cat-tiles .prod-details:hover h3 {
    color: #FFFFFF;
  }

  .home-cat-tiles .prod-details h2,
  .home-cat-tiles .prod-details .product-description {
    display: none;
  }

  /* Tablet: 3 columns */
  @media (max-width: 1000px) {
    .results .category-listings[data-layout="gallery"] .prod-card {
      width: 31% !important;
    }
  }

  /* Tablet: 3 columns */
  @media (max-width: 992px) {

    .retail-main-nav__li-2 ul.retail-main-nav__ul-3 {
      column-count: 2 !important;
    }

    .retail-main-nav {
      display: flex;
      justify-content: center;
      background-color: #ffffff;
    }

    [class*="retail-header"] .logo-bar__right .retail-main-nav li a {
      color: #E16F42 !important;
      padding-right: 20px;
      background-color: #ffffff;
    }

    [class*="retail-header"] .logo-bar__right .retail-main-nav li a:hover {
      color: #013267 !important;
      background-color: #ffffff;
    }

    .home-cat-tiles {
      padding: 20px;
    }

    .home-cat-tiles .grid-container a,
    .home-cat-tiles .prod-card,
    .home-cat-tiles .grid-item {
      flex: 0 1 calc(33.333% - 1rem);
    }

    .home-cat-tiles h3 {
      font-size: 18px;
    }
  }

  /* Mobile: 2 columns */
  @media (max-width: 600px) {

    .head_desc_container {
      display: inline-flex;
      flex-wrap: wrap;
    }

    .results .category-listings[data-layout="gallery"] .prod-card {
      width: 100% !important;
    }

    .retail-main-nav__li-2 ul.retail-main-nav__ul-3 {
      column-count: 1 !important;
    }

    .home-cat-tiles .grid-container a,
    .home-cat-tiles .prod-card,
    .home-cat-tiles .grid-item {
      flex: 0 1 calc(50% - 1rem);
    }

    .home-cat-tiles h3 {
      font-size: 18px;
    }
  }

  .topbar {
    display: none;
  }

  .retail-header-0 {
    border-bottom: 0;
  }

  .retail-header-0 .logo-bar__right {
    margin-left: 0;
  }

  .retail-header-0 .logo-bar__logo {
    min-height: auto;
  }

  .retail-header-0 .logo-bar__logo .logo img {
    max-height: 80px;
  }

  .retail-header-1 .logo-bar__logo .logo {
    height: auto;
  }

  .retail-header-1 .logo-bar__logo .logo img {
    max-height: 140px;
  }

  .retail-header-1 .header__hero-search {
    padding: 4% 20px;
    position: relative;
  }

  .retail-header-1 .header__hero-search:before {
    background: rgba(0, 0, 0, 0.4);
    bottom: 0;
    content: '';
    display: block;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
    width: 100%;
    z-index: 1;
  }

  .retail-header-1 .header__hero-search__wrap {
    position: relative;
    z-index: 2;
  }

  .footer-email-list {
    padding: 0 20px;
  }

  .retail-footer-1 .retail-footer__logo {
    max-height: 100px;
  }

  .retail-footer-1 .retail-footer__area {
    padding: 15px 20px;
  }

  body.production .home-staging {
    display: none;
  }

  .home-cat-tiles .home__marketing-wells__row.home__marketing-wells__row--1 .home__marketing-wells__col--1 .home__marketing-wells__container {
    background-color: transparent;
  }

  /* Results */
  /*.retail-header-1 .header__hero-search form,*/
  .interior .retail-header-1 .header__hero-search form,
  .checkout .retail-header-1 .header__hero-search form,
  .results .retail-header-1 .header__hero-search form {
    display: flex;
    align-items: center;
  }

  /*.retail-header-1 .header__hero-search form:before,*/
  .interior .retail-header-1 .header__hero-search form:before,
  .checkout .retail-header-1 .header__hero-search form:before,
  .results .retail-header-1 .header__hero-search form:before {
    content: 'Search Our Products';
    color: #444444;
    font-size: 16px;
    font-weight: 700;
    text-shadow: none;
    text-transform: none;
    text-indent: 20px;
    white-space: nowrap;
    margin-right: 1rem;
  }

  .interior.retail-header-1 .header__hero-search form input,
  .checkout .retail-header-1 .header__hero-search form input,
  .results .retail-header-1 .header__hero-search form input {
    max-width: 440px;
  }

  body .interior.retail .breadcrumb,
  .checkout .breadcrumb,
  .results .breadcrumb {
    display: flex;
  }

  .prodcat .head_thumbs {
    display: none !important;
  }

  .head_container {
    margin-bottom: 0;
  }

  .checkout .head_desc,
  .results .head_desc {
    margin-bottom: 0;
  }

  .results .category-listings {
    gap: 1rem;
    row-gap: 0.5rem;
    margin-top: 20px;
  }

  .results .category-listings[data-layout="gallery"] .prod-card {
    width: 20%;
    margin: 0;
    padding: 0;
  }

  .results .category-listings[data-layout="gallery"] .prod-desc {
    margin-bottom: 0;
  }

  .results .category-listings[data-layout="gallery"] .prod-nm a {
    color: #999999;
    background: linear-gradient(90deg, #FFFFFF, #FFFFFF);
    border: 1px solid #999999;
    padding: 0.75rem 1rem;
    border-radius: 8px;
  }

  .results .category-listings[data-layout="gallery"] .prod-nm a:hover {
    color: #E16F42 !important;
    background: linear-gradient(90deg, #14243E, #1B457B);
    border: 1px solid #1B457B;
  }

  .results .category-listings[data-layout="gallery"] .prod-info {
    margin-bottom: 0;
  }

  .results .catalog-sidebar {
    /*display: none;*/
  }

  .results .catalog-main {
    /*width: 100%;*/
  }

  a.shop-all-cats {
    color: #707071;
    font-weight: bold;
    font-size: 18px;
    text-decoration: none;
    padding: 12px 1rem 0;
    display: block;
  }

  .head_desc_container {
    position: relative;
    width: 100%;
    max-width: 900px;
  }

  .head_title {
    float: left;
  }

  .head_desc {
    float: right;
  }
  
  body.saved_carts .DDT-form-search .DDT-form-actions.btn-toolbar [data-test="ddt-toolbar-filters-toggle-btn"],
  body.saved_carts .DDT-form-search .DDT-form-actions.btn-toolbar [data-test="ddt-toolbar-save-search-btn"] {
  	display: none;
  }

</style>

<script>
  $(function () {
  	$('#u_search_text').attr('placeholder', 'Enter Keyword or Part#');
  });
</script>

<script>
  ko.bindingHandlers.insertShopAllLink = {
    update: function (element, valueAccessor) {
      const name = ko.utils.unwrapObservable(valueAccessor());
      const h1 = element.querySelector('h1.head_title');

      if (!h1) return;

      // Inject link only if the heading says "Shop By Category"
      if (name === 'Shop By Category') {
        const a = document.createElement('a');
        a.href = 'pc_combined_results.asp?pc_id=1630A7322EBB45FEB5832BC79ECA708E';
        a.className = 'shop-all-cats';
        a.textContent = 'Show All Categories';
        a.dataset.injected = 'shop-all';
        h1.after(a);
      }
    }
  };

  // Create and expose the viewModel
  var viewModelx = {
    name: ko.observable('Shop By Category')
  };

  // Apply binding to each container using the shared viewModel
  document.querySelectorAll('.head_desc_container').forEach(function (container) {
    ko.applyBindingsToNode(container, { insertShopAllLink: viewModelx.name }, viewModelx);
  });

  // Call applyBindings once globally if needed
  ko.applyBindings(viewModelx); // optional unless you have other bindings elsewhere
</script>
</head>

<body class="home retail t-ui-phase-4 site-type-3 loggedout  t-ui-phase-4 home loggedout site-type-3 production vandyk3">

	     

    <noscript class="noscript-padding"></noscript>

    

<style>
	.focus-header__container {
		height: 70px;
	}
	.focus-header__logo {
		padding: 5px 0;
	}
	.focus-header__exit.btn-link {
		background-color: #fe622f !important;
		color: #fff !important;
		height: 70px;
		line-height: 70px;
	}
	.focus-header__exit.btn-link:hover,
	.focus-header__exit.btn-link:focus  {
		background-color: #FFFFFF !important;
		color: #00A3E0 !important;
	}
</style>


	<div class="top-page__banner" style="background-color: #02477e;">
		<p style="color: #ffffff;"><strong>Welcome To Van Dyk Direct <strong></p>
	</div>


<header id="retail-header-1" class="retail-header-1">
	    <div id="topbar" class="topbar print-hide">
        <div class="container-fluid">
            <nav class="topbar__left">
                <ul class="topbar__left__menu linkset click-persist js-click-outside-to-close">
                    <li>
                        <a class="topbar__menu-toggle" href="#0">
                            <i class="icon-reorder"></i> Menu
                        </a>
                        <ul id="retail-topbar-left-menu" class="horizontal retail-topbar-left-menu retail-topbar-left-menu__ul-1">
	<li class="retail-topbar-left-menu__li-1" data-key='67CD3978D3A5471589F3A40B6C9378C5'><a href="#" class="retail-topbar-left-menu__a-1">About</a>
	<ul class="retail-topbar-left-menu__ul-2">
		<li class="retail-topbar-left-menu__li-2" data-key='85BAA44D0A1F4D1AB5FE82FCD6BA0FAC'><a href="/about" class="retail-topbar-left-menu__a-2">About Us</a></li>
		<li class="retail-topbar-left-menu__li-2" data-key='29C0455E69944D2C953C31BE13C9F55F'><a href="/about/team" class="retail-topbar-left-menu__a-2">Meet the Team</a></li>
	</ul></li>
</ul>
                    </li>
                </ul>
            </nav>
            <div class="topbar__right">
                
            </div>
        </div>
    </div>
	<div id="logo-bar" class="logo-bar">
		<div class="container-fluid">
		    <div class="logo-bar__logo">
			  
		<a href="https://vandyk3.cimproduction.com" class="logo hidden-xs">
		<img src="https://d1pq1nir82e5zy.cloudfront.net/images/template/vdrs-lockup-mod-8-19-22-350.png?v=3973847574?v=3973847574" alt="Van Dyk Recycling Solutions Logo">
		</a>
	
		<a href="https://vandyk3.cimproduction.com" class="logo visible-xs">
		<img src="https://d1pq1nir82e5zy.cloudfront.net/images/template/vdrs-lockup-mod-8-19-22-350.png?v=3973847574?v=3973847574" alt="Van Dyk Recycling Solutions Logo">
		</a>
	
		    </div>
		    <nav class="logo-bar__right">
			<ul id="retail-main-nav" class="retail-main-nav linkset horizontal click-persist js-click-outside-to-close retail-main-nav__ul-1">
	<li class="retail-main-nav__li-1" data-key='02A6B5C8536143BDBDA9366F329FAD29'><a href="javascript:void(0);" class="retail-main-nav__a-1">Shop Parts</a>
	<ul class="retail-main-nav__ul-2">
		<li class="sub-link retail-main-nav__li-3" data-key='1FF7A69B921C4A6798E7AA408737557A'><a href="pc_combined_results.asp?pc_id=63C46F8D873E490CA4CA2718C617D8D7" class="retail-main-nav__a-3">Shop By Category</a></li>
	</ul></li>
	<li class="retail-main-nav__li-2" data-key='52B25302F9A341BB9A2F4B682DEB94B6'><a href="javascript:void(0);" class="retail-main-nav__a-2">Account</a>
	<ul class="retail-main-nav__ul-3">
		<li class="sub-link retail-main-nav__li-3" data-key='823430AEB9B24DEF88AEC071A2FC12A9'><a href="security_logon.asp" class="retail-main-nav__a-3">Sign In</a></li>
	</ul></li>
	<li class="retail-main-nav__li-2" data-key='E1658B3F7498489CB856C043E3532497'><a href="mailto:Service@vdrs.com?subject=Website Customer Service Request" class="retail-main-nav__a-2">Contact Support Team</a></li>
</ul>
    <ul id="header__cart-preview" class="header__cart-preview linkset click-persist js-click-outside-to-close">
        <li class="link-parent">
            <a class="header__cart-preview__toggle" href="#0">
                <i class="icon-shopping-cart"></i> <span class="hidden-xs hidden-sm">My</span> Cart
                <span id="cart_window_qty" class="cart_window_qty badge">0</span>
            </a>
            <ul>
                <li class="app-minicart" id="cart_window_incl">
                    

	<div id="cart_window" class="cart-preview cart_window">
		

				<script>
					var cartQtyTarg = '#cart_window_qty',
						cartQtyTotal = 0,
						cartWinTarg = '#cart_window_incl';
				</script>

				<div class="cart-empty-text muted">Your shopping cart is empty.</div>

				
					<div class="controls">
						<a href="/showcart.asp?o_key=94B6248E99744092A661DBC70E0FE29B" class="btn btn-block btn-primary view-cart-button">
							<i class="icon-shopping-cart"></i> View &amp; Edit Cart
						</a>
					</div>
				

		<script type="text/javascript">

			jQuery(function(){
				// Set the template cart qty on initial load
				fncUpdateTemplateQty(cartQtyTarg, cartQtyTotal);
			});

			// Function to update the template qty value
			function fncUpdateTemplateQty(targ, total) {
				jQuery(targ).text(total);
			}

			// Function to remove an item from the cart window
			function fncCartWinRemove(sUnqID, sConfirmText) {
				var bUseRemoveMsg = true;

				if(bUseRemoveMsg){
					var okay = confirm(sConfirmText);
				}
				else{
					okay = true;
				}

				if (okay) {
					jQuery('#li_'+sUnqID).remove();
					try {
						jQuery.ajax({
							url: 'i_i_add_to_cart.asp',
							data: 'type=v200remove&ajax=returnJSON&rkey='+sUnqID,
							type: 'GET',
							dataType: "json",
							success: function(jsonOrder) {
								fncCartRefresh(jsonOrder[1].item_count, jsonOrder[1].o_total);
							}
						});
					} catch (err) {
						//alert(err.message);
						location.reload();
					}
				}
			} //-function fncCartWinRemove(sUnqID)

			// Refreshes the (#) for the cart items count
			function fncCartRefresh(item_count, o_total) {
				if (typeof item_count == "undefined") {
					try {
						jQuery.ajax({
							url: 'shopping_cart_window.asp',
							data: 'cartwindowajax=getcart',
							type: 'GET',
							dataType: 'json',
							error: function (jqXHR, textStatus, errorThrown) {
								alert('Error! ' + errorThrown + ' ' + textStatus);
								},
							success: function (oData) {
								var cart = oData || {};
								if (typeof cart != "undefined") {
									//console.log(cart);
									jQuery(cartQtyTarg).html(cart.items);
									jQuery("#cart_window_subtotal").html(cart.total);
									//return true;
								}
								fncReloadCartWindow();
							}
						});
					} catch(err) {
						alert(err.message);
					}
				} else {
					if (jQuery(cartQtyTarg).length==1) {
						//console.log(cart);
						jQuery(cartQtyTarg).html(item_count);
						jQuery("#cart_window_subtotal").html(o_total);
						//return true;
					}
					fncReloadCartWindow();
				}
			} //- function fncCartRefresh()

			// Reload the contents of the cart window
			function fncReloadCartWindow() {
				try {
					jQuery.ajax({
						url: 'shopping_cart_window.asp',
						data: 'cartwindowajax=ajaxcall',
						type: 'get',
						success: function (oData) {
							jQuery(cartWinTarg).html(oData);
							//fncUpdateTemplateQty(cartQtyTarg, cartQtyTotal);
							$(window).trigger('cartWindowUpdated');
							return true;
						}
					});
				} catch(err) {
					alert(err.message);
				}
			} //-function fncReloadCartWindow()

			function fncConfirmEmptyCart() {
				if(confirm("Are you sure you want to empty your cart?")) {
					fncEmptyCart();
				}
			}

			function fncEmptyCart() {
				jQuery.ajax({
					url: 'shopping_cart_window.asp',
					data: 'pageaction=empty_cart',
					type: 'get',
					success: fncReloadCartWindow
				});
			}

			

		</script>

	</div>
	<!-- /#cart_window -->
	
                </li>
            </ul>
        </li>
    </ul>

		    </nav>
		</div>
	  </div>
	
    <div class="header__hero-search">
        <div class="header__hero-search__wrap">
            <div class="header__hero-search__container">
                    <h2 class="header__hero-search__header">Search Our Products</h2>

		<form id="frmUnivSearch" class="u-search-form control-group" method="post" action="">
				

				<span class="u-search-group u-search-text">
					<input
						type="text"
						name="u_search_text"
						id="u_search_text"
						class="input-search"
						placeholder="Search..."
						value=""
						autocomplete="off"
						autocorrect="off"
						autocapitalize="off"
						spellcheck="false"
						aria-label="Search Text"
					/>
				</span>

				

				<span class="u-search-group u-search-droplist">
					<select name="u_search_type" id="u_search_type" class=" hide" aria-label="Search Type">
					<option value="pc_combined_results.asp?search_prod=((searchlike~p.sku~<keyword>|Or|searchlike~p.nm~<keyword>|Or|searchlike~p.child_skus~<keyword>|Or|searchlike~p.child_nms~<keyword>|Or|searchlike~p.ds~<keyword>|Or|searchlike~p.search_terms~<keyword>|Or|searchlike~p.searchfield1~<keyword>|Or|searchlike~p.searchfield5~<keyword>)|Or|searchlike~p.child_rollup_search_terms~<keyword>|Or|searchlike~p.search_terms~<keyword>)&search_keyword=<keyword>&ddtloaded=1">Products</option><option value="sales_orders.asp?search2=(searchlike~order_date~<keyword>|OR|searchlike~eosoh.order_number~<keyword>|OR|searchlike~eosoh.status~<keyword>|OR|searchlike~grand_total~<keyword>|OR|searchlike~po_number~<keyword>|OR|searchlike~expected_ship_date~<keyword>)&s=<keyword>&sfield=searchall&ddtloaded=1">Orders</option><option value="backorders.asp?eosoh.order_date_date_from=&eosoh.order_date_date_to=&group=&s=<keyword>&sfield=searchall&rpp=50&ddtloaded=1&pagesize=50&search2=((searchlike~p.nm~<keyword>%7COR%7Csearchlike~p.sku~<keyword>%7COR%7Csearchlike~eosoh.order_number~<keyword>%7COR%7Csearchlike~eosoh.po_number~<keyword>%7COR%7Csearchlike~eosod.qty_backordered~<keyword>%7COR%7Csearchlike~eosod.unit_price~<keyword>))&page=1&ddt=1&ddtloaded=1">Backorders</option><option value="shipments.asp?ship_date_date_from=&ship_date_date_to=&group=&s=<keyword>&sfield=searchall&rpp=50&ddtloaded=1&pagesize=50&search2=((searchlike~ship_date~<keyword>%7COR%7Csearchlike~eos.ship_via~<keyword>%7COR%7Csearchlike~eos.tracking_num~<keyword>%7COR%7Csearchlike~eos.sales_order_number~<keyword>%7COR%7Csearchlike~eoih.ref_id~<keyword>%7COR%7Csearchlike~eosoh.po_number~<keyword>%7COR%7Csearchlike~eos.notes~<keyword>))&page=1&ddt=1">Shipments</option><option value="invoices.asp?search2=(searchlike~eoih.ref_id~<keyword>|OR|searchlike~invoice_date~<keyword>|OR|searchlike~eoih.status~<keyword>|OR|searchlike~eoih.po_number~<keyword>|OR|searchlike~eoih.sales_order_number~<keyword>|OR|searchlike~invoice_due~<keyword>)&s=<keyword>&sfield=searchall&ddtloaded=1">Invoices</option><option value="my_addresses.asp?s=<keyword>&sfield=searchall&rpp=50&ddtloaded=1&pagesize=50&search2=((searchlike~sha.nm~<keyword>%7COR%7Csearchlike~sha.s_add1~<keyword>%7COR%7Csearchlike~sha.s_city~<keyword>%7COR%7Csearchlike~sha.s_state~<keyword>%7COR%7Csearchlike~sha.s_zip~<keyword>%7COR%7Csearchlike~sha.s_country~<keyword>))&page=1&ddt=1&ddtloaded=1">Addresses</option><option value="payments.asp?sortby=date_received&sortorder=desc&s=<keyword>&sfield=searchall&pagesize=100&search2=((searchlike~date_received~<keyword>|OR|searchlike~eop.invoice_number~<keyword>|OR|searchlike~amount~<keyword>|OR|searchlike~payment_type~<keyword>|OR|searchlike~reference_info~<keyword>))&ddtloaded=1">Payments</option><option value="product_stock_ddt.asp?s=<keyword>&sfield=searchall&search2=((searchlike~p.nm~<keyword>%7COR%7Csearchlike~p.sku~<keyword>%7COR%7Csearchlike~p.retail_price~<keyword>))">Price List</option><option value="favorites_lists.asp?s=<keyword>&sfield=searchall&search2=((searchlike~fl.nm~<keyword>))&ddtloaded=1">Favorite Lists</option>				
					</select>
				</span>

				<span class="u-search-group u-search-btn">
					
						<button type="submit" name="u_search_submit" id="u_search_submit" class="btn btn-primary header__search__button" aria-label="Search">
							<i class="icon-search"></i>
						</button>
				</span><input type="hidden" name="search_tip_url" id="search_tip_url" value="" />
		</form>
		
		<script type="application/javascript">

			// Execute when DOM ready
			$(function(){

				// Form post
				$('#frmUnivSearch').on('submit', function(e){
					e.preventDefault();
					fncProcessUniversalSearch();
				});

			});
			/* Boolean Vars */
				/*
				* Add the STOREFRONTURL to the page being redirected to
				* @author	johns
				* @since	6/28/2012
				* @var		bool
				*/
				var bAddStoreFrontUrlToPost		= true;

				/*
				* Log the keyword search
				* @author	johns
				* @since	6/28/2012
				* @var		bool
				*/
				var bUseKeywordSearchLogging	= true;
				
				/*
				* Create searchstring by the splitting the terms on spaces
				* @author	johns
				* @since	6/28/2012
				* @var		bool
				*/
				var bUseSplitTermSearching		= true;

				/*
				* Use Auto Suggestions when searching
				* @author	johns
				* @since	07/26/2012
				* @var		bool
				*/
				var bUseAutoSuggestions			= true;

				/*
				* Add Categories to the Searchstring
				* @author	johns
				* @since	07/26/2012
				* @var		bool
				*/
				var bUseCategories				= false;

				/*
				* Add modal=1 to redirect url
				* @author	ejohnston
				* @since	10/02/2017
				* @var		bool
				*/
				var bUseModal   				= false;

			/* String Vars */
				/*
				* VBScript variable for STOREFRONTURL
				* @author	johns
				* @since	6/28/2012
				* @var		string
				* Updated 2017-10-03 EJ - Used order front url to avoid SSL mixed content errors
				*/
				var sStoreFrontUrl				= 'https://vandyk3.cimproduction.com';

				/*
				* Name of the current page loaded
				* @author	johns
				* @since	6/28/2012
				* @var		string
				* Updated 201-11-15 EJ - Changed var name to avoid conflict
				*/
				var searchPageName				= 'i_i_u_search.asp';

				/*
				* List of product fields to check for a product search
				* @author	johns
				* @since	6/28/2012
				* @var		string
				*/
				var sProductFieldsToCheck		= 'p.sku, p.nm, p.ds, p.search_terms, p.child_rollup_search_terms, p.child_skus, pa.sku_alias, pa.nm_alias, pa.ds_alias';

				/*
				* List of fields to check for a website search
				* @author	johns
				* @since	6/28/2012
				* @var		string
				*/
				var sWebsiteFieldsToCheck		= 'p.nm,p.ds,staticpages.se_pagetitle,staticpages.se_ds,staticpages.se_keywords';

				/*
				* Name of the page to redirect to for product searches
				* @author	johns
				* @since	6/28/2012
				* @var		string
				*/
				var sProductSearchPage			= 'pc_combined_results.asp';

				/*
				* Name of the page to redirect to for website searches
				* @author	johns
				* @since	6/28/2012
				* @var		string
				*/
				var sWebsiteSearchPage			= 'search_results.asp';

				/*
				* List of category fields to check for a product search
				* @author	johns
				* @since	07/26/2012
				* @var		string
				*/
				var sCategoryFieldsToCheck		= 'pc.nm,pc.ds';
			
				//- these are for the search tips ajax call redirect
				/*
				* Page the search is being performed on
				* @author	johns
				* @since	07/26/2012
				* @var		string
				*/
				var sInitPage					= '/Default.asp';

				/*
				* Add modalaction to redirect url if populated
				* @author	ejohnston
				* @since	11/18/2022
				* @var		string
				*/
				var sModalAction   				= "";

				/*
				* Website name
				* @author	johns
				* @since	07/26/2012
				* @var		string
				*/
				var sSitename					= 'vandyk3';

				var sProductSearchTerm = 'products';
				var bTrimSearchTerm = false;

				var isWorkerDomain = false;

			/**
			* This function handles the form submission.
			*
			* @author		seanm
			* @since		??
			*
			* @author		johns
			* @modified	6/28/2012
			* 
			* @return		void
			*/		
			function fncProcessUniversalSearch(searchTerm, redirect = true) {
				var sPrepopValues  = '';
				var sDelimiter     = '?';
				var sNewSearchPage = '';
				var sSearchType    = '';
				var	sRedirectUrl   = '';
				var sSearchTerm    = jQuery('#u_search_text').val()
				if(searchTerm){ sSearchTerm = searchTerm }

				if (bTrimSearchTerm)
					sSearchTerm = $.trim(sSearchTerm);

				sSearchTerm = encodeURIComponent(sSearchTerm);

				jQuery('#u_search_text').attr('disabled', true);
				jQuery('#u_search_type').attr('disabled', true);
				jQuery('#u_search_submit').attr('disabled', true);

				sSearchType = jQuery('#u_search_type option:selected').text().toLowerCase();

				if ( bUseSplitTermSearching && sSearchType == sProductSearchTerm ) {
					sNewSearchPage = fncBuildSplitTermSearchPage ( sSearchTerm, sSearchType );
				} else {
					querystringTerm = sSearchTerm.replaceAll('(','\\(').replaceAll(')','\\)');
					sNewSearchPage = jQuery('#u_search_type option:selected').val().replace(/<keyword>/ig, querystringTerm);
				} 
				
				/*
				* If there's already a ?, we need to make sure we don't add another.
				*/
				if ( sNewSearchPage.indexOf('?') >= 0 ) {
					sDelimiter = '&';
				}

				/*
				* PMH #134437
				* Added search_keyword to query string regardless of whether splitting is in use. That allows
				* the search textbox to always populate.
				*/
				if (bUseSplitTermSearching && sSearchType == sProductSearchTerm ) {
					sPrepopValues = sDelimiter + 'search_keyword=' + sSearchTerm;
				} else {
					if (sNewSearchPage.indexOf('search_keyword') == -1)
						sPrepopValues = sDelimiter + 'search_keyword=' + sSearchTerm + sDelimiter + 'u_search_type=' + jQuery('#u_search_type option:selected').text();
					else 
						sPrepopValues = sDelimiter + 'u_search_type=' + jQuery('#u_search_type option:selected').text();
				} 

				if ( sNewSearchPage.indexOf('u_search_type') >= 0 ) {
					sPrepopValues = '';
				} 
				
				if ( bAddStoreFrontUrlToPost ) {
					sNewSearchPage = '/' + sNewSearchPage;
				}

				if ( bUseAutoSuggestions ) {
					fncCheckSearchSuggestions();
					sRedirectUrl = jQuery('#search_tip_url').val();
				} //- if ( bUseAutoSuggestions )

				if ( sRedirectUrl === '' ) {
					sRedirectUrl = sNewSearchPage + sPrepopValues;
				}

				if ( bUseKeywordSearchLogging ) {
					fncLogKeywordSearch(sSearchType, sSearchTerm, sRedirectUrl)
				}

				if(bUseModal) {
					sRedirectUrl += '&modal=1';
				}

				if(sModalAction) {
					sRedirectUrl += '&modalaction=' + sModalAction;
				}

				var config = {
					searchTerm: sSearchTerm,
					url: sRedirectUrl
				};

				runHook('processUniversalSearchUrl',config);
				
				if(!redirect){
					return config.url;
				}else{
					window.location = config.url;
				}
			} //- function fncProcessUniversalSearch()

			/**
			* This function fires an ajax call to log the keyword search.
			*
			* @author	johns
			* @since	6/28/2012
			*
			* @param	sType (string): type of search submitted
			* @param	sKeyword (string): keyword(s) searching for
			* @param	sUrl (string): url redirected to see the results
			*				of the search
			* 
			* @return	void
			*/
			function fncLogKeywordSearch ( sType, sKeyword, sUrl ) {
				
				jQuery.ajax({
					url: searchPageName + '?pageaction=logsearch&SearchFormAjax=1',
					data: 'search_type=' + sType + '&search_term=' + sKeyword + '&search_url=' + sUrl + '&r_id=' + new Date().getTime(),
					type: 'POST',
					dataType: 'text',
					beforeSend: '',
					success: function () { return; }
				});
			
			} //- function fncLogKeywordSearch ( sType, sKeyword, sUrl )
			
			/**
			* This function builds a searchstring from phrases (denoted
			* by single or double quotes) and other search terms.
			*
			* @author	johns
			* @since	6/28/2012
			*
			* @param	sSearchType (string): type of search submitted
			* @param	sSearchTerms (string): phrases or words searching for
			* 
			* @return	string (page used to redirect to with the appropriate searchstring)
			*/
			function fncBuildSplitTermSearchPage ( sSearchTerms, sSearchType ) {

				/*
				* array variables
				*/
				var aNewSearchTerms;
				var aFieldsToCheck;
				var aTerms;
				
				/*
				* string variables
				*/
				var sTerms             = '';
				var aNewSearchTerms    = [];
				var sResult            = '';
				var sSearchStringQSVar = '';
			
				/*
				* determine which list of fields to use and which
				* page we need to redirect to based on search
				* type
				*/
				switch (sSearchType) {
					case 'website':
						sSearchPage        = sWebsiteSearchPage;
						sFieldsToCheck     = sWebsiteFieldsToCheck;
						sSearchStringQSVar = 'search_website';
						break;
					
					case sProductSearchTerm:
						sSearchPage    = sProductSearchPage;
						sFieldsToCheck = sProductFieldsToCheck;

						if ( bUseCategories ) aCategoryFieldsToCheck = sCategoryFieldsToCheck.split(',');

						sSearchStringQSVar         = 'search_prod';
						sCategorySearchStringQSVar = 'search_cat';
						break;
				}

				/*
				* build an array from a list of fields to check in the extended search
				* by splitting the list on ','
				*/			
				aFieldsToCheck = sFieldsToCheck.split(',');
				
				sSearchTerms = sSearchTerms.replaceAll('(','\\(').replaceAll(')','\\)')
				aNewSearchTerms = decodeURIComponent(sSearchTerms).split(/\s+/);
				
				sSearchTerms = encodeURIComponent(sSearchTerms);


				if ('1' == '1') {
					sSearchPage = sSearchPage + '?' + sSearchStringQSVar + '=' + fncBuildSearchString(aNewSearchTerms, aFieldsToCheck);

					if (bUseCategories && sSearchType === sProductSearchTerm) {
						sSearchPage = sSearchPage + '&' + sCategorySearchStringQSVar + '=' + fncBuildSearchString(aNewSearchTerms, aCategoryFieldsToCheck);
					}
				}

				return sSearchPage;
			} // fncBuildSplitTermSearchPage

			function fncCheckSearchSuggestions () {
				var sSearchTerm = '';

				try {				
						
					/*
					* if it is blank, ge the value of term being searched
					* for
					*/
					sSearchTerm = jQuery('#u_search_text').val();
					sSearchTerm = sSearchTerm.toLowerCase();
					
					/*
					* loop through the json object build by the ajax call
					* to build the search tips list
					*/
					for ( var i = 0, goTo = window.oSearch.terms.length; i < goTo; i++ ) {
											
						/*
						* get the term value of json object
						*/
						sTerm			= window.oSearch.terms[i];
						sOriginalTerm	= sTerm;
						sTerm			= sTerm.toLowerCase();
						
						/*
						* see if the term matches the word being searched for
						*/
						if ( sTerm == sSearchTerm ) {
							
							/*
							* check the term that matched an suggested term
							* has an specific url to redirect to
							*/							
							fncGetSearchTermRedirectUrl ( window.oSearch[sOriginalTerm] );

							/*
							* if a specific redirect url has been found then exit the "terms" for loop
							*/
							if ( jQuery('#search_tip_url').val() !== '' ) break;

						} //- if ( sTerm == sSearchTerm )

					} //- for ( var i = 0, goTo = window.oSearch.terms.length; i < goTo; i++ )
				
				} catch ( oError ) {
					//alert(oError.message);
				} //- try

				return 

			} //- function fncGetSearchSuggestionUrl ()

			/**
			* AJAX Call to get search term suggestions
			*
			* @author	johns
			* @since	4/15/2010
			*
			* @return	void
			*/
			function fncGetSuggestions ( text ) {

				try {
					
					//- check to see if the ajax call should be fired again
					//- run it again, if length of the search term = 0
					if ( text.length < 1 ) {
						window.bGetSuggestions = false;
					} //- if ( text.length < 1 )

					if ( ! window.bGetSuggestions ) {

						if ( text.length >= 1 ) {
							
							//- set it to true once inside the ajax call so it does not fire again
							window.bGetSuggestions = true;

							text = text.substr(0,1);

							jQuery.ajax({ 
								url: 'i_i_pc_search_form.asp'
								, data: ({	searchformaction : 'ajaxcall'
											, searchformactiontype : 'getsearchtips'
											, firstchar : text
											, ajax: new Date().getTime() 
										})
								, cache: false
								, dataType: 'json'
								, error: function(jqXHR, textStatus, errorThrown) { console.log('error - ' + errorThrown); }
								, success: fncLoadSuggestions
							});

						} //- if ( text.length >= 1 )

					} //- if ( ! window.bGetSuggestions )

				} catch ( oError ) {
					//alert(oError.message);
				} //- try

			}// function fncGetSuggestions()

			/**
			* AJAX callback function to load search
			* term suggestions
			*
			* @author	johns
			* @since	4/15/2010
			*
			* @return	void
			*/
			function fncLoadSuggestions ( oData ) {

				window.oSearch = oData;

				jQuery('#u_search_text')
					.autocomplete(oData.terms,{					
						select: function ( event, ui ) {

							/*
							*	get the search term selected from the auto complete list (json object)
							*/
							oResult = window.oSearch[ui.item.label];

							/*
							* check the term that matched an suggested term
							* has an specific url to redirect to
							*/	
							fncGetSearchTermRedirectUrl ( oResult );
							
						}
					});

			} //- function fncLoadSuggestions ( oData )		
			
			/**
			* This function determines if the term that matched an
			* suggested term has an specific url to redirect to.
			*
			* @author	johns
			* @since	7/30/2012
			*
			* @param	oResult (json object): term in the json object
			*				that matched the term entered by the user
			* 
			* @return	void
			*/
			function fncGetSearchTermRedirectUrl ( oResult ) {

				/*
				*	if the term matching the word being searched for
				*	has action = 'link' and the url is not blank then
				*	set the hidden input field for the search tip url
				*	to the url of the current term index
				*/
				if ( oResult.action == 'link' && oResult.url != '' ) {

					/*
					*	get the length of the name of the site currently on(needed
					*	to see if the sitename/virtualweb needs to be 
					*	stripped off)
					*/
					iLenSitename = sSitename.length;
					
					/*
					*	if on an admin server strip off the '/sitename/virtualweb/' off the 
					*	current page name (pulled from the Request.ServerVariables("SCRIPT_NAME")
					*/
					if ( sInitPage.substr(0,iLenSitename + 13) === '/' + sSitename + '/virtualweb/' ) {
						sInitPage = sInitPage.substr(iLenSitename + 13);
					} //- if ( sInitPage.substr(0,iLenSitename + 13) === '/' + sSitename + '/virtualweb/' )

					/*
					*	check to see if the static page is located in
					*	a directory (only assuming one directory deep off the root
					*	since static pages are only one directory deep 
					*	off the root)
					*/
					if ( sInitPage.indexOf('/') >= 0 ) {
						sResultUrl = '../' + oResult.url;
					} else {
						sResultUrl = oResult.url;
					} //- if ( sInitPage.indexOf('/') >= 0 )

					/*
					*	set the hidden input field to the url of the matched term
					*/
					jQuery('#search_tip_url').val(sResultUrl);

				} //- if ( oResult.action == 'link' && oResult.url != '' )

			} //- function fncGetSearchTermRedirectUrl ( oResult )

			/**
			* This function builds a searchstring based on the list of
			* split search terms and the list of fields to check.
			*
			* @author	johns
			* @since	7/30/2012
			*
			* @param	aSearchTerms (array): list of search terms
			* @param	aFieldsToCheck (array): list of fields to check
			* 
			* @return	string (WSP formatted searchstring)
			*/
			function fncBuildSearchString ( aSearchTerms, aFieldsToCheck ) {
				/*
				* string variables
				*/
					/*
					* New Search String Built
					* @author	johns
					* @since	08/15/2012
					* @var		string
					*/
					var sNewSearchString	= '';

				/*
				* boolean variables
				*/
					/*
					* Is this the First Field in the list of fields
					* to check in split term searching?
					* @author	johns
					* @since	08/14/2012
					* @var		bool
					*/
					var bFirstField			= true;

				/*
				* loop through the search terms split on whitespace
				*/					
				for ( var iTermCounter = 0, iTermGoTo = aSearchTerms.length - 1; iTermCounter <= iTermGoTo; iTermCounter++ ) {

					/*
					* loop through the fields to search list with the current search term
					*/
					for ( var iFieldCounter = 0, iFieldGoTo = aFieldsToCheck.length - 1; iFieldCounter <= iFieldGoTo; iFieldCounter++ ) {
						
						/*
						* check to see if we are at the first field
						* if so, add the opening parantheses
						*/
						if ( bFirstField ) {
							sNewSearchString += '(';
						} //- if ( bFirstField )

						/*
						* check to see if the searchstring var has assigned to anything yet
						* if so, concatentate '|Or|' to the end of the string
						*/
						if ( sNewSearchString != '' && ! bFirstField ) {
							sNewSearchString += '|Or|';
						} //- if ( sNewSearchString != '' && ! bFirstField )
						
						/*
						* build the searchstring with the current search term and field based
						* on the search type
						*/
						sNewSearchString += '(searchlike~' + aFieldsToCheck[iFieldCounter] + '~' + encodeURIComponent(aSearchTerms[iTermCounter]) + ')';
						
						/*
						* set the var to false that signals we are on the first field the next time 
						* through the loop
						*/
						bFirstField = false;

					} //- for ( var iFieldCounter = 0, iFieldGoTo = aFieldsToCheck.length - 1; iFieldCounter <= iFieldGoTo; iFieldCounter++ )
					
					/*
					* after looping through the fields to search with the current search term,
					* reset the var that signals we are on the first field the next time we
					* loop through them
					*/						
					bFirstField = true;
					
					/*
					* after looping through the fields to search with the current search term,
					* check to see if we just looped through the last search term
					*/
					if ( iTermCounter >= iTermGoTo ) {
						
						/*
						* if we are at the last search term, add the closing parantheses to 
						* the current search term
						*/
						sNewSearchString += ')';

					} else {
						
						/*
						* if we are not at the last search term, add the closing parantheses
						* to the current search term and concatenate '|And|' to the end of 
						* the searchstring
						*/
						sNewSearchString += ')|And|';

					} //- if ( iTermCounter >= iTermGoTo )

				} //- for ( var iTermCounter = 0, iTermGoTo = aSearchTerms.length - 1; iTermCounter <= iTermGoTo; iTermCounter++ )

				return sNewSearchString

			} //- function fncBuildSearchString ( aSearchTerms, aFieldsToCheck )

			jQuery(function() {
				jQuery('#u_search_text').keyup(function() {

					if ( bUseAutoSuggestions ) {
						fncGetSuggestions(this.value);
					} //- if ( bUseAutoSuggestions )

				});

				if (bUseAutoSuggestions) {
					jQuery('#search_keyword').autocomplete([]);
				}
			});

			jQuery(function() {
				if(utils && utils.getParameter('autosearch') == '1'){
					fncProcessUniversalSearch();
				}
			});
		</script>
            </div>
        </div>	
    </div>
  </header>
    <div id="home-cat-tiles" class="home-cat-tiles">
        <div class="container-fluid">
            <div class="row-fluid">
                <!-- Home Categories (Generic) -->
                <div class="home-categories">
<div class="home-categories-row">
<div class="home-categories-row-header">
<div>
<h2>&nbsp;</h2>
</div>
<div><a href="pc_combined_results.asp?pc_id=63C46F8D873E490CA4CA2718C617D8D7">View All Categories</a></div>
</div>
<div id="category-items" class="grid-container"><a href="pc_combined_results.asp?pc_id=C52C7711B0EE4309AF1338600688DDF3">
<div class="grid-item" style="background-image: url('images/category/Conveyor-Category-Image-Final.jpg');">
<div class="grid-item-content">
<h3>Conveyor</h3>
</div>
</div>
</a><a href="pc_combined_results.asp?pc_id=B1A7DED7A88B4B41ADD465477A965974">
<div class="grid-item" style="background-image: url('https://d1pq1nir82e5zy.cloudfront.net/images/resized_electrical category image final.png?v=3967289411');">
<div class="grid-item-content">
<h3>Electrical</h3>
</div>
</div>
</a><a href="pc_combined_results.asp?pc_id=4145F85039FB47FE8928203FE2BF386A">
<div class="grid-item" style="background-image: url('images/category/Baler-Category-Image-Final.jpg');">
<div class="grid-item-content">
<h3>Baler</h3>
</div>
</div>
</a><a href="pc_combined_results.asp?pc_id=3192CAB04E64475ABC628AD8CCC1B235">
<div class="grid-item" style="background-image: url('images/category/Star-Screen-Category-Image-Final.jpg');">
<div class="grid-item-content">
<h3>Star Screens</h3>
</div>
</div>
</a><a href="pc_combined_results.asp?pc_id=E63D2C9B45664C5F81CDE99FD6CDE8A5">
<div class="grid-item" style="background-image: url('https://d1pq1nir82e5zy.cloudfront.net/images/resized_motion category image final.png?v=3967289411');">
<div class="grid-item-content">
<h3>Motion</h3>
</div>
</div>
</a><a href="pc_combined_results.asp?pc_id=08FA79C1477549C1A1AB9BF0E82D1CCC">
<div class="grid-item" style="background-image: url('images/category/Optical-Sort-Category-Image-Final.jpg');">
<div class="grid-item-content">
<h3>Optical Sort</h3>
</div>
</div>
</a><a href="pc_combined_results.asp?pc_id=B9FB96C461634ED78BB25885F8C77CAC">
<div class="grid-item" style="background-image: url('https://d1pq1nir82e5zy.cloudfront.net/images/resized_hardware category image final.png?v=3967289411');">
<div class="grid-item-content">
<h3>Hardware</h3>
</div>
</div>
</a><a href="pc_combined_results.asp?pc_id=9247FEA668F44B8394BCCBB8B91C965C">
<div class="grid-item" style="background-image: url('https://d1pq1nir82e5zy.cloudfront.net/images/resized_hydraulics category image final.png?v=3967289411');">
<div class="grid-item-content">
<h3>Hydraulics</h3>
</div>
</div>
</a></div>
</div>
</div>
            </div>
        </div>
    </div>

<div id="page-main" class="page-main">
    <div class="container-fluid">

<script>
var timerStart = Date.now();  
var lastElapsed = 0; 
var pageHitDate = new Date();
var pageLoad = {
    pageHitKey: 'F62899EDD9D1428DB281FEEF4AD509CC',
	sessionId: '82BB8AB2C7D34A9E9DE80C164FD176D0',
	orderId: '',
	createDate: (pageHitDate.getMonth()+1) + "/"
                + pageHitDate.getDate() + "/" 
                + pageHitDate.getFullYear() + " "  
                + pageHitDate.getHours() + ":"  
                + pageHitDate.getMinutes() + ":" 
                + pageHitDate.getSeconds(),
    priorTimeFrom: '',
    priorTime: '',
    urlRoot: 'vandyk3.cimproduction.com',
    pageName: 'Default.asp',
    uri: window.location.href,
	querystring: '',
	logs: [],
    clientTime: 0,
    serverTime: 0,
	totalTime: 0,
    dbReadCount: 0,
    dbWriteCount: 0
}; 

function addTimer(s) {
	var elapsed = (Date.now()-timerStart); //KEEP milliseconds
	var log = {
        source: 3,
        stepWithinSource: pageLoad.logs.filter( function(obj) { obj.source === 3 }).length,
        nickname: s,
        stepTime: elapsed - lastElapsed,
        cumulativeTime: elapsed
	};
	pageLoad.logs.push(log);
	lastElapsed = elapsed;
}

function logPageLoad() {
    if(pageLoad.logs.length > 0) {
        //sum the server-side, object, & client-side times onto the header.
        var serverTime=0;
        var maxClientSide=0;
        var len = pageLoad.logs.length-1;
        $.each(pageLoad.logs,function(i,o){
            if(o.source==0) { //server-side`
                serverTime=serverTime+o.cumulativeTime;
                pageLoad.dbReadCount += o.dbReadCount;
                pageLoad.dbWriteCount += o.dbWriteCount;
            /*} else if(o.source==2) { //object.. but these times are actually included in the client-side time since it is an ajax call. Don't add it twice. */
            } else if(o.source==3 && o.cumulativeTime > maxClientSide) { // on the last client-side record
                maxClientSide=o.cumulativeTime;
            }
        });
        var pt = pageLoad.priorTime;
        if(pt != "" && !isNaN(pt)){
            pageLoad.priorTime = parseFloat(pt);           
        } else {
            pageLoad.priorTime = 0;
        }
        pageLoad.serverTime = serverTime;
        pageLoad.clientTime = maxClientSide
        pageLoad.totalTime = parseInt(serverTime + maxClientSide + pageLoad.priorTime); //trim decimals
        $.ajax({
            url: '/api/timers/paymentpage/F62899EDD9D1428DB281FEEF4AD509CC',
            data: JSON.stringify(pageLoad),
            type: "POST",
            contentType: "application/json"
        });
        drawPageHitData();
        pageLoad.logs = []; //<-- this is cleared so that the page hit isn't logged more than once.. See containing IF block...
    }
}

function drawPageHitData() {
    
}

function toSecString(ms) {
    var s = (parseInt(ms*10)/10); //only keep 1 decimal
    s = parseInt(s) / 1000;

    return s + "sec(s)";
}

function addPageLoadData(data) {
    /*
    OO data is an object with 3 props: reads, writes, details[]
    */
    if(data.reads) {
        pageLoad.dbReadCount += data.reads;
    }
    if(data.writes) {
        pageLoad.dbWriteCount += data.writes;
    }
    if(data.details) {
        pageLoad.logs = pageLoad.logs.concat(data.details);
    } 
}

addTimer('master top');
</script>

    </div>

    <!-- Retail Bot WPC Footer Email List -->
    <div class="footer-email-list">
        
    </div>
</div>
<footer id="retail-footer-1" class="retail-footer-1 print-hide">
    <div class="container-fluid retail-footer__area">
        <div class="retail-footer__col retail-footer__col-1">
            <a class="retail-footer__logo-wrap" href="/">
                <img src="https://d1pq1nir82e5zy.cloudfront.net/images/template/vdrs-lockup-mod-8-19-22-350.png?v=3973847574" class="retail-footer__logo" alt="Van Dyk Recycling Solutions">
            </a>
        </div>
        <div class="retail-footer__col retail-footer__col-2">
            <div class="nav-header">
                <span class="">Corporate</span>
            </div>
            <address class="retail-footer__address">
                
                <span class="retail-footer__company-name">
                    Van Dyk Recycling Solutions
                </span>
                <br>
                <span class="retail-footer__address-text">
                    360 Martin Luther King Drive, Norwalk, CT 06854
                </span>
                
                    <br>
                    <a class="retail-footer__email" href="mailto:Service@vdrs.com?subject=Website Customer Service Request"><i class="icon-envelope icon-fixed-width"></i>Customer Service</a>
                
                    <br>
                    <a class="retail-footer__phone" href="tel:203-967-1100"><i class="icon-phone icon-fixed-width"></i>203-967-1100</a>
                
            </address>
        </div>

            <div class="retail-footer__col retail-footer__col-3 -hidden-if-empty">
                
		<span class="missing-linkset retail-footer-nav__social"></span><!-- No links found in 'retail-footer-nav__social' -->
            </div>

            <div class="retail-footer__col retail-footer__col-4 -hidden-if-empty">
                <ul class="retail-footer__group linkset click">
                    <li class="retail-footer__link-parent link-parent drop">
                        <span class="nav-header show-mobile">Navigation <i class="icon-plus"></i>
                            <i class="icon-plus"></i>
                        </span>
                        
		<span class="missing-linkset retail-footer-nav__menu"></span><!-- No links found in 'retail-footer-nav__menu' -->
                    </li>
                </ul>
            </div>

            <div class="retail-footer__col retail-footer__col-5 -hidden-if-empty">
                <ul class="retail-footer__col-group linkset click">
                    <li class="retail-footer__link-parent link-parent drop">
                        <span class="nav-header show-mobile">
                            Product Categories <i class="icon-plus"></i>
                        </span>
                        
		<span class="missing-linkset retail-footer-nav__categories"></span><!-- No links found in 'retail-footer-nav__categories' -->
                    </li>
                </ul>
            </div>
    </div>
    <div id="bottom-bar" class="bottom-bar">
        <div class="container-fluid">
            <div class="bottom-bar__copyright">
                <p>&copy;2026. Van Dyk Recycling Solutions. All rights reserved.</p>
            </div>
            <div class="bottom-bar__colophon">
                    <p>Powered by <a href="https://www.cimcloud.com" rel="nofollow" target="_blank">CIMcloud</a></p>
            </div>
        </div>
    </div>
</footer>

<script type="text/javascript">

    // This hides any linksets in the footer that don't have any links
    var footerNavGroups = document.querySelectorAll(".retail-footer__col.-hidden-if-empty");
    Array.prototype.forEach.call(footerNavGroups, function(group) {
        group.querySelector("ul[class*='retail-footer-nav']") ? "" : group.classList.add("hidden");
    });

    /*
        Fixes the mobile nav header since it doesn't pull from the nav link name. 
        Updating the nav link name only renders on desktop.  this will copy that value
        to the mobile header.
        This will work with the core selectors, if the footer markup/classes change, 
        this will likely break.
    */
    var mobileNav = jQuery('.retail-footer__col-4 .nav-header.show-mobile').html();
    var desktopNav = jQuery('#retail-footer-nav__menu .nav-header a').html();
    jQuery('.retail-footer__col-4 .nav-header.show-mobile').html(
        mobileNav.replace('Navigation', desktopNav)
    );
    /*
        Now fix the category menu header the same way.
    */    
    var mobileCats = jQuery('.retail-footer__col-5 .nav-header.show-mobile').html();
    var desktopCats = jQuery('#retail-footer-nav__categories .nav-header a').html();
    jQuery('.retail-footer__col-5 .nav-header.show-mobile').html(
        mobileCats.replace('Product Categories', desktopCats)
    );
</script>
<div class="global-body-append">
    
	<span class="app-logo__validip-info btn btn-mini">
		<img src="https://d1pq1nir82e5zy.cloudfront.net/images/cimcloud-logo-icon.png?v=3973847574" style="height: 12px;">
		<a href="#siteInfo" role="button" data-toggle="modal" class="site-info">	
			<span title="Currently on version vandyk3.isoconversionfix.5@4.12.0" style="color: #00A3E0;">vandyk3 (vandyk3.isoconversionfix.5@4.12.0)</span>
		</a>&nbsp;&nbsp;
	</span>
	<div id="siteInfo" class="modal hide fade" tabindex="-1"  aria-hidden="true">
		<header class="modal-header">
			<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
			<h3 id="leaveCheckoutLabel"><i class="icon-info-sign"></i> Site Information</h3>
		</header>
		<div class="modal-body app-logo__validip-body">
			
		
		<table border="0" cellpadding="0" cellspacing="0">
            <tr>
				<td class="pageInfoLabel">Version:</td>
				<td>vandyk3.isoconversionfix.5@4.12.0</td>
			</tr>
				<tr>
					<td class="pageInfoLabel">ServerName:</td>
					<td>WEBSERVER</td>
				</tr>
				<tr>
					<td class="pageInfoLabel">WS_ID:</td>
					<td>3270DF9DFC5E4BB09A3BEADA3FC0A257</td>
				</tr>
				<tr>
					<td class="pageInfoLabel">L_WS_ID:</td>
					<td>3270DF9DFC5E4BB09A3BEADA3FC0A257</td>
				</tr>
			<tr>
				<td class="pageInfoLabel">SiteName:</td>
				<td>vandyk3</td>
			</tr>
			<tr>
				<td class="pageInfoLabel">Application URL:</td>
				<td>https://vandyk3.cimproduction.com</td>
			</tr>
			<tr>
				<td class="pageInfoLabel">Workspace URL:</td>
				<td>https://vandyk3.mycimcloud.com</td>
			</tr>
				<tr>
					<td class="pageInfoLabel">OrderFrontURL:</td>
					<td>https://vandyk3.cimproduction.com</td>
				</tr>
				<tr>
					<td class="pageInfoLabel">ALTERNATE_DB_NAME:</td>
					<td>vandyk3</td>
				</tr>
				<tr>
					<td class="pageInfoLabel">ALTERNATE_DB_SERVER:</td>
					<td>sql04-db03.i2network.local.\db03</td>
				</tr>
				<tr>
					<td class="pageInfoLabel">FileName:</td>
					<td>c:\inetpub\wwwroot\WebSite\default.asp</td>
				</tr>
				<tr>
					<td class="pageInfoLabel">Local Commit Hash:</td>
					<td>44cc3041f2e241b7e0b54b8792ca5b6a23a7b367</td>
				</tr>
				<tr>
					<td class="pageInfoLabel">SCRIPT_NAME:</td>
					<td>/Default.asp</td>
				</tr>
				<tr>
					<td class="pageInfoLabel">HTTP_X_ORIGINAL_URL:</td>
					<td></td>
				</tr>
				<tr>
					<td class="pageInfoLabel">getUrl():</td>
					<td>/Default.asp?</td>
				</tr>
			<tr>
				<td class="pageInfoLabel">cimcloud.helpers.urlPath()</td>
				<td>/Default.asp</td>
			</tr>
			<tr>
				<td class="pageInfoLabel">cimcloud.helpers.pageName</td>
				<td>Default.asp</td>
			</tr>
		</table>
		</div>
		<div class="modal-footer">
			<a class="app-logo__validip-link btn btn-danger" href="https://vandyk3.cimproduction.com/?action=logout"><i class="fas fa-power-off"></i> Sign Out</a>
			<a class="app-logo__validip-link btn" href="https://vandyk3.cimproduction.com/session_info.asp?key=82BB8AB2C7D34A9E9DE80C164FD176D0"><i class="fas fa-code"></i> Session Info</a>
			<a class="app-logo__validip-link btn" href="https://k8info.cimcloud.com/api/kubernetes/production/vandyk3/" target="_blank"><i class="fab fa-docker"></i> K8 Info</a>
			<a id="toggleTimerBtn" class="app-logo__validip-link btn" onclick="toggleTimers()"><i class="fas fa-clock"></i> Toggle Timers</a>
			
				<a class="app-logo__validip-link btn" target="_blank" href="https://bitbucket.org/websitepipeline/vandyk3" target="_blank"><i class="fab fa-bitbucket"></i> Bitbucket Repo</a>
			
		</div>
	</div>
	<script>
		$('#demoToolsBtn').click(function() {
			$('.modal').modal('hide');
		});

		function toggleTimers() {
			$.ajax({url: "?toggle=timers&modal=1", 
					success: function(data){
						if(data == "ON") {
							$("#toggleTimerBtn").addClass("btn-danger");
						} else {
							$("#toggleTimerBtn").removeClass("btn-danger");
						}
						utils.popToastr('Success', 'Timers are now ' + data, { 'positionClass' : 'toast-bottom-right' });
					}
				});
		}
	</script><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="hide">
  <symbol id="icon-user-circle-o" viewBox="0 0 28 28">
    <path d="M14 0c7.734 0 14 6.266 14 14 0 7.688-6.234 14-14 14-7.75 0-14-6.297-14-14 0-7.734 6.266-14 14-14zM23.672 21.109c1.453-2 2.328-4.453 2.328-7.109 0-6.609-5.391-12-12-12s-12 5.391-12 12c0 2.656 0.875 5.109 2.328 7.109 0.562-2.797 1.922-5.109 4.781-5.109 1.266 1.234 2.984 2 4.891 2s3.625-0.766 4.891-2c2.859 0 4.219 2.312 4.781 5.109zM20 11c0-3.313-2.688-6-6-6s-6 2.688-6 6 2.688 6 6 6 6-2.688 6-6z"></path>
  </symbol>
  <symbol id="icon-address-card" viewBox="0 0 32 28">
    <path d="M16 17.672c0-2.422-0.594-5.109-3.063-5.109-0.766 0.438-1.797 1.188-2.938 1.188s-2.172-0.75-2.938-1.188c-2.469 0-3.063 2.688-3.063 5.109 0 1.359 0.891 2.328 2 2.328h8c1.109 0 2-0.969 2-2.328zM13.547 9.547c0-1.953-1.594-3.547-3.547-3.547s-3.547 1.594-3.547 3.547c0 1.969 1.594 3.547 3.547 3.547s3.547-1.578 3.547-3.547zM28 17.5v-1c0-0.281-0.219-0.5-0.5-0.5h-9c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h9c0.281 0 0.5-0.219 0.5-0.5zM28 13.438v-0.875c0-0.313-0.25-0.562-0.562-0.562h-8.875c-0.313 0-0.562 0.25-0.562 0.562v0.875c0 0.313 0.25 0.562 0.562 0.562h8.875c0.313 0 0.562-0.25 0.562-0.562zM28 9.5v-1c0-0.281-0.219-0.5-0.5-0.5h-9c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h9c0.281 0 0.5-0.219 0.5-0.5zM32 4.5v19c0 1.375-1.125 2.5-2.5 2.5h-5.5v-1.5c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1.5h-12v-1.5c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1.5h-5.5c-1.375 0-2.5-1.125-2.5-2.5v-19c0-1.375 1.125-2.5 2.5-2.5h27c1.375 0 2.5 1.125 2.5 2.5z"></path>
  </symbol>
  <symbol id="icon-building" viewBox="0 0 22 28">
    <path d="M21 0c0.547 0 1 0.453 1 1v26c0 0.547-0.453 1-1 1h-20c-0.547 0-1-0.453-1-1v-26c0-0.547 0.453-1 1-1h20zM8 4.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5zM8 8.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5zM8 12.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5zM8 16.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5zM6 21.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM6 17.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM6 13.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM6 9.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM6 5.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM14 25.5v-3c0-0.281-0.219-0.5-0.5-0.5h-5c-0.281 0-0.5 0.219-0.5 0.5v3c0 0.281 0.219 0.5 0.5 0.5h5c0.281 0 0.5-0.219 0.5-0.5zM14 17.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM14 13.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM14 9.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM14 5.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM18 21.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM18 17.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM18 13.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM18 9.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5zM18 5.5v-1c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5v1c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5z"></path>
  </symbol>
  <symbol id="icon-currency-dollar" viewBox="0 0 20 20">
    <path d="M10 20c-5.523 0-10-4.477-10-10s4.477-10 10-10v0c5.523 0 10 4.477 10 10s-4.477 10-10 10v0zM11 15h1c1.657 0 3-1.343 3-3s-1.343-3-3-3v0h-4.010c-0.552 0-1-0.448-1-1s0.448-1 1-1v0h6.010v-2h-3v-2h-2v2h-1c-1.657 0-3 1.343-3 3s1.343 3 3 3v0h4c0.552 0 1 0.448 1 1s-0.448 1-1 1v0h-6v2h3v2h2v-2z"></path>
  </symbol>
  <symbol id="icon-check-square" viewBox="0 0 24 28">
    <path d="M10.703 20.297l9.594-9.594c0.391-0.391 0.391-1.016 0-1.406l-1.594-1.594c-0.391-0.391-1.016-0.391-1.406 0l-7.297 7.297-3.297-3.297c-0.391-0.391-1.016-0.391-1.406 0l-1.594 1.594c-0.391 0.391-0.391 1.016 0 1.406l5.594 5.594c0.391 0.391 1.016 0.391 1.406 0zM24 6.5v15c0 2.484-2.016 4.5-4.5 4.5h-15c-2.484 0-4.5-2.016-4.5-4.5v-15c0-2.484 2.016-4.5 4.5-4.5h15c2.484 0 4.5 2.016 4.5 4.5z"></path>
  </symbol>
  <symbol id="icon-life-bouy" viewBox="0 0 28 28">
    <path d="M14 0c7.734 0 14 6.266 14 14s-6.266 14-14 14-14-6.266-14-14 6.266-14 14-14zM14 2c-2.031 0-3.953 0.516-5.641 1.406l3.031 3.031c0.828-0.281 1.703-0.438 2.609-0.438 0.922 0 1.781 0.156 2.609 0.438l3.031-3.031c-1.687-0.891-3.609-1.406-5.641-1.406zM3.406 19.641l3.031-3.031c-0.281-0.828-0.438-1.703-0.438-2.609 0-0.922 0.156-1.781 0.438-2.609l-3.031-3.031c-0.891 1.687-1.406 3.609-1.406 5.641s0.516 3.953 1.406 5.641zM14 26c2.031 0 3.953-0.516 5.641-1.406l-3.031-3.031c-0.828 0.281-1.687 0.438-2.609 0.438-0.906 0-1.781-0.156-2.609-0.438l-3.031 3.031c1.687 0.891 3.609 1.406 5.641 1.406zM14 20c3.313 0 6-2.688 6-6s-2.688-6-6-6-6 2.688-6 6 2.688 6 6 6zM21.562 16.609l3.031 3.031c0.891-1.687 1.406-3.609 1.406-5.641s-0.516-3.953-1.406-5.641l-3.031 3.031c0.281 0.828 0.438 1.703 0.438 2.609s-0.156 1.781-0.438 2.609z"></path>
  </symbol>
  <symbol id="icon-calendar-plus-o" viewBox="0 0 26 28">
    <path d="M24 4c1.094 0 2 0.906 2 2v20c0 1.094-0.906 2-2 2h-22c-1.094 0-2-0.906-2-2v-20c0-1.094 0.906-2 2-2h2v-1.5c0-1.375 1.125-2.5 2.5-2.5h1c1.375 0 2.5 1.125 2.5 2.5v1.5h6v-1.5c0-1.375 1.125-2.5 2.5-2.5h1c1.375 0 2.5 1.125 2.5 2.5v1.5h2zM18 2.5v4.5c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5v-4.5c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5zM6 2.5v4.5c0 0.281 0.219 0.5 0.5 0.5h1c0.281 0 0.5-0.219 0.5-0.5v-4.5c0-0.281-0.219-0.5-0.5-0.5h-1c-0.281 0-0.5 0.219-0.5 0.5zM24 26v-16h-22v16h22zM14 17h3.5c0.281 0 0.5 0.219 0.5 0.5v1c0 0.281-0.219 0.5-0.5 0.5h-3.5v3.5c0 0.281-0.219 0.5-0.5 0.5h-1c-0.281 0-0.5-0.219-0.5-0.5v-3.5h-3.5c-0.281 0-0.5-0.219-0.5-0.5v-1c0-0.281 0.219-0.5 0.5-0.5h3.5v-3.5c0-0.281 0.219-0.5 0.5-0.5h1c0.281 0 0.5 0.219 0.5 0.5v3.5z"></path>
  </symbol>
  <symbol id="icon-sticky-note" viewBox="0 0 24 28">
    <path d="M16 19.5v6.5h-14.5c-0.828 0-1.5-0.672-1.5-1.5v-21c0-0.828 0.672-1.5 1.5-1.5h21c0.828 0 1.5 0.672 1.5 1.5v14.5h-6.5c-0.828 0-1.5 0.672-1.5 1.5zM18 20h5.953c-0.141 0.75-0.547 1.594-1.016 2.063l-2.875 2.875c-0.469 0.469-1.313 0.875-2.063 1.016v-5.953z"></path>
  </symbol>
  <symbol id="icon-document-add" viewBox="0 0 32 32">
    <path d="M21 25v-3h1v3h3v1h-3v3h-1v-3h-3v-1h3zM17.257 29v0 0c1.009 1.221 2.535 2 4.243 2 3.038 0 5.5-2.462 5.5-5.5 0-2.137-1.219-3.99-3-4.9v-11.6l-6-7h-10.997c-1.106 0-2.003 0.898-2.003 2.007v22.985c0 1.109 0.891 2.007 1.997 2.007h10.26zM16.6 28h-9.6c-0.545 0-1-0.446-1-0.995v-23.009c0-0.54 0.446-0.995 0.996-0.995h10.004v4.994c0 1.119 0.895 2.006 1.998 2.006h4.002v10.207c-0.477-0.135-0.98-0.207-1.5-0.207-3.038 0-5.5 2.462-5.5 5.5 0 0.9 0.216 1.75 0.6 2.5v0 0zM18 3.5l4.7 5.5h-3.703c-0.546 0-0.997-0.452-0.997-1.009v-4.491zM21.5 30v0c-2.485 0-4.5-2.015-4.5-4.5s2.015-4.5 4.5-4.5c2.485 0 4.5 2.015 4.5 4.5s-2.015 4.5-4.5 4.5z"></path>
  </symbol>
  <symbol id="icon-document-add1" viewBox="0 0 32 32">
    <path d="M21 25v-3h1v3h3v1h-3v3h-1v-3h-3v-1h3zM16.022 29h-9.024c-1.107 0-1.997-0.899-1.997-2.007v-22.985c0-1.109 0.899-2.007 2.009-2.007h9.991v6.002c0 1.111 0.898 1.998 2.006 1.998h4.994v9.498c-0.77-0.321-1.614-0.498-2.5-0.498-3.59 0-6.5 2.91-6.5 6.5 0 1.289 0.375 2.49 1.022 3.5v0 0zM18 2v5.997c0 0.554 0.451 1.003 0.991 1.003h5.009l-6-7zM21.5 31c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5c-3.038 0-5.5 2.462-5.5 5.5s2.462 5.5 5.5 5.5v0z"></path>
  </symbol>
</svg>
</div><div class="retail-scripts-bot">
	<div class="global-scripts">
    <script>

        var jsConfig = {
            static_pages: {
                content_key: '',
                content_type: ''
            }
        }
        if(processPageTitle){
            //$('.page-header h1').html('&nbsp;').show();
            var originalPageTitle = $('.page-header h1').html();
            var originalSubmitText = $('.form-actions button').html();
            var pageTitleText = "";
            if(pageTitleText.trim() != ''){
                $('.page-header h1').html(pageTitleText);
                $('.form-actions button').html( 'Save ' + pageTitleText);
                if(isWorkerDomain) pageTitleText = sitename.toUpperCase() + ' - ' + pageTitleText;
                document.title = pageTitleText;
            }else if (originalPageTitle){
                pageTitleText = originalPageTitle;
                $('.form-actions button').html( 'Save ' + pageTitleText);
                if(isWorkerDomain) pageTitleText = sitename.toUpperCase() + ' - ' + pageTitleText;
                document.title = pageTitleText;                
            }
            $('.page-header h1').show();
            $('.form-actions button').show();
        }
    </script>

    <script src="../hooks/js_custom_functions.js?44cc304"></script>
    <script src="js/vendor/stretchy.min.js?44cc304" data-filter=".stretchy, .stretchy *"></script><script>
  // Components Engine Spare Parts Catalog - 3rd party exploded view catalog 
  if (cimcloud.helpers.pageKey === '0B6C8C4D168242CA866AA73DF34AAFF8' || cimcloud.helpers.pageKey === 'D0DDDB67B4C04C27836CB57760B2E21D') {    
    // these pageKeys are two different product categories; one is used for production and the other for admin 
    // we are using category pages because the addToCart helper function is currently only available on prod catalog pages
    const environment = cimcloud.helpers.pageKey === 'D0DDDB67B4C04C27836CB57760B2E21D' ? 'production' : 'admin';
  
    window.onload = function () {
      // show loading widget while CE spare parts catalog loads
      $('#appLoadingWidget').show();
  
      // styling updates to the category page for displaying the CE spare parts catalog 
      const sidebar = document.getElementById('catalog-sidebar');
      const mainPage = document.getElementById("page-main");
      const mainContainer = document.querySelector("#page-main .container-fluid");
      const catMain = document.getElementById("catalog-main");
      const listWrap = document.getElementById('list_wrap');
  	  const recentlyViewed = document.getElementById('recently_viewed_products');
  
      if (sidebar) {
    	sidebar.remove();
      }
      if (listWrap) {
      	listWrap.remove();
      }
      if (recentlyViewed) {
  	    recentlyViewed.remove();
  	  }
      if (catMain) {
  	    catMain.style.width = "100%";
      }
  	  mainPage.style.paddingTop = "5px";
      mainContainer.style.padding = "0";
      mainContainer.style.maxWidth = "none";
  
      // load CE spare parts catalog in an iframe
      const iframe = document.createElement('iframe');
      let iframeUrl;
    
      // set the base URL depending on enviroment - admin vs production
      if (environment === 'production') {
        iframeUrl = 'https://parts.shopvandykdirect.com';
      } else if (environment === 'admin') { 
        iframeUrl = 'https://parts.shopvandykdirect.com/amministrazione';
      } 
      
      // append querystring parameters 
      iframeUrl = iframeUrl + '/site/pagece5.wplus?ID_COUNT=ce_5_home&CEPV=Vandyk001&AUTOSTARTCEPV=1&UID=' + 
                  cimcloud.session.username + '&SESSIONID=' + cimcloud.session.sessionKey;
  
      // configure and load iframe
      iframe.src = iframeUrl;
      iframe.style.width = "100%";
      iframe.style.height = "100vh";
      iframe.style.border = "none";
  
      // after iframe loads, remove loading widget
      iframe.addEventListener("load", function() {
        $('#appLoadingWidget').hide();
      });

      document.getElementById('detail_page').appendChild(iframe);
    };
  
    // add listener event for add to cart calls from CE spare parts catalog 
    window.addEventListener('message', function (event) {
      // Verify the origin of the message is from Components Engine
      if (event.origin !== "https://parts.shopvandykdirect.com") {
        return;
      }
      const message = event.data;
      if (message && message.type === "addToCart" && Array.isArray(message.items)) {
        try {
          cimcloud.catalog.addToCart(message.items);
        } catch (e) {
          console.log("Failed to add to cart");
        }
      }
    }); 
  }
</script>

<style>
  .retail-footer-1 .retail-footer__area {
    padding: 1rem;
  }

  .retail-footer-1 .retail-footer__col-1 {
    display: none;
  }

  .retail-footer-1 .retail-footer__col-2 {
    flex-basis: initial;
    width: 100%;
  }

  .retail-footer-1 .retail-footer__col-2 .nav-header {
    display: none;
  }

  .retail-footer-1 .retail-footer__col-2 br {
    display: none;
  }

  .retail-footer-1 .retail-footer__col-2 .retail-footer__company-name {
    display: none;
  }

  .retail-footer-1 .retail-footer__col-2 .retail-footer__phone {
    order: 0;
    text-decoration: none;
  }

  .retail-footer-1 .retail-footer__col-2 .retail-footer__email {
    order: 1;
    text-decoration: none;
  }

  .retail-footer-1 .retail-footer__col-2 a.retail-footer__phone i:before,
  .retail-footer-1 .retail-footer__col-2 a.retail-footer__email i:before {
    color: #E16F42;
    font-size: large;
  }

  .retail-footer-1 .retail-footer__col-2 a.retail-footer__phone:hover,
  .retail-footer-1 .retail-footer__col-2 a.retail-footer__email:hover {
    color: #E16F42;
  }

  .retail-footer-1 .retail-footer__col-2 .retail-footer__phone:visited,
  .retail-footer-1 .retail-footer__col-2 .retail-footer__email:visited {
    color: #ffffff;
    ;
  }

  .retail-footer-1 .retail-footer__col-2 .retail-footer__address-text {
    order: 2;
    margin: auto 0;
  }

  .retail-footer-1 .retail-footer__col-2 .retail-footer__address-text:before {
    content: "\f041";
    color: #E16F42;
    font-family: 'FontAwesome';
    font-size: large;
    margin-right: 3px;
  }

  .retail-footer-1 .bottom-bar__copyright::before {
    content: url('/images/logos/logo-footer.png');
  }

  .retail-footer-1 .bottom-bar__copyright p {
    color: #133478;
    font-size: large;
    font-family: 'Roboto', sans-serif;
    font-weight: 500;
  }

  [class*="retail-footer"] .bottom-bar {
    color: #133478;
    padding: 1rem;
  }

  .retail-footer-1 .bottom-bar__copyright a:hover {
    text-deocration: underline;
  }

  .retail-footer-nav__social {
    font-size: 0;
  }

  .retail-footer-nav__social i {
    font-size: 24px;
    background-color: #E16F42;
    border-radius: 100%;
    line-height: 36px;
  }

  .icon-facebook:before {
    margin-right: 5px;
  }

  @media(max-width: 980px) {

    .retail-footer-1 .retail-footer__col-2 .retail-footer__phone:after,
    .retail-footer-1 .retail-footer__col-2 .retail-footer__email:after {
      content: '|';
      margin-left: 1rem;
    }

    .retail-footer-1 address {
      display: inline-flex;
      width: 100%;
      gap: 1rem;
    }

    .retail-footer-1 .bottom-bar__copyright {
      display: block;
    }

    .retail-footer-1 .bottom-bar__copyright p {
      text-align: center;
      margin: auto 0 auto auto;
    }
  }

  @media(max-width: 768px) {
    .retail-footer-1 address {
      display: block;
      width: 100%;
    }

    .retail-footer-1 .retail-footer__col-2 .retail-footer__phone:after,
    .retail-footer-1 .retail-footer__col-2 .retail-footer__email:after {
      content: '';
      margin-left: 1rem;
    }

    .retail-footer-1 .retail-footer__col-2 .retail-footer__address-text {
      width: 100%;
      display: block;
      margin-bottom: 4px;
    }

    .retail-footer-1 .bottom-bar__copyright {
      display: flex;
      width: 100%;
    }

    .retail-footer-1 .bottom-bar__copyright p {
      text-align: right;
      margin: auto;
    }
  }

  @media(min-width: 981px) {
    .retail-footer-nav__social {
      display: flex;
      gap: 4px;
    }

    .retail-footer-1 .retail-footer__col-2 .retail-footer__phone:after,
    .retail-footer-1 .retail-footer__col-2 .retail-footer__email:after {
      content: '|';
      margin-left: 1rem;
    }

    .retail-footer-1 address {
      display: inline-flex;
      width: 100%;
      gap: 1rem;
    }

    .retail-footer-1 .bottom-bar .container-fluid {
      display: flex;
    }

    .retail-footer-1 .bottom-bar__copyright {
      display: flex;
      width: 90%;
    }

    .retail-footer-1 .bottom-bar__copyright p {
      text-align: right;
      margin: auto 2% auto auto;
    }

    .retail-footer-1 .bottom-bar__colophon {
      width: 10%;
      display: flex;
    }

    .retail-footer-1 .bottom-bar__colophon p {
      margin: auto;
    }
  }
</style>

<script>
  if (window.location.href.includes('payment')) {
    var modalElement = document.getElementById('global_modal');


    if (modalElement) {
      modalElement.style.width = '800px';
    }
  }
  
  if (cimcloud.helpers.pageType() == 'catalog' && !cimcloud.session.isLoggedIn) {
    const loginMessage = document.createElement('div');
    const container = document.getElementById('detail_page');
  
  	loginMessage.classList.add("alert", "alert-primary");
  	loginMessage.innerHTML = 'Please <a href="security_logon.asp">log in</a> to view and order parts.';
    if (container) {
  		container.insertAdjacentElement('afterend', loginMessage);
  	}
  }
</script>



    <noscript class="noscript">
        WARNING: You will not be able to place an order or use most features of this site with JavaScript disabled
    </noscript>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/js/all.min.js" integrity="sha512-Sr1M6mlMOXTKahO1wcUzFu/kAb3iZVaQWGvxOEePRm7c2NjGRZ7ckRT6218PaSXlz8eEoFpKkDVvl2rTqKrQLA==" crossorigin="anonymous" referrerpolicy="no-referrer" async></script>

<script src="/js/bundles/coreBot.js?44cc3041f2e241b7e0b54b8792ca5b6a23a7b367" ></script>


<script src="/js/bundles/coreBotPlugins.js?44cc3041f2e241b7e0b54b8792ca5b6a23a7b367" ></script>
    <script src="../js/retail.js?44cc304"></script>
    
</div>

<div id="appLoadingWidget" class="sk-three-bounce--full-screen app-loading hide">
    <div class="sk-three-bounce">
        <div class="sk-child sk-bounce1"></div>
        <div class="sk-child sk-bounce2"></div>
        <div class="sk-child sk-bounce3"></div>
    </div>
</div>

    <!-- KO Templates -->
    
<script>
	var oConfig = {

		sessionData : {
			sc_id              : "94B6248E99744092A661DBC70E0FE29B",
			sessionKey         : "82BB8AB2C7D34A9E9DE80C164FD176D0",
			storefrontUrl      : "https://vandyk3.cimproduction.com",
			orderfrontUrl      : "https://vandyk3.cimproduction.com",
			cdnUrl             : "https://d1pq1nir82e5zy.cloudfront.net",
			isSuperUserSession : false,
			cartPage           : "showcart.asp"
		}
	}; 
	
	const additionalFields = [
		'content1',
		'content2',
		'content3',
		'content4',
		'opt1',
		'opt2',
		'opt3',
		'opt4',
		'opt5',
		'opt6',
		'opt7',
		'opt8',
		'opt9'
	];
</script>
<div class="catalog-templates-scripts">




	<script>
		utils.isMainProduct = function (product) {
			return product && ko.unwrap(viewModel.mainProduct) === product;
		};

		utils.buildQuickAddLink = function(product){
			var link = product.link();
			var orderKey = utils.getParameter('o_key');
			if(orderKey){
				orderKey = '&o_key=' + orderKey;
			}
			if(link.includes('?')){
				link += '&quickadd=true' + orderKey;
			}else{
				link += '?quickadd=true' + orderKey;
			}
			return link;
		};

		utils.setCookie = function (name, value) {
			//need to also set expiry date?
			document.cookie = name + '=' + value + ';path=/;';
		};

		// www.the-art-of-web.com/javascript/getcookie/
		utils.getCookie = function (name) {
			var re = new RegExp('\\b' + name + '=([^;]+)');
			var value = re.exec(document.cookie);
			return value ? unescape(value[1]) : undefined;
		};

		utils.isActiveQuote = ((utils.getParameter('modalaction') == 'addProduct' || utils.getParameter('modalaction', parent) == 'addProduct') && "".length == 32) && "" != "94B6248E99744092A661DBC70E0FE29B";

		//http://stackoverflow.com/questions/5999118/add-or-update-query-string-parameter
		utils.setParameter = function (key, value, uriArg) {
			var uri = uriArg || window.location.toString();
			var re = new RegExp('([?&])' + key + '=.*?(&|$)', 'i');
			var separator;
			if (uri.indexOf('?') === -1) {
				separator = '?';
			} else {
				if (uri[uri.length - 1] === '?') {
					separator = '';
				} else {
					separator = '&';
				}
			}
			if (uri.match(re)) {
				return uri.replace(re, '$1' + key + '=' + value + '$2');
			} else {
				return uri + separator + key + '=' + value;
			}
		};

		utils.handleImageError = function (img) {
			handleImageError(img, oConfig.noImagePath);
		};

		utils.drawHidePriceMessage = function(){
			return "";
		}

		utils.removeHTML = function(text){
			return $(text).text();
		}

		utils.decodeHTML = function(text) {
			return $('<textarea>').html(text).text()
		};

		utils.getStoredBreadcrumbs = function() {
			try {
				return JSON.parse(localStorage.getItem('breadcrumbs'));
			} catch(e) {
				return null;
			}
		};

		utils.setStoredBreadcrumbs = function(obj) {
			try {
				localStorage.setItem('breadcrumbs', JSON.stringify(obj));
			} catch(e) {
				// nothing to do if it didn't work
				console.error('failed to save breadcrumbs');
			}
		};

		utils.breadCrumbProperty = function(link) {
			if(link == '' || link === null){
				return '';
			} else {
				return 'itemListElement';
			}
		};

		utils.breadCrumbItemType = function(link) {
			if(link == '' || link === null){
				return '';
			} else {
				return 'ListItem';
			}
		};

		utils.pageUrl = location.pathname + location.search;
		if(utils.pageUrl.charAt(0) == '/') {
			utils.pageUrl = utils.pageUrl.substring(1);
		}
		utils.loginUrl = 'security_logon.asp?autopage=' + encodeURIComponent(utils.pageUrl);

		utils.formatMoney = (function (positiveExample, negativeExample) {
			/*
			This takes an example of a formatted currency and returns a function
			to format numbers in that same format. The first argument is the
			number 1111.2222 formatted with the desired default number of
			decimals. The second argument is the additive inverse of the first.
			For example:
			$1,111.22
			¥ 1,111.22
			€1.111,22
			1 111,22F
			*/
			'use strict';
			function buildFormatter(example) {
				var thousandsSeperator = getGroup(/1([^1]?)1/);
				var decimalSymbol = getGroup(/1([^12]?)2/);
				var decimalPlaces = (example.match(/2/g) || []).length;
				var thousandsRegex = /(\d)(?=(?:\d{3})+$)/g;

				var escapedDecimalSymbol = _.escapeRegExp(decimalSymbol);
				var symbolRegex = new RegExp('^([^1' + escapedDecimalSymbol + ']*).+?([^12' + escapedDecimalSymbol + ']*)$');
				var symbolMatch = symbolRegex.exec(example);
				var prefix = symbolMatch ? symbolMatch[1] || '' : '';
				var suffix = symbolMatch ? symbolMatch[2] || '' : '';
                var infoLost = 0;

				function getGroup(pattern) {
					var match = pattern.exec(example)
					return match ? match[1] : '';
				}

				function decimalToString(decimal, places) {
					if (!places) {
						return '';
					} else {
						var result = getZeroString(places);

						if (decimal) {
							// Converting to an integer and rounding helps with
							// floating point errors.
							var int = Math.round(decimal * Math.pow(10, places));
							// Because leading zeroes will be dropped after
							// converting to integer, the string needs to be
							// padded with zeroes after being converted to a
							// string.
							result = (result + int.toString()).slice(-places);
						}
                        if(int==Math.pow(10,places)) {
                            // in this case, we have to track a +1 to add to the result because "100" sliced down to 2 places loses info
							/*
							* 2020-07-22 - EJ - More info: This is due to things like utils.formatPrice(1.995, 2) returning 1.00 instead
							* of 2.00 or utils.formatPrice(1.9995, 3) returning 1.000 instead of 2.000
							* This is because the whole number is truncated, then the decimal is rounded and only the decimal portion
							* is added back to the whole number portion.
							* This means the rounded decimal portion is always 1 or less.  If it's less than 1 (ex .89) this works.
							* However, if it's 1.000, it would "lose" 1 since only the decimal portion is added back.
							* PS - Don't ask me about the original design, I didn't design it that way.  I only corrected the "if"
							*      statement above where it was hard-coded to 100 to be Math.pow(10,places) so it would work when
							*      rounded to something other than 2 decimal places.
							*/
                            infoLost = 1;
                        } else {
                            infoLost = 0;
                        }

						return decimalSymbol + result;
					}
				}

				function getZeroString(length) {
					var pow = Math.pow(10, length);
					return pow.toString().slice(1);
				}

				function addThousandsSeperator(integer) {
					return (integer + infoLost).toString().replace(thousandsRegex, '$1' + thousandsSeperator);
				}

				return function (value, places) {
					places = typeof places === 'undefined' ? decimalPlaces : parseInt(places);

					// Truncate the value to get the integer portion.
					var integer = value | 0;
					var decimal = decimalToString( ((value * 1000) % (1 * 1000)) / 1000, places); //do first because we need to know if info was lost.
					var number = addThousandsSeperator(integer) + decimal;
					return prefix + number + suffix;
				}
			}

			var formatPositive = buildFormatter(positiveExample);
			var formatNegative = buildFormatter(negativeExample);

			return function (value, places) {
				value = parseFloat(ko.unwrap(value));
				if (isNaN(value)) return value.toString();
				return (value < 0 ? formatNegative : formatPositive)(Math.abs(value), places);
			}
		}('$1,111.22', '($1,111.22)'));

		utils.formatPrice = _.partialRight(utils.formatMoney, parseInt(2) || 2);

		utils.plural = function (singularForm, quantifier, includeQuantity) {
			var quantifierValue = ko.unwrap(quantifier);
			var quantity = _.isArray(quantifierValue) ? quantifierValue.length : quantifier;
			return (includeQuantity ? quantity + ' ' : '') + attache.plural(singularForm, quantity);
		};

		utils.atcListButtonText = function() {
			if(utils.isActiveQuote) {
				return 'Add to ' + oConfig.labels.savedCarts;
			} else {
				return oConfig.labels.addToCartList || 'Add to Cart';
			}
		};

		utils.resetATCForm = function() {
			if(!runHook('catalogTemplatesOverrideResetATCForm', { utils: this })) {
				document.getElementById("atc_form").reset();
				_.forEach(viewModel.results(), function(result) {
					if (setSelectedQty()) {
						result.selectedQty(result.minQty() || 1);
					}else{
						result.selectedQty(undefined);
					}
					result.selectedQty.notifySubscribers();
				});
			}
		}

		utils.addToCart = function (details, cart, parentKey, useInstances) {

			function getQueryStringParameters() {
				var params = {
					type : 'v200add',
					sc_id : oConfig.overrideCartKey || oConfig.sessionData.sc_id,
					s_key : oConfig.sessionData.sessionKey,
					s_url : oConfig.sessionData.storefrontUrl,
					o_url : oConfig.sessionData.orderfrontUrl,
					createsessioncookie : '1',
					noredirect : '1',
					l_ws_key : '',
					mobile : 'no',
					action : 'postlogic',
					modal  : utils.getParameter("modal"),
					modalaction  : utils.getParameter("modalaction"),
					useinstances: useInstances,
					postProcess: true
				};

				// Only one order detail can be edited at a time, so if there
				// are more than one, choose an arbitrary one to edit.
				var editedOrderDetail = _.find(details, 'configuratorEditData');
				if (editedOrderDetail) params.odeditkey = editedOrderDetail.configuratorEditData.od_id;

				if (cart) {
					_.assign(params, {
						type: 'add-to-saved-cart',
						o_id: cart.key,
						nickname: cart.nickname
					});
				}

				return params;
			}

			function getPostData() {
				var data = {};
				var productKeys = [];
				var instanceKeys = [];
				var instancesWithKeys = {};

				//' Force use instances on if it's off / undefined and we detect duplicate p_keys
				if(!useInstances && details.length) {
					var distinctProductKeys = details.reduce(function(distinctValues, detail, index) {
						if(distinctValues && distinctValues.indexOf(detail.key()) < 0)
						{
							distinctValues.push(detail.key());
						}
					}, []);

					if(distinctProductKeys && distinctProductKeys.length !== details.length) {
						//' Always use instances if we're adding the same product key to cart in one post
						useInstances = true;
					}
				}

				buildConfiguratorData = function(product) {

					/* config_json field example:
						{
							"question":"F4E82B1A5F5A4A3F915B6618A766754F",
							"questionText":"STRIKE (required)",
							"name":"6800_000_10",
							"answer":"790BA1B41D6E48ECB2A825CB02EDAB93",
							"answerText":"A - 4-7/8 ASA Strike",
							"question_erp_code":"10_001",
							"answer_erp_code":"01",
							"question_status":"True"
						}

						Full config_json structure:
						{ choices: [
							{ field1 },
							{ field2 }
						],
						  configType: 'cartoptions' // or 'configurator'
						}
					*/

					/*
					'	We're setting configType based on the customForm value,
					'	but we should never hit this code if it's true until we
					'	put configurator in KO.  This is here just in case or
					'	for future use.
					'   customForm not be ndefined if not on the product detail page.
					*/
					if(oConfig.showProductConfigurator && typeof customForm !== 'undefined' && !!customForm) {
						configType = 'configurator';
					} else {
						configType = 'cartoptions';
					}
					var configJson = {
										choices: [],
										configType: configType
									};
					var cartOptions = '';
					if(product.questions && product.questions().length > 0)  {
						product.questions().forEach(function(question) {
							configJsonQuestion = {
								question: question.q_key(),
								name: question.questionText(),
								questionText: question.questionText(),
								answerText: question.selectedAnswer(),
								question_status: true
							};
							if(question.erpCode()) {
								configJsonQuestion["question_erp_code"] = question.erpCode();
							}
							configJson.choices.push(configJsonQuestion);
							cartOptions += question.questionText() + "~" + question.selectedAnswer() + "|"
						});
					}
					return {
							configJson: configJson,
							cartOptions: cartOptions.slice(0, -1) // trim trailing pipe
					};
				}

				handleAddOnProducts = function(item){
					addOnProducts = [];
					if(item.selectedProduct().selectedAddOn && item.selectedProduct().selectedAddOn()){
						addOnProducts.push(item.selectedProduct().selectedAddOn());
					}else if(item.selectedProduct().selectedAddOns && item.selectedProduct().selectedAddOns().length > 0){
						_.forEach(item.selectedProduct().selectedAddOns(), function(item){
							addOnProducts.push(item);
						})
					}
					
					_.forEach(addOnProducts, function(item){
						//handle the add to cart for  addons
						prepareAddToCart(item);
					})
				}

				prepareAddToCart = function(product){
					var productKey = product.key();
					var minQty = product.minQty() || 1;
					var uom = ko.toJS(product.selectedUom());
					var lineKey = productKey;

					if(useInstances) {
						lineKey = utils.createGuid();
						var tempInstanceKey = product.instance || utils.createGuid();

						product.instanceKey = tempInstanceKey;
						product.lineKey = lineKey;

						instanceKeys.push(tempInstanceKey);

						if(!instancesWithKeys[tempInstanceKey]) {
							instancesWithKeys[tempInstanceKey] = [];
						}

						instancesWithKeys[tempInstanceKey].push(lineKey);

						data['instance_' + lineKey] = tempInstanceKey;
						data['p_id_' + lineKey] = productKey;
						data['instance_key_set'] = '1';
						
						if(product.isCarrier){	
							data["row_display_case_" + lineKey] = "group-parent";
							data['remove_type_' + lineKey] = 'instance';
						}else if(product.carrier && product.carrier.trim() != ''){
							data["remove_type_" + lineKey] = "hide";
							data["qty_display_type_" + lineKey] = "text"; 
							data["row_display_case_" + lineKey] = "group-child";
							if(product.qtyType == 'ratio') {
								data['qty_control_from_' + lineKey] = 'parent';
								data['multiplier_on_parent_qty_' + lineKey] = product.qtyMultiplier;
							}
						}else{
							data['remove_type_' + lineKey] = 'instance';
						}
					}
					
					data['qty_' + lineKey] = product.selectedQty() || minQty;
					if(!!uom){
						data['uom_' + lineKey] = uom.description;
						if(uom.hasOwnProperty("UomId")) data['uom_id_' + lineKey] = uom.UomId;
						data['uom_conversion_' + lineKey] = uom.uom_conversion ?? 1.0;
					}
					data['pw_id_' + lineKey] = ko.unwrap(product.selectedWarehouse().key) || oConfig.defaultWarehouse;

					if(product.type == 'child') {
						if(parentKey && product.key() !== parentKey) {
							data['parent_p_id_' + lineKey] = parentKey;
						} else if(product.parents && product.parents().length > 0){
							data['parent_p_id_' + lineKey] = product.parents()[0].key();
						}
					}

					
					data['url_' + lineKey]           = product.link();
					data['minqty_' + lineKey]        = product.minOrderQty();
					data['maxqty_' + lineKey]        = product.maxOrderQty();
					data['qty_increment_' + lineKey] = product.stepQty();

					_.assign(data, buildConfiguratorPostData(productKey));
					if (minQty && minQty != oConfig.defaultMinimumQuantity) data['min_order_qty_' + lineKey] = product.minOrderQty();

					if(product.configuratorEditData && product.configuratorEditData.lineNumber) {
						data['line_number_' + lineKey] = product.configuratorEditData.lineNumber;
					}

					if(product.configuratorEditData && product.configuratorEditData.minQty) {
						data['minqty_' + lineKey] = product.configuratorEditData.minQty;
					}

					if(product.configuratorEditData && product.configuratorEditData.maxQty) {
						data['maxqty_' + lineKey] = product.configuratorEditData.maxQty;
					}

					if(product.configuratorEditData && product.configuratorEditData.qtyIncrement) {
						data['qty_increment_' + lineKey] = product.configuratorEditData.qtyIncrement;
					}

					if(product.configuratorEditData && product.configuratorEditData.removeType) {
						data['remove_type_' + lineKey] = product.configuratorEditData.removeType;
					}

					if(product.configuratorEditData && product.configuratorEditData.priceCalcType) {
						data['price_calc_type_' + lineKey] = product.configuratorEditData.priceCalcType;
						if(product.configuratorEditData.priceCalcType == 'fixed') {
							data['price_' + lineKey] = product.configuratorEditData.price;
						}
					}

					if(product.questions && product.questions().length > 0 && product.hasCartOptions()) {
						var configData = buildConfiguratorData(product);
						data['config_json_' + lineKey] = JSON.stringify(configData.configJson);
						data['cart_option_' + lineKey] = configData.cartOptions;
					}

					additionalFields.forEach(function(field){
						if(product[field] != ''){
							data[field+'_'+lineKey] = product[field];
						}
					});

					productKeys.push(lineKey); //This needs to be above the hook so that the order in which the items are added to cart will persist. - cainb 2019-01-23 12:35:49
				}

				_.forEach(details, function (item) {

					if(oConfig.detailConfig.useAddOnProducts && item.showAddOns){
						handleAddOnProducts(item);
					}
					
					prepareAddToCart(item.selectedProduct());
					runHook('getPostDataBeforeReturn', { self: self, data: data, product: item.selectedProduct(), productKeys: productKeys });
				});

				data.keys = _.uniq(productKeys).join(',');

				if(instanceKeys.length) {
					data.instances = _.uniq(instanceKeys).join(',');

					for(key in instancesWithKeys) {
						data[key + '_keys'] = instancesWithKeys[key].join(',');
					}
				}

				return data;
			}

			function getAjaxConfig() {
				return {
					url: 'i_i_add_to_cart.asp?' + $.param(getQueryStringParameters()),
					type: 'POST',
					data: $.param(getPostData(), true),  // true forces jquery to not add brackets to array keys
					headers: {
						'X-Requested-With': 'XMLHttpRequest',
						'Accept': 'application/json, text/plain, * / *',
						'Content-Type': 'application/x-www-form-urlencoded'
					}
				};
			}

			function logSummary() {
				var cartName = cart ? ' "' + cart.nickname + '"' : '';
				console.log('Products added to cart' + cartName + ':\n' + _.map(details, function (product) {
					return '\tQty: ' + (product.selectedQty() || 1) + ' - ' + product.name();
				}).join('\n'));
			}

			function successHandler(responseData, status, request) {
				var cartLines = JSON.parse(responseData)[1].detailLines;
				var newCart = [];
				_.each(cartLines, function(line){
					newCart.push({
						prod_key: line.productID,
						parent_p_id: line.parentProductID,
						qty: line.qty,
						od_key: line.orderDetailKey
					})
				});

				if((oConfig.t_ga4 && oConfig.t_ga4_analytics != '') || (!oConfig.t_ga4_in_gtm && oConfig.t_gtm)) {
					try{
						var gtmProducts = [];
						var line_total = 0;
						_.each(details, function(item){

							var gtmProduct = {
								item_id: item.sku(),
			                    item_name: item.name(),
			                    currency: "USD",
			                    price: Number(item.unitPrice()),
			                    quantity: Number(item.selectedQty())
							}
							gtmProducts.push(gtmProduct);

							line_total = Number(item.selectedQty()) * item.unitPrice();
						});

						if (gtmProducts.length >= 1) {

							window.dataLayer = window.dataLayer || [];
	        				function gtag(){dataLayer.push(arguments);}

							gtag("event", "add_to_cart", {
				                currency: "USD",
				                value: line_total,
				                items: gtmProducts
				            });
						}
					} catch (err){
						console.log(err);
					}
				}else if(oConfig.t_gtm) {
					var gtmProducts = [];
					_.forEach(details, function (item) {

						var gtmProduct = {
							'name': item.name(),
							'id': item.sku(),
							'price': item.unitPrice(),
							'quantity': Number(item.selectedQty())
						}
						gtmProducts.push(gtmProduct);
					});

					if (gtmProducts.length >= 1) {
						window.dataLayer.push({
							'event': oConfig.t_gtm_add_to_cart_event_name,
							'ecommerce': {
								'currencyCode': 'USD',
								'add': {
									'products': gtmProducts
								}
							},
							'customer': {
								'email': cimcloud.session.email,
								'firstname': cimcloud.session.firstName,
								'lastname': cimcloud.session.lastName
							}
						});
					}
				}

				if(oConfig.isModal) {
					parent.modal.done([newCart]);
					try{
						parent.fncReloadCartWindow();
					}catch(err){
					}

					var page = window.parent ? window.parent.location.pathname.split("/").pop() : window.location.pathname.split("/").pop();
					if(!page.includes("payment.asp")) {
						parent.top.utils.toastrATC(details, newCart);
					}
				}else{
					viewModel.activeCart(newCart);

					if(viewModel.mainProduct && viewModel.mainProduct.configuratorEditData) {
						window.location = oConfig.sessionData.cartPage;
						return;
					}
					if(!cart) {
						try{
							fncReloadCartWindow();
						}catch(err){

						}
						if(typeof ofConfig === "undefined" || !ofConfig.stopToastrATC) {
							utils.toastrATC(details, cart);
						}
					} else {
						utils.modalATC(cart, data, details);
					}
					if(oConfig.pageName == 'pc_combined_results.asp' && isActiveLayout('list')) {
						utils.resetATCForm();
					}

					logSummary();

					toggleLoadingWidget(false);

					//render promo messaging template and display if applicable.
					element = utils.renderTemplateToContainer('catalog.promo_bar', data);

					runHook('addToCartSuccessHandler', { self: self, responseData: responseData, details: details });
				}
			}

			function errorHandler(responseData, status, request) {
				logSummary();

				var errorToastrConfig = {
					'closeButton': true,
					'newestOnTop': true,
					'positionClass': 'toast-top-right',
					'preventDuplicates': false,
					'showDuration': 200,
					'hideDuration': 1000,
					'tapToDismiss': true,
					'timeOut': 3000,
					'extendedTimeOut': 1000
				}

				toggleLoadingWidget(false);

				console.log(responseData);

				toastr.error(
					'Unable to add product to cart',
					'There was an error',
					errorToastrConfig
				);

			}
			var bPass = true;

			// TODO: add UoM support (qty_display, etc). Fields aren't available on the UoM Price objects yet.
			if (!_.isArray(details)) {
				details = [ details ];
			}
			var data = {
				details: details,
				cart: cart
			};

			var questionsValid = true;

			details.some(function(detail) {
				if(detail.hasOwnProperty("questionsHaveValidAnswers")) {
					questionsValid = detail.questionsHaveValidAnswers();

					if(!questionsValid) {
						detail.atcErrorText("Required option fields missing.");
					} else {
						detail.atcErrorText("");
					}

					if(!questionsValid && detail.hasOwnProperty("currentParent")) {
						detail.currentParent().rowsAtcAttempted(true);
					}
				}
				return !questionsValid;
			});

			if(!questionsValid) {
				return false;
			}

			if(details.length > 0 && details[0].useConfigurator && details[0].useConfigurator()){
				/*
					doing an [eval] on an undefined throws an error.
					so, we have to try-catch.
						The template may be overriden to allow a product (with configurator)
						to be added to cart w/o any config options selected.
				*/
				try {
					if(!eval('formValidation' + oConfig.formValidationKey).check()){
						bPass = false;
					}
				} catch(e) {
					//not using FormBuilder
				}
			}else if(details.length <= 0) {
				bPass = false;
			}

			if(bPass){
				toggleLoadingWidget(true);
				cart = ko.toJS(cart);

				var promise = $.ajax(getAjaxConfig())
					.error(errorHandler)
					.done(successHandler);

				return promise;

			}else{
				return false;
			}
		};
		
		utils.toastrATC = function(details, cart) {
			if(utils.isActiveQuote){
				var sPage = oConfig.sessionData.orderfrontUrl + '/' + "payment.asp?o_key=" + oConfig.overrideCartKey;
			}else{
				var sPage = oConfig.sessionData.orderfrontUrl + '/' + oConfig.sessionData.cartPage + "?o_key=" + oConfig.sessionData.sc_id;
			}

			if(utils.isActiveQuote && oConfig.isModal){
				sPage = "javascript:window.history.back();"
			}
			var confirmToastrConfig = {
				'closeButton': true,
				'newestOnTop': true,
				'onclick': function() {
					window.location = sPage;
				},
				'positionClass': 'toast-top-right',
				'preventDuplicates': false,
				'showDuration': 200,
				'hideDuration': 1000,
				'tapToDismiss': false,
				'timeOut': 3000,
				'extendedTimeOut': 1000
			}

			if(cart && utils.isActiveQuote) {
				var confirmTextConfig =  {title: utils.plural(oConfig.labels.savedCartsLabels.itemText, details, true) + ' ' + oConfig.labels.savedCartsLabels.modalConfirmationHeader }
			}else{
				if(utils.isActiveQuote){
					var confirmTextConfig = { title: utils.plural(' product', details, true) + ' added to ' + oConfig.labels.savedCarts }
				}else{
					var confirmTextConfig = { title: utils.plural(' product', details, true) + ' added to Cart'}
				}
			};

			runHook('toastrATCConfiguration', { confirmToastrConfig: confirmToastrConfig, confirmTextConfig: confirmTextConfig });

			utils.popToastr(
				confirmTextConfig.title,
				'Click or tap to view',
				confirmToastrConfig
			);
		}

		utils.modalATC = function(cart, data, details) {
			var modalConfig = cart && utils.isActiveQuote ?
			{
				body: 'catalog.saved_cart_confirmation_body',
				size: '',
				title: utils.plural(oConfig.labels.savedCartsLabels.itemText, details, true) + ' ' + oConfig.labels.savedCartsLabels.modalConfirmationHeader
			} :
			{
				body: 'catalog.atc_body',
				footer: 'catalog.atc_popup_buttons',
				size: getATCModalSize(),
				title: utils.plural(' Product', details, true) + ' Added to Cart'
			};

			runHook('modalATCAfterConfiguration', { self: self, cart: cart, modalConfig: modalConfig, details: details });

			utils.openModal(modalConfig, data);
		}

		utils.renderTemplateToContainer = function (template, data) {
			var surrogate = document.createElement('div');
			var $container = $('<div>').append(surrogate);

			ko.renderTemplate(
				template,
				data,
				null,
				surrogate,
				'replaceNode'
			);

			return $container;
		};

		utils.getSavedCart = function (carts, callback) {
			var selectedCart = ko.observable();
			selectedCart.subscribe(callback);

			var data = { savedCarts: carts, selectedCart: selectedCart };
			utils.openModal({
				body: oConfig.savedCartPopupTemplate,
				size: 'small',
				title: oConfig.labels.savedCartsLabels.modalHeader,
				backdrop: 'static'
			}, data);
		};

		utils.createGuid = function () {
			// https://stackoverflow.com/a/8809472
			var d = new Date().getTime();
			if(window.performance && typeof window.performance.now === "function"){
				d += performance.now(); //use high-precision timer if available
			}
			var guid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
				var r = (d + Math.random()*16)%16 | 0;
				d = Math.floor(d/16);
				return (c=='x' ? r : (r&0x3|0x8)).toString(16);
			});
			return guid.toUpperCase();
		};

		// Generates a unique ID and returns it twice in a row. Intended for use with the `for`
		// attribute on `label` elements.
		(function () {
			var previousId;

			utils.labelId = function () {
				var id = previousId || utils.createGuid();
				previousId = previousId ? null : id;
				return id;
			};
		}());

		utils.openModal = function (options, data) {
			_.forEach(['header', 'body', 'footer'], function (prop) {
				if (options[prop]) {
					options[prop] = utils.renderTemplateToContainer(options[prop], data);
				}
			});
			modal.open(options);
		};

		var productModel = function (product) {
			var self = this;

			var productCollectionMapping = {
				create: function (options) {
					return new productModel(options.data);
				}
			}

			ko.mapping.fromJS(product, {
				copy: [
					'tab2_html',
					'opt1',
					'opt2',
					'opt3',
					'opt4',
					'opt5',
					'opt6',
					'opt7',
					'opt8',
					'opt9',
					'accountHistory',
					'reviews',
					'tabs',
					'type',
					'promoDescriptions',
					'configuratorEditData',
					'calc_inv_message',
					'use_cart_options',
					'showAddOns',
					'addOnLabel',
					'addOnDisplay',
					'showPrice'
				],
				related: productCollectionMapping,
				secondRelated: productCollectionMapping,
				children: productCollectionMapping,
				alsoBought: productCollectionMapping,
				addOns: productCollectionMapping
			}, self);

			self.children.extend({ deferred: true });

			self.atcButtonText = ko.observable('');
			self.atcErrorText = ko.observable('');

			if(oConfig.isModal && !ko.unwrap(self.link).includes("modal=1")) {
				self.link((function() {
					var processedLink = ko.unwrap(self.link);
					if(processedLink.indexOf('?') > -1) {
						processedLink += "&";
					} else {
						processedLink += "?";
					}
					processedLink += 'modal=1&modalaction=' + utils.getParameter("modalaction");

					return processedLink;
				})());
			}

			if(utils.getParameter('quickadd') == 'true' && self.type === 'parent'){
				self.childDisplayType('droplist');
			}

			self.updateAtcButtonText = function() {
				if(self.configuratorEditData && self.configuratorEditData.od_id) {
					self.atcButtonText(oConfig.labels.updateCartItem || 'Update');
				} else if (utils.isActiveQuote) {
					self.atcButtonText('Add To ' + oConfig.labels.savedCarts);
				} else {
					self.atcButtonText(oConfig.labels.addToCart || 'Add');
				}
			};

			self.updateAtcButtonText();

			var accordionDisplaySetting = self.tabGroupAccordionDisplay();

			// set defaultTabAccordionState based for first tab
			var defaultTabAccordionState = accordionDisplaySetting === 'open-first' || accordionDisplaySetting === 'open-all';
			self.tabs.forEach(function(tab) {
				tab.open = ko.observable(defaultTabAccordionState);
				// set defaultTabAccordionState for remaining tabs (after first)
				defaultTabAccordionState = accordionDisplaySetting === 'open-all';
			});	

			self.hasCartOptions = ko.computed(function() {
				if(!oConfig.useCartOptions){
					return false;
				}else{
					if(self.questions && self.questions() && self.questions().length && (self.type === 'parent' || self.useCartOptions())) {
						return true;
					} else {
						if(self.children && self.children().length) {
							return self.children().reduce(function(hasQuestions, child) {
								return hasQuestions || (!!child.questions && !!child.questions() && !!child.questions().length && child.useCartOptions());
							}, false);
						} else {
							return false;
						}
					}
				}
			});

			self.requireInfoForAtc = ko.computed(function () {
				return !!(
						self.childDisplayType() || 
						self.useConfigurator() || 
						( oConfig.useCartOptions && self.hasCartOptions() ) ||
						(self.showAddOns && oConfig.detailConfig.useAddOnProducts && oConfig.searchConfig.requireAddOnSelection)
					) && oConfig.pageName == 'pc_combined_results.asp';
			});

			self.priceDisplay = ko.observable(function () {
				var template = 'catalog.details_link';
				if(oConfig.pageName == 'pc_product_detail.asp'){
					template = 'catalog.selected_product_price_display';
				}else if (self.useConfigurator()){
					template = 'catalog.configurator_price_display';
				}else if (self.requireInfoForAtc() && self.childStartingPrice()) {
					template = 'catalog.price_start_at'
				} else if (!self.requireInfoForAtc()) {
					template = 'catalog.selected_product_price_display';
				}

				//Manually check for template overrides since this is dynamically returned.
				//Need to convert this to a function and use any time template names are returned.
				if(document.getElementById(template + '_custom')){
					return template + '_custom';
				}else{
					return template;
				}

			}());

			self.selectedQty = ko.observable(function() {
				if(self.configuratorEditData) {
					return self.configuratorEditData.qty;
				}else if(self.qty){
					return self.qty;
				} else {
					return undefined;
				}
			}());

			self.isQtyValid = ko.observable(true);

			self.selectedWarehouse = ko.observable(_.find(self.inventory.warehouses(), function(wh){ return wh.isDefault() } ) || [] );

			if(self.configuratorEditData && self.configuratorEditData.pw_id) {
				self.selectedWarehouse(_.find(self.inventory.warehouses(), function(wh){ return wh.key() == self.configuratorEditData.pw_id } ) || [] );
			}
				
			_.each(self.uomPrice(),function(uom){
				uom.isDefault( (oConfig.useSalesUomAsDefault && uom.uom() == "uom_sales") || (!oConfig.useSalesUomAsDefault && uom.uom() == "uom_std"))

				var bHasBreaks = false;
				bHasBreaks = uom.breaks && uom.breaks() && uom.breaks().length > 0;
				uom.hasBreaks(bHasBreaks);
			});

			if(oConfig.useSalesUom)
			{
				var salesUomIndex = self.uomPrice().findIndex(function (uom) { return uom.uom() == "uom_sales"});
				if(salesUomIndex > -1) {
					for(i = self.uomPrice().length -1; i > 0; i--) {
						if(i != salesUomIndex) {
							self.uomPrice.splice(i, 1);
						}
					}
				}
			}

			self.selectedUom = ko.observable(
				(function () {
					var uom = undefined;

					// It's possible that this is a parent product and the configurator
					// edit data will not match a uom on the parent, however the selectedProduct
					// has not been set at this point, so we can't just use selectedProduct
					// With the previous version of this code, this would make the selectedUom
					// undefined and break other parts of the code because it skipped all other
					// checks if configuratorEditData had a uom value. Now it will check the
					// other conditions if the configuratorEditData doesn't match an available uom.
					// There's certainly a better way to handle this, but this will do for now.
					if(self.configuratorEditData && self.configuratorEditData.uom) {
						uom = _.find(self.uomPrice(), function (price) {
							return price.description() == self.configuratorEditData.uom;
						});
					}

					if(!uom) {
						uom = _.find(self.uomPrice(), function (price) {
							if (price.hasOwnProperty("isDefault")){
								return price.isDefault(); //set from SSJS
							} else if(typeof price.IsDefault() === "undefined"){
								return price.ConversionRate() === 1; //fallback
							} else{
								return price.IsDefault() || price.ConversionRate() === 1 || price.ConversionRate() === 12; //returned from COM
							}
						});
					}

					if(!uom && self.uomPrice().length > 0) {
						//if still no match then take the first one
						uom = self.uomPrice()[0];
					}

					return uom;
				})());

			// Set the selected quantity back to the default if the UOM changes
			self.selectedUom.subscribe(function(newValue) {
				var defaultQty = newValue.minQty() || newValue.step() || 1;
				self.selectedQty(defaultQty);
			});

			self.processedBreaks = ko.computed(function() {
				if (!( self.selectedUom() && self.selectedUom().hasBreaks() )) {
					return [];
				}

				var processed = [];
				var data = self.selectedUom().breaks();

				//converted from price break popup window to properly display single breaks that actually have multiple break records
				if(data[data.length -1].qty() == '1000000' || data[data.length -1].qty() == '100000000'){
					data.pop();
				}

				//If there's only one break we will skip showing the price break information
				if(data.length == 1) {
					return [];
				}

				processed = data.map(function(lineBreak) {
					var price, startQ, endQ, range, qty, proceed;
					price = lineBreak.price();
					startQ = lineBreak.qty();
					endQ = lineBreak.qtyEnd();
					proceed = true;

					if (endQ != '0') {
						range = startQ + ' - ' + endQ;
					} else {
						range = startQ + '+';
					}

					if(proceed){
						return {
							price: price,
							range: range
						}
					}else{
						return {}
					}
				});
				return processed.filter( function(value){ return JSON.stringify(value) !== '{}'; });
			});

			self.configPrice = buildConfiguratorPriceObservable(self.key());

			self.unitPrice = ko.computed(function () {
				if(oConfig.useListPriceOverride){
					return self.selectedUom().suggestedPrice()
				}

				if(self.configuratorEditData && self.configuratorEditData.priceCalcType == 'fixed') {
					return self.configuratorEditData.price;
				}
				return self.selectedUom().price() + self.configPrice();
			});

			self.priceTrace = ko.computed(function() {
				var bPriceErrorMessage = 'Price trace is not available';
				return self.selectedUom().priceTrace
					? self.selectedUom().priceTrace()
					: bPriceErrorMessage;
			});

			self.youSavePercent = ko.computed(function () {
				return (((self.selectedUom().suggestedPrice() - self.selectedUom().price()) / self.selectedUom().suggestedPrice()) * 100).toFixed(oConfig.youSaveDecimalPlaces) + '%';
			});

			self.suggestedPrice = ko.computed(function () {
				return self.selectedUom().suggestedPrice();
			});

			self.showYouSave = ko.computed(function () {
				return parseFloat((self.selectedUom().suggestedPrice() || 0).toFixed(oConfig.detailConfig.globalUnitPriceDecimalPlaces)) > parseFloat((self.selectedUom().price() || 0).toFixed(oConfig.detailConfig.globalUnitPriceDecimalPlaces)) && self.selectedUom().suggestedPrice() > 0 && oConfig.showProdYouSave && !oConfig.useListPriceOverride;
			});

			self.childView = function () {
				// return (self.childDisplayType() || 'stand-alone') + '-layout';
				return 'stand-alone-layout';
			}();

			if(!self.mapPriceType() && oConfig.defaultPriceDisplayType) {
				self.mapPriceType(oConfig.defaultPriceDisplayType);
			}

			self.showAtc = function () {
				var primary1    = self.inventory.showAtc() && oConfig.searchConfig.showAtc && self.mapPriceType() !== 'show_message';
				var secondary1  = oConfig.isLoggedIn || self.mapPriceType() !== 'require_login_for_atc';
				var secondary2  = oConfig.isLoggedIn || self.mapPriceType() !== 'require_login_for_price_and_atc';
				var tertiary1   = self.mapPriceType() === 'require_atc' || (self.mapPriceType() === 'require_login_or_atc' && !oConfig.isLoggedIn);
				var tertiary2   = self.mapPriceType() !== 'hide' && (self.showProdAtc() != 0);
				var quaternary1 = !(self.type === 'parent' && self.childDisplayType() == 'droplist' && oConfig.pageName != 'pc_combined_results.asp')

				return primary1 &&
						(secondary1) &&
						(secondary2) && (
							(tertiary1) ||
							(tertiary2)
						)
						&& quaternary1;
			}();

			self.showQuickAtc = ko.computed(function () {
				var showQuickAtc = self.showAtc && !self.requireInfoForAtc() && self.type !== "parent"; //initial setting
				if(oConfig.showChildrenSelection && self.type === "child" ){
					if(self.unitPrice() > 0 && self.showAtc){
						showQuickAtc = true;
					}
					else{
						showQuickAtc = false;
					}
				}

				return showQuickAtc; //(self.showAtc && !self.requireInfoForAtc())
			});

			self.allowQuickAddModal = ko.computed(function() {
				if (oConfig.pageName == 'pc_product_detail.asp') return false;

				var exceptions = ['exploded-view', 'input-qty'];
				if (exceptions.includes(self.childDisplayType())) return false;

				return true;
			});

			self.isInCart = ko.computed(function(){
				var cartCount = 0;
				if(viewModel.activeCart && viewModel.activeCart().length){
					var product = _.filter(viewModel.activeCart(), function(cart){
						return cart.prod_key == self.key() || cart.parent_p_id == self.key();
					});
					cartCount = product.length;
				}
				return cartCount;
			});

			self.showPrice = function () {
				if (typeof self.showPrice !== 'undefined') return self.showPrice;
				return self.type !== 'parent' && (
						(self.mapPriceType() !== 'show_message') &&
						!((self.mapPriceType() === 'require_login_for_price_and_atc') && !oConfig.isLoggedIn) &&
						!(self.mapPriceType() === 'require_atc' || (self.mapPriceType() === 'require_login_or_atc' && !oConfig.isLoggedIn))
					) &&
					(self.mapPriceType() !== 'hide') && (self.showProdPrice() != 0) &&
					oConfig.showPricingOrderEntry;
			}();

			self.showBreaks = ko.observable(function(){
				return oConfig.useBreaks && oConfig.useBreaksTable && oConfig.pageName == 'pc_product_detail.asp';
			}());

			self.mapPriceMessage = function () {
				if (self.mapPriceType() === 'show_message') {
					return oConfig.mapBehaviorMessages.showMessage;
				} else if (self.mapPriceType() === 'require_login_for_price_and_atc' && !oConfig.isLoggedIn) {
					return oConfig.mapBehaviorMessages.requireLoginMessage;
				} else if (self.mapPriceType() === 'require_login_for_atc' && !oConfig.isLoggedIn) {
					return oConfig.mapBehaviorMessages.requireLoginAtcMessage;
				} else if (self.mapPriceType() === 'require_atc') {
					return oConfig.mapBehaviorMessages.requireAtcMessage;
				} else if (self.mapPriceType() === 'require_login_or_atc' && !oConfig.isLoggedIn) {
					return oConfig.mapBehaviorMessages.requireLoginOrAtcMessage;
				} else if (self.mapPriceType() !== 'hide') {
					return '';
				}
			}();

			self.warehouseTemplate = function () {
				if (oConfig.showWarehouses && self.inventory.warehouses().length > 0 && self.inventory.inventoryStatus() === 'in') {
					if ((oConfig.allowWarehouseSelection && (oConfig.pageName == 'pc_combined_results.asp' && oConfig.searchConfig.hideAtcShowInv)) || (oConfig.allowWarehouseSelection && self.showQuickAtc())) {
						return 'warehouse_droplist';
					} else if (oConfig.useWarehousesTable) {
						return 'warehouse_table';
					} else {
						if(self.showQuickAtc()){
							return 'warehouse_info';
						}else{
							return '';
						}
					}
				} else {
					return 'warehouse_off';
				}
			}();

			self.priceBreaksTemplate = function(){
				if(oConfig.useBreaks){
					if(oConfig.useBreaksTable && oConfig.pageName == 'pc_product_detail.asp'){
						return 'catalog.qty_breaks_table';
					}else{
						return 'catalog.qty_breaks';
					}
				}else{
					return 'catalog.breaks_off';
				}
			}();

			self.uomTemplate = ko.computed(function() {
				var template;
				if (self.selectedProduct().uomPrice().length > 1) {
					template = 'catalog.uom_select';
				} else {
					template = 'catalog.uom_input';
				}

				if(document.getElementById(template + '_custom')){
					return template + '_custom';
				}else{
					return template;
				}
			});

			self.documentsTemplate = function () {
				return 'catalog.documents';
			}();

			self.qtyInputClick = function (key) {
				jQuery('#qty_'+key).focus();
				jQuery('#qty_'+key).select();
			};

			self.validateQtyInput = function (data) {
				if (utils.getCookie('productLayout') === 'list') {
					return '';
				} else {
					return '1';
				}
			};

			self.hideOnListView = function () {
				if ((isActiveLayout('list') && !oConfig.detailConfig.productKey ) || (self.type === 'child' && oConfig.detailConfig.productKey)) {
					return 'hide';
				}
			};

			self.isChild = function () {
				if (self.type === 'child'){
					return true;
				}else{
					return false;
				}
			}

			self.isParent = function() {
				if (self.type.toLowerCase() === 'parent'){
					return true;
				}else{
					return false;
				}
			}

			self.minQty = ko.computed(function () {
				if (self.configuratorEditData && self.configuratorEditData.minQty) {
					self.selectedProduct().selectedUom().minQty(self.configuratorEditData.minQty);
				}
				if (oConfig.useMinimumQuantity) {
					return self.selectedProduct().selectedUom().minQty();
				} else {
					return 0;
				}
			});

			self.stepQty = ko.computed(function () {
				if (self.configuratorEditData && self.configuratorEditData.qtyIncrement) {
					self.selectedProduct().selectedUom().step(self.configuratorEditData.qtyIncrement);
				}
				if (oConfig.useQuantityIncrement) {
					return self.selectedProduct().selectedUom().step();
				} else {
					return oConfig.defaultQuantityIncrement;
				}
			});

			self.maxQty = ko.computed(function () {
				if (self.configuratorEditData && self.configuratorEditData.maxQty) {
					self.selectedProduct().selectedUom().maxQty(self.configuratorEditData.maxQty);
				}
				if (oConfig.useMaximumQuantity && self.selectedProduct().selectedUom().maxQty() > 0) {
					return self.selectedProduct().selectedUom().maxQty();
				} else {
					return '';
				}
			});
			
			self.recentlyViewedTabExists = ko.computed(function() { 
				if (!Array.isArray(self.tabs) || self.tabs.length === 0) {
					return false;
				}
				return self.tabs.some(function(tab) {
					return tab.dynamicSource == "recently_viewed"; 
				});
			});

			self.customersAlsoBoughtExists = ko.computed(function() { 
				if (!Array.isArray(self.tabs) || self.tabs.length === 0) {
					return false;
				}
				return self.tabs.some(function(tab) { 
					return tab.dynamicSource == "customers_also_bought"; 
				}); 
			});

			self.hasQuantityRestrictions = ko.computed(function () {
				return (oConfig.useMinimumQuantity || oConfig.useMaximumQuantity || oConfig.useQuantityIncrement) && 
						!!(
							(self.minQty() && self.minQty() !== oConfig.defaultMinimumQuantity) || 
							( (!self.selectedUom().uom_conversion() || self.selectedUom().uom_conversion() == 1) && self.stepQty() && self.stepQty() != oConfig.defaultQuantityIncrement) || 
							self.maxQty()
						);
			});

			self.quantityRestrictionsHtml = ko.computed(function () {
				var lines = [];

				if (self.minQty() && self.minQty() !== oConfig.defaultMinimumQuantity)
					lines.push(oConfig.labels.minQtyPopover.replace(/<min_qty>/,self.minQty()));

				if ( (!self.selectedUom().uom_conversion() || self.selectedUom().uom_conversion() == 1) && self.stepQty() && self.stepQty() != oConfig.defaultQuantityIncrement)
					lines.push('Qty Increment: ' + self.stepQty());

				if (self.maxQty())
					lines.push('Maximum Qty: ' + self.maxQty());

				return lines.join('<br>');
			});

			self.addToSavedCart = function () {
				utils.getSavedCart(viewModel.savedCarts(), function (cart) {
					if (cart) {
						if (!_.includes(viewModel.savedCarts(), cart)) {
							// Insert the new cart in sorted order, notifying exactly once.
							var index = _.sortedIndex(viewModel.savedCarts(), cart, 'nickname');
							viewModel.savedCarts.splice(index, 0, cart);
						}

						var parentKey;

						if(self.type == 'child' && viewModel.mainProduct) {
							parentKey = ko.unwrap(viewModel.mainProduct).key();
						}

						utils.addToCart(self, cart, parentKey);
					}
				});
			};

			self.addToCart = function () {
				var parentKey;

				if(self.type == 'child' && viewModel.mainProduct) {
					parentKey = ko.unwrap(viewModel.mainProduct).key();
				}

				return utils.addToCart(self, undefined, parentKey);
			};

			self.atcPopupSuccess = function () {

			};

			self.atcPopupFail = function () {

			};

			self.searchfields = function() {
				var includedSearchfields = Object.keys(self).filter(function(key) { return key.startsWith('searchfield'); }).sort();
				var searchfieldsObject = {};
				includedSearchfields.forEach(function(searchfieldName) {
					if(searchfieldName == "searchfields") // The existing searchfields object
					{
						var originalKeys = Object.keys(self[searchfieldName]).filter(function(key) { return key.startsWith('searchfield'); });
						//console.log(JSON.stringify(originalKeys));
						originalKeys.forEach(function(key) {
							if(!searchfieldsObject[key]) {
								searchfieldsObject[key] = ko.unwrap(self[searchfieldName][key])
							}
						});
					} else {
						if(!oConfig.searchFieldsShell[searchfieldName]) {
							// because we can't use "?." until we stop trying to support IE
							// like: oConfig.searchFieldsShell[searchfieldName]?.label || ''
							oConfig.searchFieldsShell[searchfieldName] = {
								label: '',
								show: false
							};
						}
						searchfieldsObject[searchfieldName] = {
							field: ko.observable(searchfieldName),
							value: ko.observable(ko.unwrap(self[searchfieldName])),
							label: ko.observable(oConfig.searchFieldsShell[searchfieldName].label || ''),
							show: ko.observable(!!ko.unwrap(self[searchfieldName]) && (oConfig.searchFieldsShell[searchfieldName].show || false))
						}
					}
				});
				return searchfieldsObject;
			}();

			self.selectFirstTab = function () {
				$('#tab1 > a').click();
			};

			self.getWebPage = function (webPageId) {
				return 'page.asp?p_key=' + webPageId + ' .wpc_page_content';
			};

			self.useTabs = function () {
				return oConfig.detailConfig.useAdvancedTabs && self.tabs && self.tabs.length && self.showTabs();
			}

			self.usingDownloadsTab = function() {
				var index = _.findIndex(self.tabs, function(t) {
								return t.dynamicSource == "downloads"
							});
				return self.useTabs() && index > -1;
			}

			self.getSmartListLinkOrderStats = function(parent){
				if(!parent){
					return 'product_history_detail.asp'
				}
				if(oConfig.detailConfig.useSkuDisplay && oConfig.detailConfig.useAliases) {
					return 'product_history_detail.asp?search2=(searchexact~p.sku_display~' + parent.sku() + '|OR|searchexact~pa.sku_alias~' + parent.sku() + '&s=' + parent.sku()
				}
				else if(oConfig.detailConfig.useAliases){
					return 'product_history_detail.asp?search2=searchexact~pa.sku_alias~' + parent.sku() + '&s=' + parent.sku()
				}
				else if(oConfig.detailConfig.useSkuDisplay) {
					return 'product_history_detail.asp?search2=searchexact~p.sku_display~' + parent.sku() + '&s=' + parent.sku()
				}else{
					return 'product_history_detail.asp?search2=searchexact~p.sku~' + parent.sku() + '&s=' + parent.sku()
				}
			};

			self.getSmartListLinkInvoiceStats = function(parent){
				if(!parent){
					return 'products_invoiced_detail.asp'
				}
				if(oConfig.detailConfig.useSkuDisplay && oConfig.detailConfig.useAliases) {
					return 'products_invoiced_detail.asp?search2=(searchexact~p.sku_display~' + parent.sku()+ '|OR|searchexact~pa.sku_alias~' + parent.sku() + '&s=' + parent.sku()
				}
				else if(oConfig.detailConfig.useAliases){
					return 'products_invoiced_detail.asp?search2=searchexact~pa.sku_alias~' + parent.sku() + '&s=' + parent.sku()
				}
				else if(oConfig.detailConfig.useSkuDisplay) {
					return 'products_invoiced_detail.asp?search2=searchexact~p.sku_display~' + parent.sku() + '&s=' + parent.sku()
				}else{
					return 'products_invoiced_detail.asp?search2=searchexact~p.sku~' + parent.sku() + '&s=' + parent.sku()
				}
			};
			
			self.explodedViewHasQty = function(product){
				hasQty = false;
				_.each(product.children(), function(child){
					if(!hasQty && child.diagramQty() != ''){
						hasQty = true;
					}
				});
				return hasQty;
			};

			self.explodedViewHasNotes = function(product){
				hasNotes = false;
				_.each(product.children(), function(child){
					if(!hasNotes && child.diagramNotes() != ''){
						hasNotes = true;
					}
				});
				return hasNotes;
			};

			self.tableColumns = ko.computed( function () {
				var columns = Array.prototype.concat(
					[
						{
							if: oConfig.showImgCol,
							label: oConfig.labels.imgCol,
							field: 'thumb',
							template: 'catalog.input_qty_thumb_display',
							cellClass: 'qty-input-table-thumb'
						},
						{
							if: oConfig.showNameCol && self.childDisplayType() !== 'add-row',
							label: oConfig.labels.nameCol,
							field: 'nm',
							template: 'catalog.input_qty_nm_display'
						},
						{
							label: 'SKU',
							field: 'sku',
							template: 'catalog.input_qty_sku_display'
						},
						{
							if: oConfig.useCartOptions && self.hasCartOptions(),
							label: 'Options',
							field: 'questions',
							template: 'catalog.cart_options'
						},
						{
							if: self.childDisplayType() == 'exploded-view' && self.explodedViewHasQty(self),
							label: oConfig.labels.qtyCol,
							field: 'diagramQty'
						},
						{
							if: self.childDisplayType() == 'exploded-view' && self.explodedViewHasNotes(self),
							label: oConfig.labels.notesCol,
							field: 'diagramNotes'
						}

					],
					self.childDisplayType() !== 'add-row' ? self.childSelectors() || [] : [],
					[
						{
							if: oConfig.usePromos,
							label: 'Promo',
							template: 'catalog.input_qty_promo_display'
						},
						{
							if: oConfig.useIdp,
							label: 'Status',
							template: _.property('warehouseTemplate')
						},
						{
							label: 'Price',
							headerClass: 'cell-right',
							cellClass: 'cell-right',
							field: 'unitPrice',
							format: utils.formatPrice,
							template: 'catalog.selected_product_price_display'
						},
						{
							label: 'Actions',
							headerClass: 'cell-right',
							cellClass: 'cell-right',
							template: 'catalog.atc_qty_input',
							width: '1%'
						}
					]
				);

				// diagram_number needs to be added to the child products using the pos field on the mapping table
				if (self.childDisplayType() == 'exploded-view') {
					columns.unshift({
						label: oConfig.labels.idCol,
						field: 'diagramNumber'
					});
				}

				return _.filter(columns, function (column) {
					return !('if' in column) || ko.unwrap(column.if);
				});
			});

			runHook('productModelReviews', { self: self, product: product, reviews: self.reviews });

			self.reviews = new PagedArray(self.reviews, 3);

			self.setupQuestions = function() {
				self.questions().forEach(function(question) {
					question.answers = ko.observableArray();

					if(question.type() === 'select') {
						if(question.useSelectOne()) {
							question.answers.push({
								label: question.selectOneText(),
								value: ''
							});
						}

						if(question.answerList().length > 0) {
							var answersSplit = question.answerList().split('|');
							answersSplit.forEach(function(answer) {
								var answerSet = answer.split('~');
								question.answers.push(
									{
										label: answerSet[0],
										value: answerSet[0]
									});
							});
						}
					}
				});
			}

			self.rowCollection = ko.observableArray();

			self.addRows = function(numberOfRows) {
				numberOfRows = numberOfRows || 1;
				for(var i=0;i<numberOfRows;i++) {
					var row = {
						selectedChild: ko.observable(undefined)
					}
					//' Clone the children for the selector so that the child
					//' references across rows are not the same.
					row.children = self.children ?  self.children().map(
														function(item) {
															var jsChild = ko.mapping.toJS(item);
															var koChild = ko.mapping.fromJS(jsChild, productMapping);

															//' Add the currentParent back as the re-mapping rests it
															koChild.currentParent = ko.observable(self);

															//' Add a new property for the current row index
															//' to make it easier to remove the items
															//' We use a guid to ensure it's always consistent.
															koChild.rowKey = utils.createGuid();
															return koChild;
														})
												: [];
					self.rowCollection.push(row);
				}
			}

			self.setupAddRowView = function() {
				if(self.childDisplayType() === 'add-row') {
					self.addRows();
					if(!!oConfig.childSkuMatch) {
						var matchedChild = self.rowCollection()[0].children.filter(function(child) {
												return oConfig.childSkuMatch === child.sku();
											});

						if(matchedChild && matchedChild.length > 0){
							self.rowCollection()[0].selectedChild(
								matchedChild[0]
							);
						}
					}
				}
			}

			self.childSelectors = ko.observableArray([]);

			self.processChildren = function() {
				_.each(self.children(), function(child){
					child.promoDescriptions = [];
					_.each(self.promoDescriptions, function(promo){
						if(promo.targetKeys.indexOf(child.key()) > -1){
							child.promoDescriptions.push(promo);
						}
					});
					child.configuratorEditData = self.configuratorEditData;
					child.currentParent(self);
					child.updateAtcButtonText();
					child.setupQuestions();
				});

				_.forEach(self, function (value, key) {
					var keyString = String(key);
					var matched = keyString.match(/^opt(\d)$/);

					if (matched && value) {
						var selector = {};
						selector.field = matched[0];
						selector.label = value;
						// _.sortByAll has been absorbed into _.sortBy in lodash 4.0.0+
						var lodashSortBy = typeof(_.sortByAll) == 'function' ? _.sortByAll : _.sortBy;

						selector.options = lodashSortBy(
							_.uniq(
								_.map(self.children(), function (child) {
									var optXsort = (typeof(child[matched[0] + '_sort']) == 'function' ? child[matched[0] + '_sort']() : child[matched[0] + '_sort']) || 0;
									var optSort = (typeof(child[matched[0]]) == 'function' ? child[matched[0]]() : child[matched[0]]) || 0;

									var thisOpt = {
										option: child[matched[0]],
										sort: optXsort || optSort
									};

									runHook("selectorOptionsItemOverride", { key: key, option: thisOpt, product: child });

									return thisOpt;

								}),
								'option'
							),
							[ 'sort', 'option' ]
						);
						selector.selectedOption = ko.observable();
						if (oConfig.detailConfig.selectChildProductOnLoad) {
							selector.selectedOption(selector.options[0]);
						}

						var childSku = oConfig.childSkuMatch || (!!self.configuratorEditData ? self.configuratorEditData.sku : '') || '';

						if (childSku){
							var oChild = {};
							_.each(self.children(),function(child){
								if(childSku == child.sku()) {
									oChild = child;
									return false;
								}
							});
							_.each(selector.options,function(option){
								if(option.option == oChild[selector.field]){
									selector.selectedOption(option);
								}
							})
						}
						selector.showSelector = ko.observable(true);
						self.childSelectors.push(selector);
					}
				});

				self.selectedOptions = ko.computed(function () {
					return _.map(ko.unwrap(self.childSelectors), function (item) {
						var selection = { option: '', sort: '' };
						if (item.selectedOption()) {
							selection = item.selectedOption().option;
						}
						return { field: item.field, option: selection };
					});
				});

				self.selectedProduct = ko.computed(function () {
					var filter = {};
					_.forEach(self.selectedOptions(), function (option) {
						filter[option.field] = option.option;
					});
					var filteredChildren = _.filter(self.children(), filter);
					if (filteredChildren
						&& filteredChildren.length === 1
						&& self.childDisplayType() !== 'input-qty'
						&& self.childDisplayType() !== 'exploded-view'
						&& self.childDisplayType() !== 'matrix-all'
						&& self.childDisplayType() !== 'add-row'
						) {
							return filteredChildren[0];
					} else {
						return self;
					}
				});

				self.setupQuestions();
				self.setupAddRowView();
			}

			if (self.children && self.children().length) {
				self.processChildren();
			} else {
				self.selectedProduct = ko.observable(self);
			}

			self.loadNextSelector = function(optNumber, callback, asyncLoadOptions) {
				var selectedOpts = _.map(self.childSelectors().filter(function(selector) {return !!selector.selectedOption && !!selector.selectedOption();}), function(s) {
					return s.selectedOption().option;
				}).join('~');
				$.ajax({
					url: oConfig.pageName, //current page.
					data: {
						ajax: 'get-product-opts',
						key: self.key(),
						optFields: selectedOpts,
						modal: 1,
						modalaction: utils.getParameter("modalaction")
					},
					dataType: 'json',
					success: function(data) {
						var selector = self.childSelectors().find(function(selector) {
							return selector.field == 'opt' + optNumber;
						});

						if(!selector) {
							selector = {
								field: 'opt' + optNumber,
								label: self['opt' + optNumber],
								showSelector: ko.observable(oConfig.displayDroplistPlaceholdersForLazyLoad)
							}
							self.childSelectors.push(selector);
						}

						selector.index = optNumber;
						selector.options = data.map(function(o) {
							return {
								option: o.opt,
								sort: 0,
								count: o.count,
								pKey: o.p_key
							}
						});
						selector.loading = ko.observable(false);
						selector.selectedOption = ko.observable();
						selector.showSelector(oConfig.displayDroplistPlaceholdersForLazyLoad || !!selector.options.length);

						selector.selectedOption.subscribe(function(newValue) {
							//clear any selectors after this one
							self.childSelectors().forEach(function(s) {
								if(s.index > selector.index) {
									s.selectedOption(undefined);
									s.options.splice(0, s.options.length);
									s.selectedOption.notifySubscribers();
									s.showSelector(oConfig.displayDroplistPlaceholdersForLazyLoad);
								}
							});

							self.childSelectors.notifySubscribers();

							if(self.children) self.children.removeAll();
							// did we actually select something
							if (newValue) {
								selector.loading(true);
								var callback = function() {
									selector.loading(false);
								}
								if (selector.index < self.childSelectors().length) {
									self.loadNextSelector(selector.index + 1, callback, asyncLoadOptions);
								} else {
									//load the child
									if (asyncLoadOptions) asyncLoadOptions.done = true;
									self.loadChildProduct(newValue.pKey, callback);
								}
							}else{
								self.selectedProduct(self);
							}
						});
						self.childSelectors.notifySubscribers();
					},
					error: function() {
						utils.popToastrError('Error Loading List', 'There was an error loading the product drop list');
					},
					complete: function() {
						if (callback) callback();
						if (asyncLoadOptions) {
							var asyncLoadOptionsArray = asyncLoadOptions.getArray();
							var index = optNumber - 1;
							var option = self.childSelectors()[index];
							var availableOptions = option.options;
							var selectedOption = null;
							if (index < asyncLoadOptionsArray.length) {
								_.forEach(availableOptions, function (value, key) {
									if (value.option == asyncLoadOptionsArray[index]) selectedOption = value;
								});
							} else if (oConfig.detailConfig.selectChildProductOnLoad) {
								selectedOption = availableOptions[0];
							}
							option.selectedOption(selectedOption);
						}
					}
				})
			}

			self.loadChildProduct = function(pKey, callback) {
				$.ajax({
					url: oConfig.pageName, //current page.
					data: {
						ajax: 'get-child-product',
						key: pKey,
						parentKey: oConfig.mainProductKey,
						modal: 1,
						modalaction: utils.getParameter("modalaction"),
						action: utils.getParameter("action") == 'showpricetrace' ? 'showpricetrace' : ''
					},
					dataType: 'json',
					success: function(data) {
						var product = ko.mapping.fromJS(data.products[0], productMapping);
						product.type = "child";
						// self.children.push(product);
						if(self.questions && self.questions().length && (oConfig.useCartOptions && product.useCartOptions() && !product.f_id())) {
							if(product.questions) {
								product.questions(ko.mapping.fromJS(ko.mapping.toJS(self.questions))());
							} else {
								product.questions = ko.observableArray(ko.mapping.fromJS(ko.mapping.toJS(self.questions))());
							}
						}
						product.setupQuestions();
						product.configuratorEditData = self.configuratorEditData;
						product.updateAtcButtonText();
						self.selectedProduct(product);
						runHook('productDetailLoadChildProductSuccess', { self: self, product: product });
					},
					error: function() {
						utils.popToastrError('Error Loading Product', 'There was an error loading the product');
					},
					complete: callback
				});
			}

			self.quantityRestrictionsHtml.subscribe(function(newValue) {
				var popoverElement = $('#pop-' + $.escapeSelector(self.sku().replace(/([^A-Za-z0-9[\]{}_.:-])\s?/g, '_')));
				if(popoverElement){
					var popoverData = popoverElement.data('popover');
					if (popoverData) {
						popoverData.options.content = newValue;
					}
				}
			});

			self.lazyLoadingInProgress = ko.observable(false);

			if (self.key() == oConfig.mainProductKey && self.type === 'parent' && self.children().length === 0 && getOriginalPageName() === 'pc_product_detail.asp' && self.childDisplayType() != 'droplist') {
				if (!runHook('productDetailGetChildProductsByParentKey', { self: self }, {}, this)) {
				self.lazyLoadingInProgress(true);
				$.ajax({
					url: oConfig.pageName, //current page.
					data: {
						ajax: 'get-child-products-by-parent-key',
						key: oConfig.mainProductKey,
						modal: 1,
						modalaction: utils.getParameter("modalaction")
					},
					dataType: 'json',
					success: function(data) {
						data.forEach(function(childProduct) {
							var product = ko.mapping.fromJS(childProduct, productMapping);
							product.type = "child";
							if(self.questions && self.questions().length && product.useCartOptions() && !product.f_id()) {
								if(product.questions) {
									product.questions(ko.mapping.fromJS(ko.mapping.toJS(self.questions))());
								} else {
									product.questions = ko.observableArray(ko.mapping.fromJS(ko.mapping.toJS(self.questions))());
								}
								//product.hasCartOptions(true);
							}
							product.configuratorEditData = self.configuratorEditData;
							product.updateAtcButtonText();
							//self.selectedProduct(product);
							self.children.push(product);
						});

						self.processChildren();
						/*
						if(self.childDisplayType() === 'matrix-all') {
							self.matrix(ko.unwrap(ko.mapping.fromJS(generateMatrixData(ko.mapping.toJS(self)))));
						}
                        */
						self.lazyLoadingInProgress(false);
					},
					error: function() {
						utils.popToastrError('Error Loading Product', 'There was an error loading the product');
					},
					complete: function() { self.lazyLoadingInProgress(false); }
				});
			}
			}

			/*
				This function generates the display data for parent products whose
				childDisplayType is 'matrix-all'. It returns a data structure that
				looks like this:

				[
					{
						label: <<Label of row>>,
						swatch: <<Either an HTML color without the # or blank. E.g. 00ff44>>,
						thumb: <<A thumbnail image link to display if the swatch is blank>>,
						cols: [
							{
								label: <<Label of column>>,
								product: <<This links to the product.children[x] data. When KO maps this, it will be its own self-contained ProductModel.>>
							},
							{
								... more columns
							}
						]
					},
					{
						... more rows
					}
				]
			*/
			
			self.matrix = ko.computed(function () {
				if (self.childDisplayType() === 'matrix-all') {
					/***** Main method *****/

					// Initial vars
					var rowOpt = '';
					var colOpt = '';
					var swatchOpt = "color_code";

					// Build the matrix
					var rawChildren = self.children();
					var bucketed = bucketChildren(rawChildren, rowOpt, colOpt, swatchOpt);
					bucketed.rowKeys = sortKeys(bucketed.rowKeys);
					bucketed.colKeys = sortKeys(bucketed.colKeys);

					runHook('productDetailMatrixBeforeConstruction', {
						parent: self,
						configs: {
							rowOpt: rowOpt,
							colOpt: colOpt,
							swatchOpt: swatchOpt
						},
						bucketed: bucketed
					});

					var matrix = constructMatrix(bucketed);

					runHook('productDetailMatrixData', {
						parent: self,
						matrix: matrix
					});

					return matrix;
				} else {
					return [];
				}

				/***** Helper functions *****/

				// Find the unique row and column values and their sorts, and index the children.
				function bucketChildren(rawChildren, rowOpt, colOpt, swatchOpt) {
					var initial = {
						products: {},
						rowKeys: {},
						colKeys: {}
					};

					var result = _.reduce(rawChildren, function(acc, cur) {
						var rowLabel = ko.toJS(cur[rowOpt]);
						var rowSort = ko.toJS(cur[rowOpt + '_sort']);
						var rowSwatch = ko.toJS(cur[swatchOpt]);
						var rowThumb = ko.toJS(cur['thumb']);
						var colLabel = ko.toJS(cur[colOpt]);
						var colSort = ko.toJS(cur[colOpt + '_sort']);

						// Index the child
						if (!acc.products[rowLabel]) {
							acc.products[rowLabel] = {};
						}
						acc.products[rowLabel][colLabel] = cur;

						// Keep track of the unique row values
						if (!acc.rowKeys[rowLabel]) {
							acc.rowKeys[rowLabel] = {
								label: rowLabel,
								sort: rowSort,
								swatch: rowSwatch,
								thumb: rowThumb
							};
						}

						// Make sure we have a swatch and thumb
						if (!acc.rowKeys[rowLabel].swatch) {
							acc.rowKeys[rowLabel].swatch = rowSwatch;
						}
						if (!acc.rowKeys[rowLabel].thumb) {
							acc.rowKeys[rowLabel].thumb = rowThumb;
						}

						// Keep track of the unique column values
						acc.colKeys[colLabel] = {
							label: colLabel,
							sort: colSort
						};

						runHook('productDetailMatrixReduce', {
							parent: self,
							configs: {
								rowOpt: rowOpt,
								colOpt: colOpt,
								swatchOpt: swatchOpt
							},
							acc: acc,
							cur: cur
						});

						return acc;
					}, initial);

					runHook('productDetailMatrixBucketed', {
						parent: self,
						configs: {
							rowOpt: rowOpt,
							colOpt: colOpt,
							swatchOpt: swatchOpt
						},
						bucketed: bucketed
					});

					return result;
				}

				// Sort the rowKeys and colKeys
				function sortKeys(obj) {
					var list = _.values(obj);
					var sorted = _.sortBy(list, 'sort');
					return sorted;
				}

				function constructMatrix(bucketed) {
					// Construct the matrix, one row at a time
					var matrix = _.map(bucketed.rowKeys, function(row) {
						// Get values
						var label = row.label || '';
						var swatch = row.swatch || '';
						var thumb = row.thumb || 'images/no-image.png';

						// Generate the columns
						var cols = constructColumns(bucketed, row);

						// Return the results
						var row = {
							label: label,
							swatch: swatch,
							thumb: ko.observable(thumb), // Don't know why this is an observable, so leaving it
							cols: cols
						};

						runHook('productDetailMatrixRow', {
							parent: self,
							bucketed: bucketed,
							row: row
						});

						return row;
					});

					return matrix;
				}

				function constructColumns(bucketed, row) {
					var columns = _.map(bucketed.colKeys, function(col) {
						// Find the product that sits at the intersection of row and col
						var product = bucketed.products[row.label][col.label];

						var col = {
							label: col.label,
							product: product
						};

						runHook('productDetailMatrixCol', {
							parent: self,
							bucketed: bucketed,
							row: row,
							col: col
						});

						return col;
					});

					return columns;
				}
			}).extend({ deferred: true });

			if ((self.key() == oConfig.mainProductKey || oConfig.mainProductKey == '') && self.type !== 'parent') {
				self.setupQuestions();
			}

			// 2019-07-05 EJ - This only needs to run on the mainProduct on
			//                 the product detail page so I added a mainProductKey
			//                 var and check it against the current product's key
			if (self.key() == oConfig.mainProductKey && self.type === 'parent' && self.childSelectors().length === 0 && getOriginalPageName() === 'pc_product_detail.asp' && self.childDisplayType() == 'droplist') {

				if(!self.opt1 || self.opt1 == ''){
					//force this to stand-alone because it has bad data setup.
					self.type = 'stand-alone';
					console.log('This Parent product is set to "droplist" type but doesn\'t have a value in the opt1 field.  This is required for drop list type products.');
				}else{
					var selectorCount = 0;
					_.forEach(self, function (value, key) {
						var keyString = String(key);
						var matched = keyString.match(/^opt(\d)$/);

						if (matched && value) {
							selectorCount++;
							var selector = {};
							selector.field = matched[0];
							selector.label = value;
							selector.options = [];
							selector.selectedOption = ko.observable();
							selector.loading = ko.observable(matched[0] === 'opt1');
							selector.index = selectorCount;
							selector.showSelector = ko.observable(oConfig.displayDroplistPlaceholdersForLazyLoad);

							self.childSelectors.push(selector);
						}
					});

					// as third option:
					//   pass an array of options to do loading
					//   pass a shorter array to load up to that point then load first option from then on
					//   pass null/false to not do loading
					self.loadNextSelector(1, null, new (function() { // this is an inplace constructor
						this.array = oConfig.childOptionList,
						this.done = false;
						this.getArray = function() {
							if (this.done) return [];
							else return this.array;
						}
					})());
				}
			}

			self.removeRow = function(rowKey) {
				var index = _.findIndex(self.rowCollection(), function(row) {
					return row.selectedChild().rowKey === rowKey;
				});

				self.rowCollection.splice(index, 1);
			}

			self.currentParent = ko.observable(function() {
				if(self.type !== 'child') {
					return self;
				}

				if (self.currentparent && self.currentParent()) {
					return self.currentParent
				}

				if (self.type === 'child' && viewModel && ko.unwrap(viewModel.mainProduct)) {
					var matchingParent = self.parents().find(
						function(parent) {
							return parent.key() === ko.unwrap(viewModel.mainProduct).key();
						}
					)
					return matchingParent ? ko.unwrap(viewModel.mainProduct) : self;
				}

				return self;
			}());

			self.addRowsToCart = function() {
				var items = self.rowCollection().map(
					function(item) {
						return item.selectedChild();
					}
				).filter(
				function(item) {
					return item !== undefined
				});

				utils.addToCart(items, undefined, self.key(), true);
			}

			self.rowsAtcAttempted = ko.observable(false);

			self.questionsHaveValidAnswers = ko.computed(function() {
				var validOptions = true;

				if(self.useCartOptions()) {
					self.questions().some(function(question) {
						var answer = question.selectedAnswer();
						var answerPopulated = (answer !== '' && answer !== undefined);
						if(question.required() && !answerPopulated) {
							validOptions = false;
						}
						return !validOptions;
					});
				}

				return validOptions;
			});

			self.allRowsValid = ko.computed(function() {
				var rowsValid = true;

				self.rowCollection().some(function(row) {
					rowsValid = row.selectedChild() === undefined || row.selectedChild().questionsHaveValidAnswers();
					return !rowsValid;
				});

				return rowsValid;
			});

			// List of fields that will have legitimate HTML in them. Note - we
			// are intentionally not putting these in a site option in order to
			// force any changes to go through source control.
			var fieldsToDecode = [
				'inventory.stockMessage',
				'description',
				'name',
				'tabs>staticContent',
				'thumbAltText',
				'picAltText',
				'addOnLabel',
				'opt1',
				'opt2',
				'opt3',
				'opt4',
				'opt5',
				'opt6'
			];

			runHook('fieldsToDecodeFilter', { self: self, product: product, fieldsToDecode: fieldsToDecode });

   			// Set each field in the above list to the decoded version of itself
            fieldsToDecode.forEach(function(propName) {
                //using a > to denote a property on an array.
                if(propName.indexOf('>') > -1){
                    //get the array and the property on that array to be decoded
                    var collection = self[propName.split('>')[0]];
                    var propName = propName.split('>')[1];

                    //now check to see if the array is observable before looping
                    if(typeof collection == 'function'){
                        _.each(collection(), function(obj){
                            if (typeof obj()[propName] == 'function') { // If it is an observable
                                obj()[propName](utils.decodeHTML(obj()[propName]()) );
                            } else if (typeof obj()[propName] == 'string') { // If it is a string
                                obj()[propName] = utils.decodeHTML(obj()[propName]);
                            } else {
                                // Do nothing
                            }
                        })
                    }else{
                        _.each(collection, function(obj){
                            if (typeof obj[propName] == 'function') { // If it is an observable
                                obj[propName](utils.decodeHTML(obj[propName]()) );
                            } else if (typeof obj[propName] == 'string') { // If it is a string
                                obj[propName] = utils.decodeHTML(obj[propName]);
                            } else {
                                // Do nothing
                            }
                        })
                    }

                }else{
                    var field = _.get(self, propName);

                    if (typeof field == 'function') { // If it is an observable
                        field( utils.decodeHTML(field()) );
                    } else if (typeof field == 'string') { // If it is a string
                        _.set(self, propName, utils.decodeHTML(field));
                    } else {
                        // Do nothing
                    }
                }
            });

			if(self.currentParent()) {

				if(self.documents().length == 0 && self.currentParent().documents().length > 0 ){
					self.documents(self.currentParent().documents());
				}

				if(!self.description() && self.currentParent().description()) {
					self.description(self.currentParent().description())
				}

				//Handle Tab Inheritance at the individual tab instead of the array of tabs
				var newTabs = [];
				for(i=1; i <= oConfig.detailConfig.numberOfProductTabs; i++){
					var parentTab = _.filter(self.currentParent().tabs, function(tab){ return tab.index == i})[0];
					var childTab  = _.filter(self.tabs,                 function(tab){ return tab.index == i})[0];
					if(childTab){
						newTabs.push(childTab);
					}else if(!childTab && parentTab){
						//Inherit from the parent
						newTabs.push(parentTab);
					}
				}
				if(newTabs){
					self.tabs = newTabs;
				}

			}

			if (self.showAddOns)  {
				if (self.addOnDisplay == 'checkboxes')
					self.selectedAddOns = ko.observableArray([]);
				else //             ^ note difference in plural
					self.selectedAddOn  = ko.observable();
			}

			self.addOnOptions = function(){
				var options = [];
				
				if(self.addOns){
					options = _.map(self.addOns(), function(product){
						product.selectedQty(product.minQty() || 1);
						return ko.observable(product)
					})
				}
				return options;
			}();

			runHook('productModelBottom', { self: self, product: product });
		};

		var productMapping = {
			create: function (options) {
				return new productModel(options.data);
			}
		};

		var searchResultsPropertiesModel = function (properties) {
			var self = this;

			self.selectedSort = ko.observable(utils.getParameter('sortby') || oConfig.searchConfig.defaultSort);
			self.selectedRpp = ko.observable(utils.getParameter('rpp') || oConfig.searchConfig.rpp);
			self.startCount = ko.observable((oConfig.searchConfig.page - 1) * oConfig.searchConfig.rpp + 1);

			self.endCount = ko.observable(function () {
				var rpp = (oConfig.searchConfig.rpp === 0) ? oConfig.searchConfig.total : oConfig.searchConfig.rpp;
				return Math.min(((oConfig.searchConfig.page - 1) * rpp) + rpp, oConfig.searchConfig.total);
			}());

			self.maxPage = ko.observable(
				Math.ceil(oConfig.searchConfig.total / oConfig.searchConfig.rpp)
			);

			self.selectedLayout = ko.observable(
				utils.getCookie('productLayout')
			);

			self.changeRpp = function (value) {
				var sUrl = utils.setParameter('rpp', value);
				sUrl = sUrl + '&page=1';
				window.location =  sUrl;
			};

			self.layoutTemplate = ko.computed(function () {
				utils.setCookie('productLayout', self.selectedLayout());
				window.location.reload();
				return 'catalog.' + self.selectedLayout() + '_view'
			});

			runHook('searchResultsPropertiesModelBottom', { self: self, properties: properties });
		};

		function PagedArray(array, pageSize) {
			//' if array has already been processed, just return it
			if(array && array.all) {
				return array;
			}
			var self = this;
			self.all = ko.observableArray(array || []);
			self.pageSize = ko.observable(pageSize || 10);
			self.page = ko.observable(1).extend({ counter: 1 });
			self.numPages = ko.computed(function () {
				return Math.ceil(self.size() / self.pageSize());
			});
			self.size = ko.computed(function () {
				return self.all().length;
			});
			self.items = ko.computed(function () {
				var pageSize = self.pageSize();
				var start = (self.page() - 1) * pageSize;
				return self.all.slice(start, start + pageSize);
			});
		}
	</script>


<script>
	$(function () {
		if(typeof(ko) != 'undefined'){
			ko.applyBindings();
		}
	});
	
</script>
</div>

<div class="cart-templates-scripts">
<script type="text/javascript">

    addTimer("start of ko model");
    $( document ).ready(function() {

        if('default.asp' == 'payment.asp' ){
            $(document).on('hidden', '#global_modal', function () {
                if( $('#handle-modal-exit').val() == 'true'){
                    $('#handle-modal-exit').val(false);
                    viewModel.genericModalComplete();
                }
            });
        }
        addTimer("cart_templates: document.ready");

    });

    function emptyHandler(){}

    function scrollToSection(id) {
         $('html, body').animate({
            // subtracting body padding-top accounts for toolbars with absolute
            // position, such as the SU bar
            scrollTop: $(id).offset().top - parseInt($("body").css('padding-top'))
         }, 300,
        function(){
            $('html, body').clearQueue();
        });
    }

    function reloadPage() {
        viewModel.processing(true);
        location.reload();
	}

	function sendError(message) {
		if (window['insightRUM']) {
			insightRUM.rawErrors.push(["Error", sPageName, 1, 1, new Error(message.substring(0,149))]);
		}
	}

    function buildContinueShoppingUrl(){
        var url = ofConfig.continueShopPage;
        if(ofConfig.guestLoggedIn){
            url = utils.setParameter('action','logout',url);
        }
        return url;
    }

    function isValidRequestedDate(date) {
		try {
			isBusinessDay = businessDaySettings[1][""].work_days_of_week == undefined || businessDaySettings[1][""].work_days_of_week.indexOf(date.getDay()+1) > -1;
			isHoliday = businessDaySettings[1][""].holiday_dates != undefined && businessDaySettings[1][""].holiday_dates.indexOf(moment(date).format("MM/DD/YYYY")) > -1;
			if(isBusinessDay && !isHoliday) {
				return true;
			} else {
				return false;
			}
		} catch (error) {
			sendError("isValidRequestedDate: " + error.message);
			return true;
		}
    }

    function calculateAdjustedLeadTimeDays(effectiveOrderDate, leadTimeDays) {
        /*
            EJ - 2016-11-10 - adding 1 initially due to JS interpreting the date from the db as UTC date
                              when in fact it is a local date at midnight.  This causes the date
                              in JS to be the day before at 7pm (-5) or 8pm (-4) depending on time
                              of year.
                              Since we are only really concerned with the day, we can add one to
                              compensate for this.
        */
        var adjustedLeadTimeDays = leadTimeDays;

        /* EJ - 2016-11-10 - This if block helps mitigate when the cuttoff time has passed, but the
                             effective_order_date on the SelectedShipVia has not be recalced by the
                             ordering object yet.  It essentially checks the cutoff if the effective_order_date
                             is today.  Otherwise, there is no need to calculate it.
        */
        if(moment(effectiveOrderDate).date() <= moment().date()) {
            var currentDateTime = new Date();
            var currentUTCSeconds = currentDateTime.getUTCSeconds() + (60 * currentDateTime.getUTCMinutes()) + (60 * 60 * currentDateTime.getUTCHours());

            if(currentUTCSeconds > businessDaySettings[1][""].utc_cutoff_time_seconds) {
                adjustedLeadTimeDays += 1;
            }
        }

        var startingDate = moment();

        for(var i = 1; i < adjustedLeadTimeDays + 1; i++) {
            if(!isValidRequestedDate(startingDate.add(1, "days").toDate())) {
                adjustedLeadTimeDays += 1;
            }
        }

        return adjustedLeadTimeDays;
    }

    function autoAllocateItems() {
        if((!viewModel.useMultiShipEditUI()) || (viewModel.shipments().length === 1
            && viewModel.shipments()[0].shipTo().key() != ""
            && viewModel.shipments()[0].shipTo().key() != null)) {
                if(viewModel.shipments()[0].details().length === 0) {
                    viewModel.moveAllItemsToShipment(viewModel.shipments()[0]);
                }
        }
    }

    //This div is contained in the SU dashboard bar.
    utils.setActiveQuote = function (orderKey) {
        if(orderKey != ofConfig.SessionOrderKey) {
             jQuery.ajax({
                url: 'payment.asp' + '?o_key=' + viewModel.orderKey() + '&ajax=true&pageaction=setActiveQuote&quote_id=' + orderKey + '&randomnum=' + new Date().getTime()
                , cache: false
                , success: function(data,status,request){

                }
                , error: function(data) {
                    alert('Error posting Recurring Order Settings');
                }
                , complete: function(data) {
                }
            });
        }
    }

    utils.removeActiveQuote = function (bStartNewQuote) {
        utils.setActiveQuote('');
        if(bStartNewQuote){
            window.location = 'payment.asp' + (ofConfig.isModal ? '?modal=1' : '');
        }else{
            $('.active-quote-message').hide();
        }
    };

    var orderInfoPostUrl = 'payment.asp'; //'https://' + window.location.hostname + window.location.pathname;
    var orderInfoPostApiUrl = "/ordering/";
    var countries = [];
    var newCustomer = false;
    var viewModel;
    var order;

    function addAddressHandler() {
        viewModel.genericModalComplete();
        //viewModel.allocateShipments(false);
        apiGetShippingAddresses();
    }

    function changeAddressFromFinder(response, object){
        console.log(response);
        var address = new addressInfo({ key: response.sha_key.value, state: '', city: '', zipCode: '', country: '', name: response.sha_key_disp.value, address1: '', global: 0 });
        object.shipTo(address);
    }

    function getAddressOptFields(){
        var addressOptFields = [];
        for(i=1; i <= 5; i++){
            if(ofConfig['showAddressOpt'+i]){
                addressOptFields.push(i);
            }
        }
        return addressOptFields;
    }

    function apiGetShippingAddresses(editedKey) {
        $.ajax({
            url: orderInfoPostUrl + '?o_key=' + viewModel.orderKey() + '&ajax=true&pageaction=apiGetShippingAddresses',
            success: function(data) {
                // viewModel.shippingAddresses = processShippingAddresses(JSON.parse(data))();
                // viewModel.shippingAddresses.notifySubscribers();
                var newVarForTesting = processShippingAddresses(JSON.parse(data))();
				var editedShaKey = editedKey || '';
				var bFound;
                _.each(newVarForTesting, function(newItem){
                    _.each(viewModel.shippingAddresses(),function(oldItem){
                        if(editedShaKey != ''){
                            if(oldItem.key() == editedShaKey && newItem.key() == editedShaKey){
                                oldItem.name(newItem.name());
                                oldItem.phone(newItem.phone());
                                oldItem.address1(newItem.address1());
                                oldItem.address2(newItem.address2());
                                oldItem.address3(newItem.address3());
                                oldItem.address4(newItem.address4());
                                oldItem.address5(newItem.address5());
                                oldItem.city(newItem.city());
                                oldItem.state(newItem.state());
                                oldItem.zipCode(newItem.zipCode());
                                oldItem.opt1(newItem.opt1());
                                oldItem.opt2(newItem.opt2());
                                oldItem.opt3(newItem.opt3());
                                oldItem.opt4(newItem.opt4());
                                oldItem.opt5(newItem.opt5());
                                return false;
                            }
                            bFound = true;
                        }else{
                            bFound = false;
                            if(oldItem.key() == newItem.key()){
                                bFound = true;
                                return false;
                            }
                        }
                    });
                    if(!bFound && editedShaKey == ''){
                        viewModel.shippingAddresses.push(ko.mapping.fromJS(newItem, shippingAddressMappingOptions));
                        return false;
                    }
                });
                viewModel.availableAddresses = viewModel.shippingAddresses;
            },
            error: function(){
                alert('error getting shipping addresses');
            }
        });
    }

    var shippingAddressMappingOptions = {
        create: function(options) {
            var address = new addressInfo(options.data);
            address.summaryLine = ko.computed(function() {
                var lineText = '';

                function separator(separatorText) {
                    return lineText === '' ? '' : separatorText;
                }

                function addField(field, separatorText) {
                    if(!separatorText) { separatorText = ', '}
                    lineText += field ? separator(separatorText) + field : '';
                }

                addField(address.name());
                addField(address.company());
                addField(address.attention());
                addField(address.firstName());
                addField(address.lastName(), address.firstName() ? ' ' : '');
                addField(address.address1());
                addField(address.address2());
                addField(address.address3());
                addField(address.address4());
                addField(address.address5());
                addField(address.global());
                addField(address.city());
                addField(address.state());
                addField(address.zipCode(), ' ');
                addField(address.country());

                return lineText;
            });
            return address;
        }
    }

    function getNewAddress() {
        return {
            name: "",
            firstName: "",
            lastName: "",
            company: "",
            attention: "",
            address1: "",
            address2: "",
            address3: "",
            address4: "",
            address5: "",
            country: "USA",
            city: "",
            state: "",
            county: "",
            zipCode: "",
            phone: "",
            email: "",
            global: "0",
            opt1: "",
            opt2: "",
            opt3: "",
            opt4: "",
            opt5: "",
            key: utils.createGuid()
        }
    }

    function getShippingFromBilling() {
        var newAddress = JSON.parse(ko.toJSON(viewModel.Account));

        newAddress.firstName = viewModel.Customer().firstName();
        newAddress.LastName = viewModel.Customer().LastName();
        newAddress.Email = viewModel.Customer().Email();
        newAddress.Phone = viewModel.Customer().Phone();
        newAddress.Name = "";
        newAddress.Attention = viewModel.Customer().firstName() + ' ' + viewModel.Customer().LastName();
        newAddress.key = utils.createGuid();

        return newAddress;
    }

    function getNewShipment() {
        return new Shipment({
            Key: utils.createGuid(),
            SelectedShipVia: {},
            ShipViaChoices: [],
            Details: [],
            LeadTimeDays: null,
            ShipTo: getNewAddress(),
            Comments: "",
            availableAddresses: shippingAddresses
        });
    }

    function generateRefId(){
        var today = new Date();
        var dd = today.getDate();
        var mm = today.getMonth()+1; //January is 0!
        var yyyy = today.getFullYear();
        var rand = Math.floor(Math.random() * 1000000)

        if(dd<10) {
            dd='0'+dd
        }

        if(mm<10) {
            mm='0'+mm
        }

        today = yyyy+mm+dd+'-'+rand;
        return today;
    }

    var StateChoice = function(code, name) {
        var self = this;

        var choice = {};
        choice["code"] = code;
        choice["name"] = name;

        ko.mapping.fromJS(choice, {}, self);
    }

    var detailLineInstanceSort = function (a, b) {
            var aInstance = a.instance();
            var bInstance = b.instance();
            var aLineNum = a.orderDetailNum();
            var bLineNum = b.orderDetailNum();
            var aParentId = a.parentProductID().trim();
            var bParentId = b.parentProductID().trim();

            if(aInstance == bInstance) {
                return (aParentId < bParentId) ? -1 : (aParentId > bParentId) ? 1 : 0;
            } else {
                return (aLineNum < bLineNum) ? -1 : 1;
            }
    }

    var shipmentDetailsInstanceSort = function(a, b) {
        return detailLineInstanceSort(a.orderDetail, b.orderDetail);
    }

    var addressInfo = function(addressData, addressType) {
        var self = this;

        if(!addressData) {
            addressData = getNewAddress();
        }

        // Order JSON returns isGlobalAddress while the rest of the template loads in data relying on global
        if('isGlobalAddress' in addressData && !('global' in addressData)){
            addressData.global = addressData.isGlobalAddress;
        }
        
        addressData.addressType = ko.observable(addressType || 'shipping');

        ko.mapping.fromJS(addressData, {}, self);

        if(!self.state()) {
            self.state(undefined);
        }

        for(var prop in self) {
            if(self.hasOwnProperty(prop) && typeof self[prop] === 'function') {
                if(self[prop]() === null || self[prop]() === 'null') {
                    self[prop]('');
                }
            }
        }

        self.address1.extend({ required: true });
        self.country.extend({ required: true });
        self.city.extend({ required: false });
        self.state.extend({ required: ofConfig.bRequireStateForShipping });
        self.zipCode.extend({ required: ofConfig.bRequireZIPCodeForShipping });

        if(!self.country()) {
            self.country("USA");
        }

        self.stateChoices = ko.observableArray();

        self.editing = ko.observable(false);

        self.loadStates = function() {
            countries.forEach(function(country) {
                if(country.iso3 === self.country()) {
                    self.stateChoices(country.states);
                }
            });
            if(!self.country()){
                self.stateChoices.removeAll();
            }
        }

        self.country.subscribe(function() {
            self.loadStates();
        });

        self.loadStates();
    }

    var detailMap = function(detailLine, shipment, order) {
        var self = this;
        self.detailLine = detailLine;
        self.shipment = shipment;
        self.totalOrderQty = ko.computed(function() { return detailLine.qty(); });
        self.qtyToShip = ko.observable();
        self.qtyInShipment = ko.observable(function() {
                self.qtyToShip(self.shipment.details().reduce(function(prev, curr, index, arr) {
                    if(curr.orderDetailId() == detailLine.orderDetailKey()) {
                        return prev + Number(curr.qtyToShip());
                    } else {
                        return prev;
                    }
                }, 0));
                return self.qtyToShip();
            }()
        );

        self.qtyInAllShipments = ko.computed(function() {
            var theKey = self.shipment.key();
            var allShipmentsTotal = order.shipments().reduce(function(runningTotal, individualShipment) {
                var individualShipmentTotal = individualShipment.details().reduce(function(runningSubTotal, detail) {
                    if(detail.orderDetailId() == detailLine.orderDetailKey()) {
                        return runningSubTotal + Number(detail.qtyToShip() || 0);
                    } else {
                        return runningSubTotal;
                    }
                }, 0);
                return runningTotal += individualShipmentTotal;
            }, 0);

            var currentShipmentTotal = self.shipment.details().reduce(function(runningSubTotal, detail) {
                if(detail.orderDetailId() == detailLine.orderDetailKey()) {
                    return runningSubTotal + Number(detail.qtyToShip() || 0);
                } else {
                    return runningSubTotal;
                }
            }, 0)

            var otherShipmentsTotal = allShipmentsTotal - currentShipmentTotal;

            return otherShipmentsTotal + Number(self.qtyToShip() || 0);
        });

        self.unallocatedQty = ko.computed(function() { return self.detailLine.qty() - self.qtyInAllShipments(); });
        self.toggleItemSelect = ko.computed({
            read: function() {
                return this.qtyToShip() > 0;
            },
            write: function(isChecked) {
                if(isChecked){
                    this.qtyToShip(1);
                } else {
                    this.qtyToShip(0);
                }
            }
        }, self);

        runHook('detailLineModelBottom', { self: self, detailLine: self });
    };

    var Shipment = function(shipmentData) {
        var self = this;
        ko.mapping.fromJS(shipmentData, {
            shipTo : shippingAddressMappingOptions,
            shipViaChoices : {
                create: function(options) {
                    if(options.data == null) {
                        return ko.mapping.fromJS([]);
                    } else {
                        return ko.mapping.fromJS(options.data);
                    }
                }
            },
            selectedShipVia : {
                create: function(options) {
                    if(options.data == null) {
                        return ko.mapping.fromJS({requestDate: "0001-01-01T00:00:00"});
                    } else {
                        return ko.mapping.fromJS(options.data);
                    }
                }
            },
            details: {
                create: function(options) {
                    options.data.orderDetail.configuratorJson = options.data.orderDetail.configuratorJson ? JSON.parse(options.data.orderDetail.configuratorJson) : {};
                    if(options.data.orderDetail.configuratorJson && options.data.orderDetail.configuratorJson.choices) {
                        options.data.orderDetail.configuratorJson.choices = options.data.orderDetail.configuratorJson.choices.sort(function(a, b) {
                            if(a.pos == b.pos) {
                                return 0;
                            }
                            return a.pos < b.pos ? -1 : 1;
                        });
                    }
                    if(options.data.orderDetail.configuratorJson) {
                        options.data.orderDetail.configuratorJson.configType = options.data.orderDetail.configuratorJson.configType || 'configurator';
                    }
                    options.data.orderDetail.parentProductID  = options.data.orderDetail.parentProductID.trim() || '';
                    options.data.orderDetail.editing = false;

                    return ko.mapping.fromJS(options.data);
                }
            }
        }, self);

        self.details().forEach(function(detail) {
            var detailLine = detail.orderDetail;
            detailLine.instanceChildren = self.details().filter(function(item) {
                                            return item.orderDetail.instance() == detailLine.instance() && item.orderDetail.parentProductID().trim() != '' && item.orderDetail != detailLine;
                                        }).map(function(item) { return item.orderDetail; }) || [];

            var mainProductArray = self.details().filter(function(item) {
                                            return item.orderDetail.instance() == detailLine.instance() && item.orderDetail.removeType() == 'instance';
                                        });
            var mainProduct = mainProductArray.length > 0 ? mainProductArray[0].orderDetail : detailLine;

            var mainProductQty = mainProduct.qty();

            detailLine.instanceUnitPrice = ko.observable(self.details().reduce(function(current, item) {
                if(!ofConfig.showProductDiscount){
                    item.orderDetail.priceBeforeAdjustment(item.orderDetail.price());
                }
                if(item.orderDetail.instance() == detailLine.instance()) {
                    //This IF block is attempting to account for calculating the unit price on "nested" configurator lines.  Any other product type should fall into
                    //  The else block so it just pulls the quantity from the od lines.
                    //TODO:  Account for configurator products AND split by address as this calculation will likely not work in that scenario.
                    //       In this scenario, it should use the extended quantity on the shipment not the one from the OD line.
                    if(item.orderDetail.parentProductID().trim() != '' && item.orderDetail.removeType() != 'instance' && item.orderDetail.removeType() != 'OD_Key') {
                        return current += ((item.orderDetail.priceBeforeAdjustment() || item.orderDetail.price()) * item.qtyToShip()) / mainProductQty;
                    } else {
                        return current += (item.orderDetail.priceBeforeAdjustment() || item.orderDetail.price());
                    }
                } else {
                    return current;
                }
            },0));

            detailLine.instanceUnitPriceDisplay = ko.computed(function(){
                return utils.formatMoney(detailLine.instanceUnitPrice(), utils.decimalPlacesOnUnitPrices);
            });

            detailLine.instanceExtPrice = ko.observable(self.details().reduce(function(current, item) {
                if(item.orderDetail.instance() == detailLine.instance()) {
                    return current += (item.orderDetail.priceBeforeAdjustment() || item.orderDetail.price()) * item.qtyToShip();
                    // return current += item.orderDetail.extTotal();
                } else {
                    return current;
                }
            },0));
        });

        self.details.sort(shipmentDetailsInstanceSort);

        self.availableAddresses = shippingAddresses;

        self.shipTo = ko.observable(self.shipTo);

        self.availableAddresses().forEach(function(address,index){
            if(!self.shipTo()){
                self.shipTo(address);
                return false;
            }
            if(address.key() == self.shipTo().key()){
                self.shipTo(address);
            }
        });

        self.earliestShipDate = ko.observable(
            function() {
                if(self.selectedShipVia && self.selectedShipVia.effectiveOrderDate) {
                    var adjustedLeadTimeDays = calculateAdjustedLeadTimeDays(self.selectedShipVia.effectiveOrderDate(), self.leadTimeDays());
                    return moment(self.selectedShipVia.effectiveOrderDate()).add(adjustedLeadTimeDays, "days").format("MM/DD/YYYY");
                } else {
                    return moment(new Date(self.ExpectedShipDate)).format("MM/DD/YYYY");
                }
            }()
        );

        self.requestedShipDate = ko.observable(
            function() {
                if(self.selectedShipVia && self.selectedShipVia.requestDate()) {
                    if(self.selectedShipVia.requestDate() >= self.earliestShipDate()) {
                        return moment(new Date(self.selectedShipVia.requestDate())).format("MM/DD/YYYY");
                    } else {
                        return self.earliestShipDate();
                    }
                } else {
                    if(self.selectedShipVia && self.selectedShipVia.effectiveOrderDate) {
                        var adjustedLeadTimeDays = calculateAdjustedLeadTimeDays(self.selectedShipVia.effectiveOrderDate(), self.selectedShipVia.leadTimeDays());
                        return moment(self.selectedShipVia.effectiveOrderDate()).add(adjustedLeadTimeDays - 1, "days").format("MM/DD/YYYY");
                    } else {
                        return moment(new Date(self.ExpectedShipDate)).format("MM/DD/YYYY");
                    }
                }
            }()
        );

        if(self.selectedShipVia && self.selectedShipVia.shippingAccountId){
            self.selectedShipVia.shippingAccountId = ko.validatedObservable(self.selectedShipVia.shippingAccountId() || '').extend({ required: true, message: 'Please select a shipping account.'});

            self.selectedShipVia.shippingAccountId.subscribe(function(shippingAccountKey){
                var postData = {};
                var orderShipViaDetails = [];

                orderShipViaDetails = [
                    {
                        "osvd_key" : self.selectedShipVia.orderShipViaDetailKey(),
                        "sa_id" : shippingAccountKey
                    }
                ]

                postData =
                {
                    "Tables": [
                        {
                            "TableName" : "orders_ship_via_details",
                            "TableKeyField" : "osvd_key",
                            "UserKeyField" : "osvd_key",
                            "UserKeyIsPrimaryKey" : "True",
                            "Data" : orderShipViaDetails
                        }
                    ]
                };

                postLogicJsonAjax(postData, true);
            });
        }else{
            self.selectedShipVia = {};
            self.selectedShipVia.shippingAccountId = ko.observable('');
            self.selectedShipVia.collectShippingAccount = ko.observable(false);
            self.selectedShipVia.name = ko.observable('');
            self.selectedShipVia.description = ko.observable('');
            self.selectedShipVia.total = ko.observable(0);
            self.selectedShipVia.shipViaChoiceID = ko.observable('');
            self.selectedShipVia.sv_ref_id = ko.observable('');
        };

        if(self.shipViaChoices && self.shipViaChoices().length > 0){
            self.selectedShipViaChoice = ko.observable(self.shipViaChoices().find(function(choice) {
                return (choice.shipViaChoiceKey() == self.selectedShipVia.shipViaChoiceID() || choice.shipViaRefID() == self.selectedShipVia.sv_ref_id());
            }) || self.shipViaChoices()[0]);
        }else{
            self.selectedShipViaChoice = ko.observable();
        }

        self.shipToValid = ko.computed(function() {
            return ko.validatedObservable(self.shipTo()).isValid();
        });

        self.saveShipToAddress = function(shipment) {

            var shipmentArray = [];

            var details = shipment.details().map(function(map, index, array) {
                return {
                    orderDetailId : map.orderDetailId(),
                    qtyToShip: map.qtyToShip()
                }
            });

            var shipmentInfo = {
                "Key": shipment.key(),
                "ShipmentId": shipment.key(),
                "ShipTo": {
                    key         : shipment.shipTo().key(),
                    name        : shipment.shipTo().name(),
                    company     : shipment.shipTo().company(),
                    attention   : shipment.shipTo().attention(),
                    address1    : shipment.shipTo().address1(),
                    address2    : shipment.shipTo().address2(),
                    address3    : shipment.shipTo().address3(),
                    address4    : shipment.shipTo().address4(),
                    address5    : shipment.shipTo().address5(),
                    city        : shipment.shipTo().city(),
                    state       : shipment.shipTo().state(),
                    zipCode     : shipment.shipTo().zipCode(),
                    county      : shipment.shipTo().county(),
                    country     : shipment.shipTo().country(),
                    email       : shipment.shipTo().email(),
                    firstName   : shipment.shipTo().firstName(),
                    lastName    : shipment.shipTo().lastName(),
                    phone       : shipment.shipTo().phone(),
                    global      : shipment.shipTo().global(),
                    opt1        : shipment.shipTo().opt1(),
                    opt2        : shipment.shipTo().opt2(),
                    opt3        : shipment.shipTo().opt3(),
                    opt4        : shipment.shipTo().opt4(),
                    opt5        : shipment.shipTo().opt5()
                },
                "Details": details
            };

            shipmentArray.push(shipmentInfo);

            var postOptions = {
                data: "shipmentJson=" + encodeURIComponent(JSON.stringify(shipmentArray)),
                success: function(data) {
                    autoAllocateItems();
                    self.showShippingAddressEdit(false);
                },
                error: function() {
                    alert('error saving shipping address');
                }
            }

            postInfo(postOptions, 'updateShipToAddress', shipment.key());
        };

        self.setShippingToBilling = function(shipment) {
            var newAddress = getShippingFromBilling();
            shipment.shipTo(ko.mapping.fromJS(newAddress,shippingAddressMappingOptions));
            shipment.saveShipToAddress(shipment);
        }

        self.showShippingAddressEdit = ko.computed({
            read: function() {
                if(!self.shipToValid()) {
                    self.shipTo().editing(true);
                }
                return self.shipTo().editing();
            },
            write: function(newValue) {
                if(!newValue && self.shipToValid()) {
                    self.shipTo().editing(false);
                } else {
                    self.shipTo().editing(true);
                }
            }
        });

        // self.EarliestShipDate.extend();
        // self.RequestedShipDate.extend({ min: self.EarliestShipDate });
        // self.RequestedShipDate.extend({ min: { params: self.EarliestShipDate, message: "The earliest ship date available is {0}" } });

        self.detailsMap = ko.observableArray([]);

        self.valid = ko.computed(function() {
            return self.shipToValid() && self.details().length > 0; // && self.RequestedShipDate.isValid();
        });

        self.itemsSelected = ko.computed(function() {
            return self.details().length > 0;
        });

        self.selectItemsComplete = function(shipment) {

            var shipmentArray = [];

            var lineMappings = shipment.detailsMap().map(function(map, index, array) {
                return {
                    OrderDetailId : map.detailLine.orderDetailKey(),
                    QtyToShip: map.qtyToShip()
                }
            });

            var shipmentInfo = {
                Key: shipment.key(),
                ShipmentId: shipment.key(),
                Details: []
            };
            shipmentInfo.Details = lineMappings;
            shipmentArray.push(shipmentInfo);

            var postOptions = {
                data: "shipmentJson=" + encodeURIComponent(JSON.stringify(shipmentArray)),
                error: function() {
                    alert('error saving item list on shipment');
                }
            }

            postInfo(postOptions, 'updateShipToAddress');

            $('#modal_add_prods[tabindex="-1"]:hidden').remove()
            $('#modal_add_prods').modal('hide');
        };

        self.selectedShipViaChoice.subscribe(function(newShipViaChoiceValue) {
            var shipViaChoiceKey = newShipViaChoiceValue.shipViaChoiceKey();
            var shipmentKey = self.key();

            var postOptions = {
                url: orderInfoPostUrl + '?ajax=true&pageaction=setShipmentShipVia&o_key=' + viewModel.orderKey()  + '&shipViaChoiceKey=' + shipViaChoiceKey + '&shipmentKey=' + shipmentKey,
                error: function() {
                    alert('error setting Shipping Method');
                }
            }

            postInfo(postOptions, 'setShipmentShipVia');

        });

        self.comments.subscribe(function(newComment) {

            var shipmentCommentPostData = {};

            var shipmentInfo = [{
                "os_key": self.key(),
                "comments": newComment
            }];

            shipmentCommentPostData =
            {
                "Tables": [
                    {
                        "TableName"           : "order_shipments",
                        "TableKeyField"       : "os_key",
                        "UserKeyField"        : "os_key",
                        "UserKeyIsPrimaryKey" : "True",
                        "Data"                : shipmentInfo
                    }
                ]
            };

            postLogicJsonAjax(shipmentCommentPostData);
        });

        self.requestedShipDate.subscribe(function(newRequestedShipDate) {

            if (!(newRequestedShipDate instanceof Date) || isNaN(newRequestedShipDate.valueOf())) {
                console.log('requestedShipDate is not a date');
                return;
            }

            var postOptions = {
                data: "shipmentId=" + encodeURIComponent(self.key()) + "&requestedShipDate=" + encodeURIComponent(moment(newRequestedShipDate).format('MM/DD/YYYY')),
                error: function() {
                    alert('error setting shipment requested ship date');
                }
            }

            postInfo(postOptions, 'setRequestedShipDate');
        });

        self.details().forEach(function(thisLine) {
            thisLine.hasChildProducts = ko.observable(function() {
                return self.details().filter(function(outerLine) {
                    return outerLine.orderDetail.instance() == thisLine.orderDetail.instance()
                            && thisLine.orderDetail.removeType() != 'instance'; // main product has removeType of instance
                }).length > 1;
            }());

            thisLine.orderDetail.hasParentInCart = ko.observable(function() {
                return self.details().filter(function(outerLine) {
                    return outerLine.orderDetail.orderDetailKey() != thisLine.orderDetail.orderDetailKey()
                            && outerLine.orderDetail.instance() == thisLine.orderDetail.instance()
                            && outerLine.orderDetail.removeType() == 'instance';
                }).length > 0;
            }());
        });

        if(ofConfig.useAccountDefaultShipVia && self.shipViaChoices && self.shipViaChoices().length > 0) {
            var filteredChoice = self.shipViaChoices().find(function(choice) {
                return choice.shipViaChoiceKey() == ofConfig.accountDefaultShipViaKey ||
                choice.shipViaRefID() == ofConfig.accountDefaultShipViaCode
            });

            if(filteredChoice) {
                self.shipViaChoices([ filteredChoice ]);
            }
        }

        self.shipTo.subscribe(function(shipTo) {
            if(viewModel && viewModel.setShipTo) {
                viewModel.setShipTo(self, shipTo);
            }
        })

        runHook('shipmentModelBottom', { self: self, shipmentData: shipmentData });
    }

    var Customer = function(customerData) {
        var self = this;

        ko.mapping.fromJS(customerData, {}, self);

        self.firstName.extend({ required: true });
        self.lastName.extend({ required: true });
        self.email.extend({ email: { message: 'A valid email address is required.', params: true }, required: { message: 'A valid email address is required.', params: true} });
        self.phone.extend({ required: true });
    }

    /*
    var Account = function(accountData) {
        var self = this;

        ko.mapping.fromJS(accountData, {
            create: function(options) {
                return new addressInfo(options.data);
            }
        }, self);

        if(!self.Country()) {
            self.Country('USA');
        }

        self.Company.extend({ required: true });
        self.Address1.extend({ required: true });
        self.Country.extend({ required: true });
        self.City.extend({ required: true });
        if(!self.State()) {
            self.State("");
        }
        self.State.extend({ required: true });
        self.ZipCode.extend({ required: true });
    }
    */

    var Order = function(orderData) {
        addTimer("start order mapping");
        var self = this;

        self.activeAjaxRequestCount = ko.observable(0);

        ko.mapping.fromJS(orderData, {
            detailLines : {
                create: function(options) {
                    options.data.configuratorJson = options.data.configuratorJson ? JSON.parse(options.data.configuratorJson) : {};
                    options.data.parentProductID  = options.data.parentProductID.trim() || '';
                    options.data.editing = false;
                    options.data.uomConversion = !options.data.uomConversion ? 1 : options.data.uomConversion;
                    options.data.minQty = oConfig.convertMinQtyFromStdToSalesUom(options.data.minQty, options.data.uomConversion);
                    options.data.maxQty = oConfig.convertMaxQtyFromStdToSalesUom(options.data.maxQty, options.data.uomConversion);
                    options.data.qtyIncrement = (options.data.uomConversion == 0 || options.data.uomConversion === 1) ? (options.data.qtyIncrement || oConfig.defaultQuantityIncrement) : 0;
                    return ko.mapping.fromJS(options.data);
                }
            },
            orderActions : {
                create: function(options) {
                    if(options.data) {
                        if(!options.data.hasOwnProperty('showPaymentMethods')) {
                            options.data.showPaymentMethods = true;
                        }
                    }
                    return ko.mapping.fromJS(options.data);
                }
            },
            shipments : {
                create: function(options) {
                    return new Shipment(options.data);
                }
            },
            customer : {
                create: function(options) {
                    return options.data ? new Customer(options.data) : {};
                }
            },
            account : {
                create: function(options) {
                    return new addressInfo(options.data);
                }
            },
            paymentMethod : {
                create: function(options) {
                    if(options.data == null || options.data == '') {
                        return ko.mapping.fromJS({
                            paymentMethodKey: "",
                            paymentType: "",
                        });
                    } else {
                        options.data.paymentType = (options.data.paymentType || "").toLowerCase();
                        return ko.mapping.fromJS(options.data);
                    }
                }
            },
            vaultedPayment : {
                create: function(options) {
                    if(options.data == null) {
                        return ko.mapping.fromJS({
                            key: "",
                            vaultKey: "",
                            nickName: "",
                            nameOnCard: "",
                            cardType: "",
                            expMonth: 0,
                            expYear: 0,
                            first2: "0",
                            last4: "0",
                            address1: "",
                            address2: "",
                            city: "",
                            state: "",
                            zip: "",
                            country: "",
                            status: true,
                            merchantId: "",
                            customerId: "",
                            accountId: "",
                            gatewayInfoDirty: false,
                            accountLast4: "",
                            achAccountType: "",
                            bankName: "",
                            routingLast4: "",
                            type: "",
                            customerProfileId: ""
                        });
                    } else {
                        return ko.mapping.fromJS(options.data);
                    }
                }
            },
            roRunDate : {
                update: function(options) {
                    //Quick fix to hide invalid expiration.
                    //ToDo: update to check for other "invalid" dates.
                    date = new Date(options.data);

                    var isValid = date instanceof Date && !isNaN(date);
                    if(moment(date).format("MM/DD/YYYY") == '01/01/0001' || moment(date).format("MM/DD/YYYY") == '01/01/2001'){
                        isValid = false;
                    }

                    if(!isValid) {
                        return moment().format("MM/DD/YYYY");
                    } else {
                        return moment(date).format("MM/DD/YYYY");
                    }
                }
            },
            roRecCase : {
                update: function(options) {
                    if(!options.data) {
                        return 'month';
                    } else {
                        return options.data;
                    }
                }
            },
            roRecQty : {
                update: function(options) {
                    if(!options.data || options.data == 0) {
                        return 1;
                    } else {
                        return options.data;
                    }
                }
            },
            'ignore': ["errorMessages"]
        }, self);

        self.showCredits = ko.observable(false);
        self.allowQtyControls = ko.observable(false);
        self.disablePlaceOrder = ko.observable(false);

        self.detailLines().forEach(function(detailLine) {
            // We can't debounce/trottle an observable
            // so we add a computed to track qty changes.
            // We use this in a subscription to call
            // update cart to update qty change in bulk atc
            // to avoid loading the qty change without posting
            // and updating the entire order model (for perf)
            detailLine.qtyTracker = ko.computed(function() {
                return detailLine.qty();
            }).extend({throttle: 500});
            detailLine.qtyTracker.subscribe(function (newQty) {
                self.postQtyUpdate();
            }, detailLine);
            
            detailLine.workerPriceTracker = ko.computed(function() {
                return detailLine.price();
            }).extend({throttle: 500});
            detailLine.workerPriceTracker.subscribe(function (newQty) {
                self.setWorkerPriceOverride(detailLine);
            }, detailLine);

            detailLine.workerQtyLimitsTracker = ko.computed(function() {
                return {
                    removeType: detailLine.removeType(),
                    minQty: detailLine.minQty(),
                    maxQty: detailLine.maxQty(),
                    qtyIncrement: detailLine.qtyIncrement()
                };
            }).extend({throttle: 500});
            detailLine.workerQtyLimitsTracker.subscribe(function (newValue) {
                self.setWorkerQtyLimits(detailLine);
            });

            detailLine.instanceChildren = self.detailLines().filter(function(item) {
                                            return item.instance() == detailLine.instance() && item != detailLine;
                                        }) || [];

            var mainProduct = self.detailLines().filter(function(item) {
                                            return item.instance() == detailLine.instance() && item.removeType() == 'instance';
                                        })[0] || detailLine;

            var mainProductQty = mainProduct.qty();

            detailLine.instanceUnitPrice = ko.observable(self.detailLines().reduce(function(current, item) {
                if(!ofConfig.showProductDiscount){
                    item.priceBeforeAdjustment(item.price());
                }
                if(item.instance() == detailLine.instance()) {
                    if(item.parentProductID().trim() != '' && item.removeType() != 'instance') {
                        return current += ((item.priceBeforeAdjustment() || item.price()) * item.qty()) / mainProductQty;
                    } else {
                        return current += (item.priceBeforeAdjustment() || item.price());
                    }
                } else {
                    return current;
                }
            },0));

            detailLine.instanceUnitPriceDisplay = ko.computed(function(){
                return utils.formatMoney(detailLine.instanceUnitPrice(), utils.decimalPlacesOnUnitPrices);
            });

            detailLine.instanceExtPrice = ko.observable(self.detailLines().reduce(function(current, item) {
                if(item.instance() == detailLine.instance()) {
                    return current += item.extTotal();
                } else {
                    return current;
                }
            },0));

            detailLine.hasQuantityRestrictions = ko.computed(function () {
                return !!(
                            (detailLine.minQty() && detailLine.minQty() !== oConfig.defaultMinimumQuantity) || 
                            ((!detailLine.uomConversion() || detailLine.uomConversion() == 1) && detailLine.qtyIncrement() && detailLine.qtyIncrement() !== oConfig.defaultMinimumQuantity) || 
                            detailLine.maxQty()
                        );
            });

			detailLine.quantityRestrictionsHtml = ko.computed(function () {
				var lines = [];

				if (detailLine.minQty() && detailLine.minQty() != oConfig.defaultMinimumQuantity)
					lines.push(oConfig.labels.minQtyPopover.replace(/<min_qty>/,detailLine.minQty()));

                if ( (!detailLine.uomConversion() || detailLine.uomConversion() == 1) && detailLine.qtyIncrement() && detailLine.qtyIncrement() != oConfig.defaultQuantityIncrement)
                    lines.push('Qty Increment: ' + detailLine.qtyIncrement());

				if (detailLine.maxQty())
					lines.push('Maximum Qty: ' + detailLine.maxQty());

				return lines.join('<br>');
			});
        });

        self.needToChoosePromos = ko.observable();

        self.paymentMethodSelected = ko.observable(function() {
                return typeof self.paymentMethod.paymentType == 'function';
            }()
        );

        self.termsAgreement = ko.observable(function() {
                return self.legalResponse() == 'agreed';
            }()
        );

        self.detailLines.sort(detailLineInstanceSort);

        self.shipments.sort(function(left, right) {
            return left.position() == right.position() ? 0 : (left.position() < right.position() ? -1 : 1);
        });

        self.processing = ko.observable(false);
        self.populatingShippingAddresses = ko.observable(false);
        self.availableShippingAddresses = ko.observableArray([]);
        self.newCouponCode = ko.observable("");
        self.newGiftCertificate = ko.observable("");
        if(!self.errorMessages) {
            self.errorMessages = ko.observableArray();
        };
        // self.orderPlaced = ko.observable(false);

        self.orderPlaced = ko.computed(function() {
            return self.completed()
        });

        self.Customer = ko.validatedObservable(self.Customer);
        self.Account = ko.validatedObservable(self.Account);

        self.shippingAddresses = shippingAddresses;

        self.addressTypes = function(){
            var addressTypes = [];
            addressTypes.push({
                label: ofConfig.addressBookLabel,
                global: '0'
            });
            if(ofConfig.useLocalPickup){
                addressTypes.push({
                    label: ofConfig.localPickupLabel,
                    global: '1'
                })
            }
            return addressTypes;
        };

        self.defaultShaKey = function() {
            sShaKey = sDefaultShaKey;
            if (sShaKey == ''){
                sShaKey = self.shippingAddresses()[0].sha_key;
            }
            return sShaKey;
        }
        self.billingSectionValid = ko.computed(function () {
            return self.Customer.isValid() && self.Account.isValid();
        });

        self.toggleShowBillingEdit = function() {
            if(self.showBillingEdit()) { // && newCustomer) {
                var postData = new CreateCustomerPost(self);
                self.postBillingUpdate(postData);
            } else {
                self.showBillingEdit(!self.showBillingEdit());
            }
        };

        self.postBillingUpdate = function(postData) {

            var postUrl = orderInfoPostUrl + '?o_key=' + viewModel.orderKey() + '&origin=bill-ship';
            var dataString;
            var keyCount = 0;

            $.each(postData, function(key, value) {
                keyCount += 1;

                if(keyCount > 1) {
                    dataString += "&"
                }
                dataString += key + "=" + encodeURIComponent(value);
            });

            var postOptions = {
                url: postUrl,
                type: "POST",
                data: dataString,
                success: function(data) {
                    self.showBillingEdit(!self.showBillingEdit());
                },
                error: function() {
                    alert('error posting billing update');
                }
            }

            postInfo(postOptions, '', 'checkout_shipping');
        }

        self.editingBilling = ko.observable(false);
        self.showBillingEdit = ko.computed({
            read: function() {
                if(!self.billingSectionValid()) {
                    self.editingBilling(true);
                }
                return self.editingBilling(); // && !self.billingSectionValid();
            },
            write: function(newValue) {
                if(!newValue && self.billingSectionValid()) {
                    self.editingBilling(false);
                } else {
                    self.editingBilling(true);
                }
            }
        });

        self.moveAllItemsToShipment = function(shipment) {
            self.processing(true);

            var shipmentArray = [];

            var lineMappings = self.detailLines().map(function(map, index, array) {
                return {
                    OrderDetailId : map.orderDetailKey(),
                    QtyToShip: map.qty()
                }
            });

            var shipmentInfo = {
                Key: shipment.key(),
                ShipmentId: shipment.key(),
                Details: []
            };

            self.shipments().forEach(function(otherShipment) {
                if(otherShipment.key() != shipment.key()) {
                    shipmentArray.push({
                        key: otherShipment.key(),
                        ShipmentId: otherShipment.key(),
                        Delete: true
                    });
                }
            });

            shipmentInfo.Details = lineMappings;
            shipmentArray.push(shipmentInfo);

            var postOptions = {
                data: "shipmentJson=" + encodeURIComponent(JSON.stringify(shipmentArray)),
                error: function() {
                    alert('error moving all products to shipment');
                },
                complete: function() {
                    self.processing(false);
                }
            }

            postInfo(postOptions, 'updateShipToAddress');

            $('#modal_add_prods[tabindex="-1"]:hidden').remove()
            $('#modal_add_prods').modal('hide');
        };

        self.isOverLineLimit = ko.observable(checkLineLimit(self));

        function checkLineLimit(vm){
            //use the alternate ui (showcart.asp) when over the line limit threshold
            // Don't allow this to happen if using user create shipments since the showcart page doesn't support
            // Split shipping.
            if(oConfig.allowUserCreatedShipments){
                return false;
            }else if (ofConfig.useTooManyLinesRestriction && vm.detailLines().length >= parseInt(ofConfig.tooManyLinesQuantity)){
                return true;
            }else{
                return false;
            }
        }

        self.isMinimumOrderTotalMet = ko.observable(checkOrderTotal(self));
        //moved definition to global scope

        self.shippingComplete = ko.observable(isShippingComplete());

        function isShippingComplete(){
            if ( self.completed() || self.lifecycleStage() == 'cancelled' || self.lifecycleStage() == 'punchout_punched_in') {
                return true;
            }

            // force step 2 if there are no lines on the order.
            if (self.detailLines().length == 0){
                return false;
            }

            // force step 3 if the lines exceed the limit.
            if (self.isOverLineLimit()){
                return true;
            }

            // force step 2 if any shipments are missing an address.
            _.each(self.shipments(),function(shipment){
                if(!shipment.shipTo().key()){
                    return false;
                }
            });

            if(!self.isMinimumOrderTotalMet()){
                utils.setCookie(self.orderKey() + '-step', '2');
            }

            if(utils.getCookie(self.orderKey()+'-step') == undefined){
                if(ofConfig.defaultShippingSectionOpen){
                    utils.setCookie(self.orderKey() + '-step', '2');
                }else{
                    utils.setCookie(self.orderKey() + '-step', '3');
                }
            }

            if (utils.getCookie(self.orderKey() + '-step') == '2'){
                return false;
            }else{
                return true;
            }
        };

        self.shipmentsComplete = ko.observable(false || self.completed());

        self.shipmentsSectionsValid = ko.computed(function() {
            var result = true;
            self.shipments().forEach(function(shipment) {
                if(!shipment.valid() || (shipment.shipTo && shipment.shipTo().editing())) {
                    result = false;
                }
            });
            return result;
        });

        self.toggleShowShippingEdit = function() {
            self.showShippingEdit(!self.showShippingEdit());
            if(!self.showShippingEdit()) {
                scrollToSection('#checkout_summary');
            }
        };

        self.moveItemToNewShipTo = function(item){
            self.itemShipToMap().push({
                orderDetailKey: item.orderDetailKey,
                instance: ko.observable(item.instance()),
                key: ko.observable(item.orderDetailKey),
                qty: ko.observable(item.qtyIncrement() || 1),
                qtyControlledFrom: ko.observable(item.qtyControlledFrom()),
                minQty: ko.observable(item.minQty()),
                qtyIncrement: ko.observable(item.qtyIncrement()),
                getPrice: item.getPrice,
                suPrice: item.suPrice,
                shaKey: ko.observable(item.shaKey()),
                shipTo: ko.observable({}),
                moveItemToNewShipTo: self.moveItemToNewShipTo,
                removeItemToShipToMap: self.removeItemToShipToMap,
                availableAddresses: shippingAddresses,
                parentProductID: item.parentProductID
            });
            item.qty(item.qty() - (item.qtyIncrement() || 1));
            self.itemShipToMap.notifySubscribers();
        };

        self.currentPackage = ko.observable(function(){
            var item = new Object();
            item.shippingAddressId = ko.observable(sDefaultShaKey);
            item.shipTo = ko.observable()
            item.details = ko.observableArray([]);
            return item;
        }());


        self.toggleItemInCurrentPackage = function(item){
            if(!_.contains(self.currentPackage().details(),item)){
                self.currentPackage().details().push(item)
            }else{
                _.pull(self.currentPackage().details(), item)
            }
            self.currentPackage.notifySubscribers();
        };

        self.isInCurrentPackage = function(item){
            return _.contains(self.currentPackage().details(),item);
        };

        self.removeItemToShipToMap = function(item){
            _.pull(self.itemShipToMap(),item);
            self.itemShipToMap.notifySubscribers();
        };

        self.getTotalQty = function(instance){
            var totalQty = 0;
            var mappings = self.itemsOnOrder();
            if(self.useMultiShipEditUI && self.useMultiShipEditUI()){
                mappings = self.itemShipToMap();
            }
            _.each(mappings, function(map){
                if(map.instance() == instance  && map.qtyControlledFrom() != 1){
                    totalQty += parseFloat(map.qty());
                    //exit after the first item is found so the carrier product qty will display.
                    //or not - added && map.qtyControlledFrom() != 1 to account for config products
                    // qtyControlledFrom is "1" when controlled by the parent
                    //return false;
                }
            });
            return totalQty;
        };

        self.getOrderDetailQty = function(instance){
            var totalQty = 0;
            self.itemsOnOrder().forEach(function(map){
                if(map.instance() == instance && map.qtyControlledFrom() != 1){
                    totalQty += parseFloat(map.qty());
                }
            });
            return totalQty;
        };

        self.setItemShipToMapPrice = function(data){
            self.itemShipToMap().forEach(function(map){
                if(map.instance() == data.instance()){
                    if(self.superUserRestrictMinPrice() & data.superUserMinPrice() != null && data.price() < data.superUserMinPrice()) {
                        data.price(undefined);
                        data.price(data.superUserMinPrice());
                    }
                    map.suPrice = data.price();
                }
            });
        };

        self.setWorkerPriceOverride = function(data){
            var orderDetail = [];

            orderDetail.push({
                orderDetailKey: data.orderDetailKey(),
                PriceCalculationType: 'fixed',
                Price: data.price(),
                PriceBeforeAdjustment: data.price()
            });

            var postOptions = {
                data: "orderDetail=" + encodeURIComponent(JSON.stringify(orderDetail)),
                success: function(data) {
                    $('#cartAdvSettings.collapse').toggleClass('in'); 
                    $('#onOrderAdvBtn').toggleClass('active');
                },
                error: function() {
                    alert('Error saving Unit Price');
                }
            }

            postInfo(postOptions, 'setOrderDetailFields');
        }

        self.setWorkerQtyLimits = function(data) {
            var orderDetail = [];

            orderDetail.push({
                orderDetailKey: data.orderDetailKey(),
                minQty: data.minQty() * data.uomConversion(),
                maxQty: data.maxQty() * data.uomConversion(),
                qtyIncrement: data.qtyIncrement(),
                removeType: data.removeType()
            });

            var postOptions = {
                data: "orderDetail=" + encodeURIComponent(JSON.stringify(orderDetail)),
                success: function(data) {
                    $('#cartAdvSettings.collapse').toggleClass('in');
                    $('#onOrderAdvBtn').toggleClass('active');
                    toggleLoadingWidget(false);
                },
                error: function() {
                    alert('Error saving qty limits');
                }
            }
            toggleLoadingWidget(true);
            postInfo(postOptions, 'setOrderDetailFields');
        }

        self.resetSuperUserPriceOverride = function(data){
            var orderDetail = [];

           orderDetail.push({
                orderDetailKey: data.orderDetailKey(),
                PriceCalculationType: 'std'
            });

            var postOptions = {
                data: "orderDetail=" + encodeURIComponent(JSON.stringify(orderDetail)),
                success: function(data) {
                },
                error: function() {
                    alert('Error resetting Price Override');
                }
            }

            postInfo(postOptions, 'setOrderDetailFields');
        };

        self.resetSuperUserShippingPrice = function(data){
            var postOptions = {
                url: "payment.asp?o_key=" + viewModel.orderKey() + "&svc_key=" + data.shipViaChoiceKey() + '&ajax=true&pageaction=resetShippingPrice&randomnum=' + new Date().getTime() ,
                success: function(data) {
                },
                error: function() {
                    alert('error resetting shipping price');
                }
            }

            postInfo(postOptions, 'resetSuperUserShippingPrice');
        }

        self.returnToPendingOrders = function() {
            window.location = 'quotes.asp?revert=1';
        }

        self.superUserOrderFormMode = ko.observable(ofConfig.superUserOrderFormMode && ofConfig.isSuperUserSession);

        self.getOrderLabel = function(){
            switch(self.recordType()){
                case 'future-order':
                    orderLabel = 'Future Order';
                    break;
                case 'recurring-order':
                    orderLabel = 'Recurring Order';
                    break;
                case 'saved-cart':
                    orderLabel = ofConfig.SavedCartLabel;
                    break;
                default:
                    if(self.superUserOrderFormMode()){
                        orderLabel = ofConfig.SavedCartLabel;
                    }else{
                        orderLabel = 'Order';
                    }
                    break;
            }
            //TODO: once we confirm this logic, return the calculated label instead of the Option.
            //return orderLabel;
            return ofConfig.SavedCartLabel;
        };

        self.superUserSetShipViaPrice = function(data, event){
            var postOptions = {
                url: 'payment.asp' + '?o_key=' + viewModel.orderKey() + '&svc_key=' + data.shipViaChoiceKey() + '&price=' + event.target.value + '&ajax=true&pageaction=setShippingPrice&randomnum=' + new Date().getTime(),
                success: function(data) {
                },
                error: function() {
                    alert('error resetting shipping price');
                }
            };
            postInfo(postOptions, 'setShippingPrice');
        };

        self.postQtyUpdate = function() {

            var postUrl = 'i_i_add_to_cart.asp' + '?ajax=qtyUpdate&modal=1&o_key=' + viewModel.orderKey();
            var dataString = '';
            var unqString = '';
            var keyCount = 0;

            self.detailLines().forEach(function(detailLine) {
                if(dataString.length) dataString += '&';
                dataString += 'qty_' + detailLine.orderDetailKey() + '=' + detailLine.qty();
                if(unqString.length) unqString +=',';
                unqString += detailLine.orderDetailKey();
            });

            dataString += '&unqs=' + unqString;

            var postOptions = {
                url: postUrl,
                type: "POST",
                data: dataString,
                success: function(data) {
                    //
                },
                error: function() {
                    alert('error updating quantity');
                }
            }

            postInfo(postOptions, '','');
        }

        self.hasMultipleShipToAddresses = function() {
            var shaKeys = [];
            var hasMultipleAddresses = false;
            self.shipments().forEach(function(shipment) {
                var shaKey = shipment.shipTo().key();
                if(shaKeys.indexOf(shaKey)) {
                    shaKeys.push(shaKey);
                }
                if(shaKeys.length > 1) {
                    hasMultipleAddresses = true;
                    return;
                }
            });
            return hasMultipleAddresses;
        };

        self.useMultiShipEditUI = ko.observable(self.hasMultipleShipToAddresses());

        self.allocateShipments = function(moveToNextStep){
            var shipmentArray = [];
            var listOfShaKeys = [];
            var newShipmentKey;
            var orderDetail = [];

            toggleLoadingWidget(true);

            if(viewModel.useMultiShipEditUI()){

                //Get a unique list of shipping address keys
                self.itemShipToMap().forEach(function(item){
                    if ( listOfShaKeys.indexOf( item.shipTo().key() ) == -1 ){
                        listOfShaKeys.push(item.shipTo().key());
                    }
                })

                //Loop the sha_keys to create shipments for each
                var items = self.itemShipToMap();
                var newShipmentCount = 0;
                listOfShaKeys.forEach(function(shaKey){
                    //reuse existing shipment keys if they exist.
                    if( newShipmentCount+1 <= self.shipments().length ){
                        newShipmentKey = self.shipments()[newShipmentCount].key();
                    }else{
                        newShipmentKey = utils.createGuid();
                    }

                    var shipmentInfo = {
                        Key: newShipmentKey,
                        ShipmentId: newShipmentKey,
                        ShippingAddressId: shaKey,
                        Details: []
                    };

                    //Loop the product - ship to mapping records to create shipment details
                    var shipments = items;
                    shipments.forEach(function(shipment){
                        if(shipment.shipTo().key() == shaKey){
                            shipmentInfo.Details.push( {
                                OrderDetailId: shipment.orderDetailKey,
                                QtyToShip: shipment.qty()
                            })
                        }
                    });
                    newShipmentCount++;
                    shipmentArray.push(shipmentInfo);
                });
            }else{
                listOfShaKeys.push(self.shipments()[0].shipTo().key());

                //Single Shipping - reuse the first shipment key
                newShipmentKey = self.shipments()[0].key();
                var shipmentInfo = {
                    Key: newShipmentKey,
                    ShipmentId: newShipmentKey,
                    ShippingAddressId: self.shipments()[0].shipTo().key(),
                    Details: []
                };

                //JB - loop the items on order since we're in single shipment mode.
                viewModel.itemsOnOrder().forEach(function(item){
                    shipmentInfo.Details.push( {
                        OrderDetailId:  item.orderDetailKey(),
                        QtyToShip: item.qty()
                    });
                });
                shipmentArray.push(shipmentInfo);
            }

            //Remove any orphaned shipments from the original data if the user is created shipments
            if(listOfShaKeys.length < self.shipments().length && oConfig.allowUserCreatedShipments){
                var count = 0;
                _.eachRight(self.shipments(), function(oldShipment){
                    if( count + 1 <= (self.shipments().length - listOfShaKeys.length ) ){
                        shipmentArray.push({
                            Key: oldShipment.key(),
                            Delete: true
                        });
                        count++;
                    }
                });
            }

            //Order Detail Field Updates
            var details = self.itemShipToMap()
            _.each(details, function(item){
                var superUserPrice = parseFloat(item.suPrice);
                var getprice = parseFloat(item.getPrice());

                if( isNaN(superUserPrice)){
                    superUserPrice = getprice;
                }

                if(viewModel.superUserOrderFormMode()) {
                    if (getprice != superUserPrice) {
                        //build OD object
                        if(!_.find(orderDetail, {orderDetailKey: item.orderDetailKey}) ){
                            var obj = {
                                orderDetailKey: item.orderDetailKey,
                                Price: superUserPrice,
                                PriceDisplay: getprice,
                                PriceCalculationType: 'fixed'
                            };

                            if(ofConfig.useTradeAdjustmentOnSession){
                                obj.PriceBeforeAdjustment = superUserPrice;
                            }
                            orderDetail.push(obj);
                        }
                    }

                    var detail = _.find(orderDetail, {orderDetailKey: item.orderDetailKey});

                    if(!detail) {
                        detail = {
                            orderDetailKey: item.orderDetailKey
                        }
                        orderDetail.push(detail);
                    }

                    // var newValues = _.find(viewModel.itemsOnOrder(), {orderDetailKey: item.orderDetailKey});

                    var itemOnOrder = viewModel.itemsOnOrder().reduce(function(returnItem, currentItem) {
                        if(currentItem.orderDetailKey() == item.orderDetailKey) {
                            returnItem = currentItem;
                        }
                        return returnItem;
                    })

                    detail.minQty        = parseFloat(itemOnOrder.minQty ? itemOnOrder.minQty() : 0);
                    detail.maxQty        = parseFloat(itemOnOrder.maxQty ? itemOnOrder.maxQty() : 0);
                    detail.qtyIncrement  = parseFloat(itemOnOrder.qtyIncrement ? itemOnOrder.qtyIncrement() : oConfig.defaultQuantityIncrement);

                    // TODO: Need to track original removeType value - checking has children for now
                    var defaultRemoveType = itemOnOrder.hasChildProducts() ? 'instance' : 'OD_Key';

                    detail.removeType   = itemOnOrder.removeType ? itemOnOrder.removeType() : defaultRemoveType;

                    runHook('allocateShipmentsOrderDetailUpdate', { itemOnOrder: itemOnOrder, detailLine: detail });
                }
                toggleLoadingWidget(false);
            });

            var postOptions = {
                data: "shipmentJson=" + encodeURIComponent(JSON.stringify(shipmentArray)) + "&orderDetail=" + encodeURIComponent(JSON.stringify(orderDetail)),
                success: function(){
                    if(ofConfig.bUsePromotions){
                        jQuery.ajax({
                            url: 'payment.asp' + '?o_key=' + viewModel.orderKey() + '&ajax=true&pageaction=checkPromos&randomnum=' + new Date().getTime()
                            , cache: false
                            , type: 'GET'
                            , success: function(data,status,request){
                                if(JSON.parse(data)){
                                    viewModel.processing(true);

                                    //redirect to reward selection page.
                                    //This is not opening in a modal for responsive reasons.
                                    var nextPage = encodeURIComponent('payment.asp?o_key=' + viewModel.orderKey() + (ofConfig.isModal ? '&modal=1' : ''));
                                    document.location = 'promo_reward_selection.asp?quickadd=1&next_page=' + nextPage + '&o_key=' + viewModel.orderKey() + (ofConfig.isModal ? '&modal=1' : '');
                                }
                            }
                            , error: function(data) {
                                console.log('Error checking Promos');
                            }
                            , complete: function(data) {
                            }
                        });
                    }
                },
                error: function() {
                    self.addShipment();
                    autoAllocateItems();
                    alert('error saving product list on shipment');
                }
            }

            postInfo(postOptions, 'updateShipToAddress');
            if(moveToNextStep === undefined) {
                moveToNextStep=true;
            }
            if(self.isOverLineLimit()){
                moveToNextStep = true;
            }
            console.log(self);
            self.shippingComplete(moveToNextStep);
            self.shippingComplete.notifySubscribers();
            if(moveToNextStep) {
                utils.setCookie(self.orderKey() + '-step' , '3');
            }
        };


        self.showShippingEdit = ko.observable(!self.completed());
        self.currentShipment = ko.observable();

        self.toggleMultiShip = function (data, event) {
            event.preventDefault();
            self.useMultiShipEditUI(!self.useMultiShipEditUI());

            self.shippingComplete.notifySubscribers();
        };

        self.toggleOrderPlaced = function (data, event) {
            event.preventDefault();

            var postData = {};

            if(viewModel.paymentMethod.paymentType() === 'cc') {

                var defaultCcnChecked = $('#ccpm_default_ccn_id_selection').is(':checked');

                if(defaultCcnChecked) {
                    postData.setDefaultCcn = 1
                }

                postData.ccn_key =  $('#ccpm_ccn_id').val();
                postData.address =  $('#ccpm_address_input').val();
                postData.city =     $('#ccpm_city_input').val();
                postData.country =  $('#ccpm_country_input').val();
                postData.state =    $('#ccpm_state_dropdown').prop('disabled') ? $('#ccpm_state_text').val() : $('#ccpm_state_dropdown').val();
                postData.zip =      $('#ccpm_zip_input').val();

                //oSavedPaymentMethodsCreditCard.saveBillingAddress();
            }

            var postOptions = {
                data: postData,
                success: function(data){
                    viewModel.updateOrderAccess();
                    var response = JSON.parse(data);
                    if(Object.keys(response[0].Errors).length == 0) {
                        //document.location = 'payment.asp?o_key=' + viewModel.orderKey() + '#checkout_confirmation';
                        scrollToSection('#checkout');
                    }else{
                        scrollToSection('#checkout');
                    }
                },
                error: function(data) {
                    alert('error placing order');
                }
            }


            if ($.active > 0) {
                viewModel.processing(true);
                $( document ).one("ajaxStop", function(){
                    postInfo(postOptions, 'placeOrder');
                });
            }
            else {
                postInfo(postOptions, 'placeOrder');
            }

        };

        self.showItemSelector = function(shipment) {
            shipment.detailsMap([]);
            self.detailLines().forEach(function(detailLine, detailIndex, detailArray) {
                shipment.detailsMap().push(new detailMap(detailLine, shipment, self));
            });

            self.currentShipment(shipment);
            $('#modal_add_prods').modal('show');
        };

        self.addShipment = function() {

            var newShipment = getNewShipment();

            var successFunction;

            if(self.shipments().length === 0) {
                /* EJ - 2016-11-15
                   I feel dirty about this, but it's a quick workaround for an odd issue
                   happening on first load of the payment page for a new order, before the first
                   shipment is added.  The issue presents as a "corrupt" countries array (missing
                   states for first country) and validation issues on Account form.

                   The only difference in code on client (verified with Arraxis Merge) is the orderPayload.
                   The first page load doesn't have shipments or paymethods (empty array and null respectively),
                   whereas the second and all subsequent page loads do.

                   The issue goes away once the first shipment is added, so I'm forcing a reload
                   the first time a shipment is added, which was already happening already
                   on first page load if there were no shipments on the order.

                   The first shipment being added needs to be moved to the ordering object eventually,
                   which will negate the need for this code anyway, so I didn't spend more hours trying
                   to find the root cause.
                */

                // so spinner stays active until location.reload happens
                self.activeAjaxRequestCount(self.activeAjaxRequestCount() + 1);

                successFunction = function() {
                    location.reload();
                }
            } else {
                successFunction = autoAllocateItems;
            }


            var shipmentArray = [];

            var shipmentInfo = {
                Key: newShipment.key(),
                ShipmentId: newShipment.key(),
                Details: []
            };

            shipmentArray.push(shipmentInfo);


            var postOptions = {
                data: "shipmentJson=" + encodeURIComponent(JSON.stringify(shipmentArray)),
                error: function() {
                    alert('error setting Shipping Method');
                },
                success: successFunction
            }

            postInfo(postOptions, 'updateShipToAddress', newShipment.key(), self);

        };

        self.deleteShipment = function(shipmentToDelete, e, index) {

            var shipmentArray = [];

            var shipmentInfo = {
                Key: shipmentToDelete.key(),
                Delete: true
            };

            shipmentArray.push(shipmentInfo);

            var postOptions = {
                data: "shipmentJson=" + encodeURIComponent(JSON.stringify(shipmentArray)),
                error: function() {
                    alert('error moving all items to shipment');
                },
                success: autoAllocateItems
            }

            postInfo(postOptions, 'updateShipToAddress', 'checkout_shipping');
        };
        self.setShipTo = function(shipment, shipToAddress) {

            var sha_id = shipToAddress.key();
            var shipmentId = shipment.key();

            var postOptions = {
                data: "shipmentId=" + shipmentId + "&sha_id=" + sha_id,
                success: function(data) {
                    autoAllocateItems();
                    self.shipmentsSectionsValid.notifySubscribers();
                },
                error: function() {
                    alert('error saving shipping address');
                }
            }

            postInfo(postOptions, 'setSha');

            $('#modal_addressbook[tabindex="-1"]:hidden').remove()
            $('#modal_addressbook').modal('hide');
        };

        self.showNewAddressForm = function(shipment) {
            shipment.shipTo(new addressInfo(getNewAddress()));

            $('#modal_addressbook[tabindex="-1"]:hidden').remove()
            $('#modal_addressbook').modal('hide');
        };

        self.showAddAddress = function(shipment){

            $('#modal_add_shipto').modal('show');
        };

        self.shipments().forEach(function(shipment) {

            self.detailLines().forEach(function(detailLine, detailIndex, detailArray) {
                shipment.detailsMap().push(new detailMap(detailLine, shipment, self));
            });
        });

        self.updatePoRequired = function() {
            if(self.paymentMethod && self.paymentMethod.paymentMethodKey() != ''){
            var isReallyRequired = self.paymentMethod.poRequired() && self.paymentMethod.collectPO();
            self.paymentMethod.poRequired(isReallyRequired);
        }
        }

        self.updatePoRequired();

        self.poNumber.extend({
            required: {
                onlyIf: function() {
                    return viewModel && viewModel.paymentMethod.poRequired()
                }
            }
        });

        self.poNumber.subscribe(function(poNumber) {
            postOrderHeaderField("ponumber", poNumber);
        });

        self.comments.subscribe(function(newComments) {
            postOrderHeaderField("comment", newComments);
        });

        self.nickname.subscribe(function(nickname) {
            postOrderHeaderField("nickname", nickname);
        });

        self.orderCcEmail.subscribe(function(orderCcEmail) {
            postOrderHeaderField("order_cc_email", orderCcEmail);
        });

        self.isValidDate = function(date){
            //Quick fix to hide invalid expiration.
            //ToDo: update to check for other "invalid" dates.

            // Parse the date parts to integers
            var parts = date.split("-");
            var day = parseInt(parts[2], 10);
            var month = parseInt(parts[1], 10);
            var year = parseInt(parts[0], 10);

            // Check the ranges of month and year
            if(year < 1000 || year > 3000 || month == 0 || month > 12)
                return false;

            var monthLength = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];

            // Adjust for leap years
            if(year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
                monthLength[1] = 29;

            // Check the range of the day
            return day > 0 && day <= monthLength[month - 1];
        };

        self.getMinimumRecurringOrderDate = function(){
            var today = moment();
            var minimumDate = moment(today).add(1, 'days');
            return moment(minimumDate).format("MM/DD/YYYY");
        }

        self.roRunDate.subscribe(function(roRunDate) {
            postOrderHeaderField("ro_rundate", roRunDate);
        });

        self.roRecCase.subscribe(function(roRecCase) {
            postOrderHeaderField("ro_rec_case", roRecCase);
        });

        self.roRecQty.subscribe(function(roRecQty) {
            postOrderHeaderField("ro_rec_qty", roRecQty);
        });

        self.updateRecurringType = function(data, event){
            newValue = event.target.value;
            sOrderDetailKey = data.orderDetailKey();
            switch(newValue){
                case 'fixed':
                    postOrderDetailFields(sOrderDetailKey, ['ro_recurring_type','ro_skip_next_runs'], ['fixed',0]);
                    break;
                case 'recurring':
                    postOrderDetailFields(sOrderDetailKey, ['ro_recurring_type','ro_skip_next_runs'], ['recurring',0]);
                    break;
                case 'skipnext':
                    postOrderDetailFields(sOrderDetailKey, ['ro_recurring_type','ro_skip_next_runs'], ['recurring',1]);
                    break;
                case 'remove':
                    viewModel.removeProduct(data);
                    break;
            }

        };

        self.getRecurringLabel = function(data){
            var sMessage = '';
            var sAction = data.orderDetail.roRecurringType();
            var sSkipRuns = data.orderDetail.roSkipNextRuns();
            switch(sAction){
                case 'recurring':
                case '':
                    if(sSkipRuns == 0){
                        //recurring
                        sMessage = ofConfig.roRecurringItemLabel;
                    }else{
                        //skip.
                        sMessage = ofConfig.roSkippedItemLabel;
                    }
                    break;
                case 'fixed':
                        sMessage = ofConfig.roOnetimeItemLabel;
                    break;
                case 'disabled':
                        sMessage = ofConfig.roDisabledItemLabel;
                    break;
            }
            return sMessage;
        };
        self.customerComment = ko.observable('');

        self.noteBook = ko.observable();

        self.populateNoteBook = function(){
            var noteBook = {};
            var count = 0;

            noteBook.notes =  ko.observableArray([]);

            if(noteBookPayload.length > 0 ){

                noteBook.nbKey = noteBookPayload[0].nb_key;
                noteBook.id    = noteBookPayload[0].id;
                noteBook.refId = noteBookPayload[0].ref_id;

                _.each(noteBookPayload,function(note){
                    if(note.internal_notes || note.external_notes) {
                        count++;
                        item = {};

                        item.internalNotes    = note.internal_notes;
                        item.externalNotes    = note.external_notes;
                        item.postedByName     = note.c_f_nm + ' ' + note.c_l_nm;
                        item.postedByUsername = note.username;
                        item.createDate       = note.create_date;
                        noteBook.notes.push(ko.observable(item));
                    }
                });
            }

            self.noteBook(noteBook);
        }();

        self.buildOrderHeaderFields = function(aFields) {
            //Available order header fields that can be POSTed using this function
            var orderHeaderFields = {
                "UseMultipleShipments": viewModel.useMultipleShipments(),
                "Nickname" : viewModel.nickname(),
                "QuoteExpirationDate": viewModel.expirationDate()
            };

            var orderHeaderFieldsJson = {};

            //Add only the field(s) that are passed in aFields.
            _.forEach(aFields, function(sField) {
                orderHeaderFieldsJson[sField] = orderHeaderFields[sField];
            });

            self.setOrderHeaderFields(orderHeaderFieldsJson);

        };

        self.setOrderHeaderFields = function(orderHeaderFieldsJson) {

            var postOptions = {
                data: "orderHeaderFields=" + encodeURIComponent(JSON.stringify(orderHeaderFieldsJson)),
                error: function(data) {
                    alert('error setting Order Header fields');
                }
            };

            postInfo(postOptions, 'setOrderHeaderFields');
        };

        if(self.shipments().length === 0) {
            //self.addShipment();
        }

        self.setPaymentMethod = function(paymentMethod) {
            if(paymentMethod.refID() != viewModel.paymentMethod.refID()){
                var postOptions = {
                    data: "pm_id=" + paymentMethod.paymentMethodKey(),
                    error: function(data) {
                        alert('error setting pm');
                    },
                    success: function() {
                        $('#ccpm_container').removeClass('hide');
                        viewModel.updatePoRequired();
                    }
                }

                postInfo(postOptions, 'setPmId');
            }
        }

        self.ccn_id.subscribe(function(newCCN) {
            if(newCCN !== '') {
                var payload = 'ccn_id=' + newCCN;
                var postOptions = {
                    data: payload,
                    error: function(data) {
                        alert('error setting CCN');
                    }
                }

                postInfo(postOptions, 'setCCN');
            }
        });

        self.SetCouponCode = function(couponCode) {
            var payload = 'couponcode=' + encodeURIComponent(couponCode);
            var postOptions = {
                data: payload,
                error: function(data) {
                    alert('error applying coupon');
                }
            }

            postInfo(postOptions, 'setCoupon');
        }

        self.SetGiftCertificate = function(giftCertCode) {
            var payload = 'giftCertCode=' + encodeURIComponent(giftCertCode);
            var postOptions = {
                data: payload,
                error: function(data) {
                    alert('error applying gift certificate');
                }
            }

            postInfo(postOptions, 'setGiftCertCode');
        };

        self.allItemsAllocated = ko.computed(function() {
            var totalItemCount = self.detailLines().reduce(function(previous, current) {
                return previous + current.qty();
            }, 0);

            var allocatedItemCount = 0;
            self.shipments().forEach(function(shipment) {
                allocatedItemCount += shipment.details().reduce(function(previous, current) {
                    return previous + current.qtyToShip();
                }, 0);
            });

            return totalItemCount === allocatedItemCount;
        });

        self.itemsOnOrder = ko.computed(function() {
            var items = [];
            self.detailLines().forEach(function(detailLine) {
                detailLine.hasChildProducts = ko.observable(function() {
                    return self.detailLines().filter(function(outerLine) {
                        return outerLine.instance() == detailLine.instance()
                                && detailLine.removeType() != 'instance'
                    }).length > 1
                }());

                detailLine.hasParentInCart = ko.observable(function() {
                    return self.detailLines().filter(function(outerLine) {
                        return outerLine.orderDetailKey() != detailLine.orderDetailKey()
                                && outerLine.instance() == detailLine.instance()
                                && detailLine.removeType() == 'instance'
                    }).length > 0
                }());

                detailLine.instanceChildren = self.detailLines().filter(function(item) {
                    return item.instance() == detailLine.instance() && item.removeType() != 'instance' && item.parentProductID().trim() != '' && item != detailLine;
                }) || [];

                var mainProduct = self.detailLines().filter(function(item) {
                                                return item.instance() == detailLine.instance() && item.removeType() == 'instance';
                                            })[0] || detailLine;

                var mainProductQty = mainProduct.qty();

                detailLine.superUserMinPrice = ko.observable(detailLine.commodity[ofConfig.superUserMinPriceProperty]() || 0);

                detailLine.instanceUnitPrice = ko.observable(self.detailLines().reduce(function(current, item) {
                    if(!ofConfig.showProductDiscount){
                        item.priceBeforeAdjustment(item.price());
                    }
                    if(item.instance() == detailLine.instance()) {
                        if(item.parentProductID().trim() != '' && item.removeType() != 'instance') {
                            return current += ((item.priceBeforeAdjustment() || item.price()) * item.qty()) / mainProductQty;
                        } else {
                            return current += (item.priceBeforeAdjustment() || item.price());
                        }
                    } else {
                        return current;
                    }
                },0));

                detailLine.instanceUnitPriceDisplay = ko.computed(function(){
                    return utils.formatMoney(detailLine.instanceUnitPrice(), utils.decimalPlacesOnUnitPrices);
                });

                detailLine.instanceExtPrice = ko.observable(self.detailLines().reduce(function(current, item) {
                    if(item.instance() == detailLine.instance()) {
                        return current += item.extTotal();
                    } else {
                        return current;
                    }
                },0));

                detailLine.allocatedQty = ko.observable(function() {
                    var runningTotal = 0;
                    self.shipments().forEach(function(shipment) {
                        shipment.details().forEach(function(shipDetail) {
                            if(shipDetail.orderDetailId() === detailLine.orderDetailKey()) {
                                runningTotal += shipDetail.qtyToShip();
                            }
                        });
                    });
                    return runningTotal;
                }());

                detailLine.minGrossMarginPercent = ko.computed(function() {
                    if(self.superUserRestrictMinPrice() & detailLine.superUserMinPrice() != null)
                    {
                        return ( (detailLine.superUserMinPrice() - detailLine.commodity.cost()) / detailLine.superUserMinPrice() * 100 ).toFixed();
                    } else {
                        return undefined;
                    }
                });

                detailLine.grossMarginPercent = ko.computed({
                    write: function(newVal) {
                        var localGrossMarginPercent = parseInt(detailLine.minGrossMarginPercent() || 0);
                        var val = parseInt(newVal);
                        if(val < localGrossMarginPercent) {
                            val = localGrossMarginPercent;
                        }
                        var marginPercent = val / 100;
                        var markup = marginPercent / (1 - marginPercent);
                        var newPrice = (detailLine.commodity.cost() * (1 + markup)).toFixed(2);
                        detailLine.price(undefined);
                        detailLine.price(newPrice);
                        detailLine.priceCalculationType('fixed');
                    },
                    read: function() {
                        var marginPercent = (detailLine.price() - detailLine.commodity.cost()) / detailLine.price();
                        return (marginPercent * 100).toFixed(0);
                    },
                    deferEvaluation: true
                });

                detailLine.minMarkupPercent = ko.computed(function() {
                    if(self.superUserRestrictMinPrice() & detailLine.superUserMinPrice() != null)
                    {
                        return ( detailLine.superUserMinPrice() / detailLine.commodity.cost() - 1 ).toFixed();
                    } else {
                        return undefined;
                    }
                });

                detailLine.markupPercent = ko.computed({
                    write: function(newVal) {
                        var minMarkupPercent = parseInt(detailLine.minMarkupPercent || 0);
                        var val = parseInt(newVal);
                        if(val < minMarkupPercent) {
                            val = minMarkupPercent;
                        }
                        var markup = val / 100;
                        var newPrice = (detailLine.commodity.cost() * (1 + markup)).toFixed(2);
                        detailLine.price(undefined);
                        detailLine.price(newPrice);
                        detailLine.priceCalculationType('fixed');
                    },
                    read: function() {
                        var markup = (detailLine.price() - detailLine.commodity.cost()) / detailLine.commodity.cost();
                        return (markup * 100).toFixed(0);
                    },
                    deferEvaluation: true
                });

                detailLine.maxDiscountPercent = ko.computed(function() {
                    if(self.superUserRestrictMinPrice() & detailLine.superUserMinPrice() != null)
                    {
                        return (100 * (1 - (detailLine.superUserMinPrice() / detailLine.commodity.retailPrice()))).toFixed();
                    } else {
                        return undefined;
                    }
                });

                detailLine.discountPercent = ko.computed({
                    write: function(newVal) {
                        var maxDiscountPercent = parseInt(detailLine.maxDiscountPercent() || 100);
                        var val = parseInt(newVal);
                        if(val > maxDiscountPercent) {
                            val = maxDiscountPercent;
                        }
                        var discount = val / 100;
                        var newPrice = (detailLine.commodity.retailPrice() * (1 - discount)).toFixed(2);
                        detailLine.price(undefined);
                        detailLine.price(newPrice);
                        detailLine.priceCalculationType('fixed');
                    },
                    read: function() {
                        var discount = 1 - detailLine.price() / detailLine.commodity.retailPrice();
                        return (discount * 100).toFixed(0);
                    },
                    deferEvaluation: true
                });

                detailLine.minGrossMarginAmount = ko.computed(function() {
                    if(self.superUserRestrictMinPrice() & detailLine.superUserMinPrice() != null)
                    {
                        return detailLine.superUserMinPrice() - detailLine.commodity.cost();
                    } else {
                        return undefined;
                    }
                });

                detailLine.grossMarginAmount = ko.computed({
                    write: function(newVal) {
                        var localMminGrossMarginAmount = parseInt(detailLine.minGrossMarginAmount() || 0);
                        var val = parseInt(newVal);
                        if(val < localMminGrossMarginAmount) {
                            val = localMminGrossMarginAmount;
                        }
                        var grossMargin = parseFloat(val);
                        var newPrice = (detailLine.commodity.cost() + grossMargin).toFixed(2);
                        detailLine.price(undefined);
                        detailLine.price(newPrice);
                        detailLine.priceCalculationType('fixed');
                    },
                    read: function() {
                        return (detailLine.price() - detailLine.commodity.cost()).toFixed(2);
                    },
                    deferEvaluation: true
                });

                detailLine.allowDeletes = ko.computed({
                    write: function(val) {
                        // TODO: need to store previous value for removeType (od_key, hide, instance) - checking child prods for now
                        if(val) {
                            detailLine.hasChildProducts() ? detailLine.removeType('instance') : detailLine.removeType('OD_Key');
                        } else {
                            detailLine.removeType('hide');
                        }
                    },
                    read: function() {
                        return !(detailLine.removeType() == 'hide')
                    }
                });

                detailLine.suMinQty = ko.computed({
                    write: function(val) {
                        val = parseFloat(val);
                        if(isNaN(val)) {
                            val = detailLine.minQty();
                        }
                        if(!detailLine.maxQty() == 0 && val > detailLine.maxQty()) {
                            detailLine.minQty(detailLine.maxQty());
                        } else {
                            detailLine.minQty(val);
                        }
                        detailLine.suMaxQty(detailLine.maxQty());
                        detailLine.suMinQty.notifySubscribers();
                    },
                    read: function() {
                        return detailLine.minQty();
                    }
                });

                detailLine.suMaxQty = ko.computed({
                    write: function(val) {
                        var originalValue = detailLine.maxQty();
                        var newValue = parseFloat(val);;

                        if(isNaN(newValue)) {
                            newValue = originalValue;
                        }
                        if(newValue > 0) {
                            if(detailLine.minQty() > 0 && newValue < detailLine.minQty()) {
                                newValue = detailLine.minQty();
                            } else {
                                var diff = ( (newValue-detailLine.minQty()) * 1000 ) % ( (detailLine.qtyIncrement() || 1) * 1000 ) / 1000;
                                newValue = newValue - diff;
                                newValue = detailLine.minQty() > 0 && newValue < detailLine.minQty() ? detailLine.minQty() : newValue;
                            }
                        } else {
                            newValue = 0;
                        }

                        // reset maxQty to workaround KO bug that causes
                        // underlying value to be changed, but the UI to
                        // not update if the end value is the same as the
                        // original value
                        /* - not a bug, need to notifySubscribers UI was broken
                        detailLine.maxQty(99);
                        detailLine.maxQty(0);
                        */

                        // set valid newValue
                        detailLine.maxQty(newValue);
                    },
                    read: function() {
                        return detailLine.maxQty();
                    }
                });

                detailLine.suQtyIncrement = ko.computed({
                    write: function(val) {
                        val = parseFloat(val);
                        if(isNaN(val)) {
                            val = detailLine.qtyIncrement();
                        }
                        detailLine.qtyIncrement(val);
                        detailLine.suMaxQty(detailLine.maxQty());
                        detailLine.suQtyIncrement.notifySubscribers();
                    },
                    read: function() {
                        return detailLine.qtyIncrement();
                    }
                });

                detailLine.selectedRoAction = ko.computed(function(){
                    var sAction = detailLine.roRecurringType();
                    var skipNextRuns = detailLine.roSkipNextRuns();
                    if( sAction == '' || sAction == 'recurring'){
                        if( skipNextRuns == 0){
                            sAction = 'recurring';
                        }else{
                            sAction = 'skipnext';
                        }
                    }
                    return sAction;
                });

                detailLine.hasQuantityRestrictions = ko.computed(function () {
                    return !!(
                                (detailLine.minQty() && detailLine.minQty() !== oConfig.defaultMinimumQuantity) || 
                                ((!detailLine.uomConversion() || detailLine.uomConversion() == 1) && detailLine.qtyIncrement() && detailLine.qtyIncrement() !== oConfig.defaultMinimumQuantity) || 
                                detailLine.maxQty()
                            );
                });

                detailLine.quantityRestrictionsHtml = ko.computed(function () {
                    var lines = [];

                    if (detailLine.minQty() && detailLine.minQty() != oConfig.defaultMinimumQuantity)
                        lines.push(oConfig.labels.minQtyPopover.replace(/<min_qty>/,detailLine.minQty()));

                    if ( (!detailLine.uomConversion() || detailLine.uomConversion() == 1) && detailLine.qtyIncrement() && detailLine.qtyIncrement() != oConfig.defaultQuantityIncrement)
                        lines.push('Qty Increment: ' + detailLine.qtyIncrement());

                    if (detailLine.maxQty())
                        lines.push('Maximum Qty: ' + detailLine.maxQty());

                    return lines.join('<br>');
                });

                items.push(detailLine);
            });

            runHook('ItemsOnOrderArrayBeforeReturn', { detailLines: items });

            return items;
        });
        
        self.detailLines.subscribe(function(){
            self.isMinimumOrderTotalMet(checkOrderTotal(self));
        });
        self.itemsOnOrder.subscribe(function(){
            self.isMinimumOrderTotalMet(checkOrderTotal(self));
        });

        self.itemShipToMap = ko.computed(function(){
            var items = [];
            self.shipments().forEach(function(shipment) {
                oShipTo = shipment.shipTo();

                shipment.details().forEach(function(shipDetail) {
                    var item                   = new Object();
                    item.shaKey                = ko.observable(oShipTo.key());
                    item.key                   = ko.observable(shipDetail.orderDetailId());
                    item.orderDetailKey        = shipDetail.orderDetailId();
                    item.instance              = ko.observable(shipDetail.orderDetail.instance());
                    item.qty                   = ko.observable(shipDetail.qtyToShip());
                    item.qtyControlledFrom     = ko.observable(shipDetail.orderDetail.qtyControlledFrom());
                    item.qtyIncrement          = ko.observable(shipDetail.orderDetail.qtyIncrement());
                    item.getPrice              = shipDetail.orderDetail.price,
                    item.suPrice               = shipDetail.orderDetail.price,
                    item.minQty                = ko.observable(shipDetail.orderDetail.minQty());
                    item.moveItemToNewShipTo   = self.moveItemToNewShipTo;
                    item.removeItemToShipToMap = self.removeItemToShipToMap;
                    item.availableAddresses    = shippingAddresses;
                    item.shipTo                = ko.observable({});
                    item.parentProductID       = shipDetail.orderDetail.parentProductID();
                    item.availableAddresses().forEach(function(address,index){
                        if( address.key() == oShipTo.key()){
                            item.shipTo(address);
                        }
                    });

                    if(shipDetail.orderDetail.instanceChildren.length > 0){
                        item.shipTo.subscribe(function(newValue) {
                            items.forEach(function(map) {
                                if(map.instance() == item.instance() && map.shipTo().key() != item.shipTo().key()) {
                                    map.shipTo(item.shipTo());
                                }
                            })
                        });
                    }

                    items.push(item);
                })
            });

            return items;
        });
        
        _.each(self.itemShipToMap(), function(item){
            item.qty.subscribe(function () { 
                self.isMinimumOrderTotalMet(checkOrderTotal(self));
            });   
        });

        self.selectedOrderActionRefId = ko.observable(
            function(){
                if(self.orderActions().length > 0 ){
                    return self.orderActions()[0].refID();
                }else{
                    return '';
                }
            }()
        );

        self.selectedOrderAction = ko.computed(
            function(){
                var action = _.find(self.orderActions(), function(action){
                    if( action.refID() == self.selectedOrderActionRefId() ){
                        return action;
                    }
                });
                if(action && action.refID()){
                    return action;
                }else if(self.orderActions().length > 0 ){
                    return self.orderActions()[0];
                }else{
                    return {
                        showPaymentMethods: ko.observable(true),
                        showRecurringOrderSettings: ko.observable(false)
                    };
                }
            }
        );

        self.showOrderActionButton = function(data, parent) {
            //override this function for custom logic to show custom buttons
            var sSelectedRefId = self.selectedOrderAction() && self.selectedOrderActionRefId();
            var bShow = data.showButton() && (data.refID() == sSelectedRefId || parent.orderActions().length == 1);

            return bShow;
        };

        self.getAddressFinderId = function(id, data, parent){
            selector = id + '_' + parent.orderDetailKey + '_' + ($.isEmptyObject(data) ? 0 : data.key());
            return selector;
        };

       self.selectedLifecycleStage = ko.computed(function(){
            var stage = _.find(self.lifecycleStages(), function(stage){
                if( stage.refID() == self.lifecycleStage() ){
                    return stage;
                }
            });
            if(stage){
                return stage;
            }else{
                return {
                    alwaysShowMessage: ko.observable(false),
                    confirmationMessage: ko.observable('')
                };
            }
       });

        if(self.lifecycleStage() == 'cancelled' || self.lifecycleStage() == 'punchout_punched_in'){
            self.completed(true);
        }

        self.showConfirmationMessage = ko.observable(function(){
            return (self.completed() || self.lifecycleStage() == 'pending' || self.lifecycleStage() == 'cancelled' || self.selectedLifecycleStage().alwaysShowMessage());
        }())

        self.allowAccountEdits         = ko.observable(self.allowAccountEdits() && ofConfig.AllowAccountEdits);
        self.allowShaEdits             = ko.observable(self.allowShaEdits() && ofConfig.AllowShippingEdits);
        self.allowWarehouseSelection   = ko.observable(ofConfig.allowWarehouseSelection );
        self.superUserRestrictMinPrice = ko.observable(ofConfig.superUserRestrictMinPrice);

        self.postOrderToGoogleAnalytics  = function() {

            if (self.completed() && self.tracked() != '1' ) {

                if(oConfig.t_ga4 && oConfig.t_ga4_analytics != '') {
                    try {

                        var items = [];

                        for(var i = 0; i < self.detailLines().length; i++) {
                            var line = self.detailLines()[i];

                            var item = {
                                item_id: line.sku(),
                                item_name: line.name(),
                                currency: "USD",
                                price: line.price(),
                                quantity: Number(line.qty()),
                                index: i
                            }
                            items.push(item);
                        }

                        if (items.length >= 1) {

                            window.dataLayer = window.dataLayer || [];
                            function gtag(){dataLayer.push(arguments);}

                            gtag("event", "purchase", {
                                transaction_id: self.orderNumber(),
                                affiliation: ofConfig.t_tracking_company,
                                value: self.orderTotal(),
                                tax: self.taxTotal(),
                                shipping: self.shippingTotal(),
                                currency: "USD",
                                coupon: self.couponCode(),
                                items: items
                            });
                        }
                    } catch(err) {
                        console.log(err);
                    } 
                }else if(ofConfig.t_gtm) {
                    try {
                        var productsOrdered = [];
                        var transactionProducts = [];
                        var transactionItems = [];

                        for(var i = 0; i < self.detailLines().length; i++) {
                            var line = self.detailLines()[i];

                            productsOrdered.push({
                                'orderNumber': self.orderNumber(),
                                'name': self.customer.firstName() + ' ' + self.customer.lastName(),
                                'sku': line.sku(),
                                'category': line.searchfield1(),
                                'brand': line.searchfield5(),
                                'unitPrice': line.price(),
                                'quantityOrdered': line.qty()
                            });

                            // id is used for Enhanced Ecommerce
                            // sku is used for Google Analytics (non-EE)
                            transactionProducts.push({
                                'id': line.sku(),
                                'sku': line.sku(),
                                'name': line.name(),
                                'category': line.searchfield1(),
                                'price': line.price(),
                                'quantity': line.qty()
                            });

                            transactionItems.push({
                                'item_id': line.sku(),
                                'item_name': line.name(),
                                'price': line.price(),
                                'quantity': line.qty()
                            });
                        }

                        window.dataLayer.push({ 'pageType' : 'confirmation' });
                        window.dataLayer.push({ 'orderNumber' : self.orderNumber() });
                        window.dataLayer.push({ 'storeName' : ofConfig.t_tracking_company });
                        window.dataLayer.push({ 'productTotal' : self.productTotal() });
                        window.dataLayer.push({ 'shippingTotal' : self.shippingTotal() });
                        window.dataLayer.push({ 'taxTotal' : self.taxTotal() });
                        window.dataLayer.push({ 'orderTotal' : self.orderTotal() });
                        window.dataLayer.push({ 'customerName' : self.customer.firstName() + ' ' + self.customer.lastName() });
                        window.dataLayer.push({ 'customerFirstname' : self.customer.firstName() });
                        window.dataLayer.push({ 'customerLastName' : self.customer.lastName() });
                        window.dataLayer.push({ 'customerCity' : self.account.city() });
                        window.dataLayer.push({ 'customerState' : self.account.state() });
                        window.dataLayer.push({ 'customerCountry' : self.account.country() });
                        window.dataLayer.push({ 'customerEmail' : self.customer.email() });
                        window.dataLayer.push({ 'couponCode' : self.couponCode() });
                        window.dataLayer.push({ 'couponAmount' : self.couponDiscountTotals.totalDiscount() });
                        window.dataLayer.push({ 'paymentMethod' : self.paymentMethod.name() });
                        window.dataLayer.push({ 'sessionIP' : ofConfig.SessionIP });

                        // For Google Analytics (non-EE)
                        window.dataLayer.push({
                            'transactionId': self.orderNumber(),
                            'transactionAffiliation': ofConfig.t_tracking_company,
                            'transactionTotal': self.orderTotal(),
                            'transactionTax': self.taxTotal(),
                            'transactionShipping': self.shippingTotal(),
                            'transactionProducts': transactionProducts,
                            'transactionItems': transactionItems
                        });

                        // For Enhanced Ecommerce
                        window.dataLayer.push({
                            'event': ofConfig.t_gtm_transaction_event_name,
                            'ecommerce': {
                                'purchase': {
                                    'actionField': {
                                        'id': self.orderNumber(),
                                        'affiliation': ofConfig.t_tracking_company,
                                        'revenue': self.orderTotal(),
                                        'tax': self.taxTotal(),
                                        'shipping': self.shippingTotal(),
                                        'coupon': self.couponCode()
                                    },
                                    'products': transactionProducts,
                                    'items': transactionItems
                                }
                            }
                        });

                        window.dataLayer.push({ 'productsOrdered' : productsOrdered });

                    } catch(err) {}

                } 

                if(ofConfig.t_adwords && ofConfig.t_adwords_analytics && ofConfig.t_adwords_conversion){

                    window.dataLayer = window.dataLayer || [];
                    function gtag(){dataLayer.push(arguments);}

                    gtag('event', 'conversion', {
                        'send_to': ofConfig.t_adwords_analytics + '/' + ofConfig.t_adwords_conversion,
                        'value': self.orderTotal(), // Total
                        'currency': 'USD',
                        'transaction_id': self.orderNumber(), // Order ID
                        'shipping' : self.shippingTotal(), // Shipping
                        'tax' : self.taxTotal() // Tax
                    });

                }

                if(ofConfig.t_facebook && ofConfig.t_facebook_analytics){

                    fbq('track', 'Purchase', {
                        value: self.orderTotal(),
                        currency: 'USD'
                    });

                }

                if(ofConfig.t_bing && ofConfig.t_bing_analytics){

                    var items = [];
                    var item_ids = [];

                    for(var i = 0; i < self.detailLines().length; i++) {
                        var line = self.detailLines()[i];
                        var item_id = line.sku();

                        var item = {
                            id: line.sku(),
                            item_name: line.name(),
                            price: line.price(),
                            quantity: Number(line.qty())
                        }
                        items.push(item);
                        item_ids.push(item_id);
                    }

                    if (items.length >= 1) {
                        window.uetq = window.uetq || [];
                        window.uetq.push('event', 'purchase', { 
                            'transaction_id': self.orderNumber(),
                            'ecomm_prodid': item_ids,
                            'ecomm_pagetype': 'purchase',
                            'revenue_value': self.orderTotal(),  // Value of the conversion
                            'currency': 'USD',
                            'items': items
                        });
                    }

                }
            }
        }

        self.updateOrderAccess = function(){
            if( self.completed() || self.lifecycleStage() == 'cancelled' || self.lifecycleStage() == 'punchout_punched_in'){
                self.allowAccountEdits( false );
                self.allowContactEdits( false );
                self.allowShaEdits( false );
                self.allowWarehouseSelection( false );
                self.allowProductAdds( false );
                self.allowShipViaEdits( false );
                self.allowPayMethodEdits( false );
                self.allowCcnChanges( false );
                self.allowPlaceOrder( false );
                self.allowQtyControls( false );
                self.disablePlaceOrder( true );
            }else if(self.superUserOrderFormMode()){
                self.allowAccountEdits(self.allowAccountEdits() && ofConfig.superUserAllowAccountEdits);
                self.allowContactEdits(self.allowContactEdits() && ofConfig.superUserAllowContactEdits);
                self.allowShaEdits(self.allowShaEdits() && ofConfig.superUserAllowShaEdits);
                self.allowWarehouseSelection(ofConfig.allowWarehouseSelection || ofConfig.superUserAllowWarehouseSelection );
                self.allowProductAdds(true);
                self.allowShipViaEdits(self.allowShipViaEdits() || ofConfig.superUserAllowShipViaEdits);
                self.allowPayMethodEdits(self.allowPayMethodEdits() || ofConfig.superUserAllowPayMethodEdits);
                self.allowCcnChanges(self.allowCcnChanges() || ofConfig.superUserAllowCcnChanges);
                self.allowPlaceOrder(self.allowPlaceOrder() || ofConfig.superUserAllowPlaceOrder);
                self.allowQtyControls( ofConfig.superUserAllowQtyControls );
                self.disablePlaceOrder( false );
            }else{
                self.allowAccountEdits( self.allowAccountEdits() && ofConfig.AllowAccountEdits );
                self.allowContactEdits( self.allowContactEdits() && ofConfig.allowContactEdits );
                self.allowShaEdits( self.allowShaEdits() && ofConfig.AllowShippingEdits );
                self.allowWarehouseSelection( ofConfig.allowWarehouseSelection );
                self.allowProductAdds( self.allowProductAdds() );
                self.allowQtyControls( false );
                self.disablePlaceOrder( false );
            }

            var inFocusTemplate = true;
            if(self.completed() && inFocusTemplate) {
                $('#focusHeaderExit').removeAttr("data-toggle");
                $('#focusHeaderExit').attr("href" , "https://vandyk3.cimproduction.com");
            }
        };

        self.updateOrderAccess();

        self.continueProcessingForm = function(data) {
            var bContinueProcessing = true;

            //TODO: Get this validation working using the validation plugin.
            //This is a quick 'fix' to prevent the page from processing the order action
            //when the shipping account is required but not set.
            _.each(viewModel.shipments(), function(shipment, index){
                if(ofConfig.CollectShipAccount && shipment.selectedShipVia.collectShippingAccount() && !shipment.selectedShipVia.shippingAccountId.isValid() ){
                    //form is not valid
                    scrollToSection('#shipping-account-info' + (index+1));
                    //jQuery('#shipping-account-select' + shipmentCount).addClass('control-group error');
                    bContinueProcessing = false;
                    return false;
                }
            });

            if(viewModel.selectedOrderAction != undefined && viewModel.selectedOrderAction().showPaymentMethods()) {
                //TODO: Do CC validation in a less janky way
                //This is a quick 'fix' to prevent the page from processing the order action
                //when the CC billing address info is invalid
                //Currently doesn't provide any messaging
                if(viewModel.allowPayMethodEdits() && viewModel.paymentMethod.paymentType() === 'cc' && (data.placeOrderActionType() == 'place' || data.placeOrderActionType() == 'validate-only')) {

                    if(   ($('#ccpm_cvv2_code').is(':visible') && !$('#ccpm_cvv2_code').val())
                        || !$('#ccpm_address_input').val()
                        || !$('#ccpm_city_input').val()
                        || !$('#ccpm_country_input').val()
                        || !($('#ccpm_state_dropdown').prop('disabled') ? $('#ccpm_state_text').val() : $('#ccpm_state_dropdown').val())
                        || !$('#ccpm_zip_input').val()
                    ) {
                        // CC billing address form is not valid
                        $("#ccpm_card_billing_address_section").addClass("in").css({ "height" : "auto" });
                        $("#ccpm_card_billing_address_section :input:visible:not('button')")
                            .filter(function() { return $(this).val() == ""; })
                            .trigger("change");
                        $("#ccpm_cvv2_code").trigger("change");
                        scrollToSection('#payment_methods');
                        bContinueProcessing = false;
                        return false;
                    }
                }
                //Pretty much just following the same pattern as cc validation.
                //Need to stop the page from processing if the poNum is required and not entered.
                if(viewModel.paymentMethod.collectPO() && viewModel.paymentMethod.poRequired() && !$('#ponumber').val()) {
                    jQuery('#controlgroup_invoice_ponumber').addClass('control-group error');
                    scrollToSection('#payment_methods');
                    bContinueProcessing = false;
                    return false;
                }
            }

            //hook for adding custom validation checks
            var config = {
                continueProcessing: bContinueProcessing
            };
            runHook('cartTemplatesContinueProcessing', config);
            return config.continueProcessing;
        };

        self.processOrderAction = function(data){
            var bContinueProcessing = self.continueProcessingForm(data);

            if(bContinueProcessing){

                if( data.showComments() && viewModel.customerComment() != '' ){
                    self.saveCustomerComment();
                }

                if( !data.showOrderCcEmail() && viewModel.orderCcEmail() != ''){
                    //if the user entered an email then chose an OA that isn't showing the email input, blank it out.
                    viewModel.orderCcEmail('');
                }

                if(data.showRecurringOrderSettings()){
                    self.saveRecurringOrderSettings();
                }

                var postData = {};
                var bPost = true;
                postData.orderActionKey = data.orderActionKey();

                if(viewModel.paymentMethod.paymentType() === 'cc' && (viewModel.selectedOrderAction != undefined && viewModel.selectedOrderAction().showPaymentMethods())) {

                    var defaultCcnChecked = $('#ccpm_default_ccn_id_selection').is(':checked');

                    if(defaultCcnChecked) {
                        postData.setDefaultCcn = 1
                    }

                    postData.ccn_key =  $('#ccpm_ccn_id').val();
                    postData.cvv     =  $('#ccpm_cvv2_code').val();
                    postData.address =  $('#ccpm_address_input').val();
                    postData.city    =  $('#ccpm_city_input').val();
                    postData.country =  $('#ccpm_country_input').val();
                    postData.state   =  $('#ccpm_state_dropdown').prop('disabled') ? $('#ccpm_state_text').val() : $('#ccpm_state_dropdown').val();
                    postData.zip     =  $('#ccpm_zip_input').val();
                    postData.tsm_id  =  viewModel.paymentMethod.merchantId();

                }else if(viewModel.paymentMethod.paymentType() === 'paypal' && data.placeOrderActionType() == 'place'){
                    bPost = false;
                    postPayPal(data.orderActionKey());
                }

                var postOptions = {
                    data: postData,
                    error: function() {
                        alert('Error Processing Order Action');
                    }
                }

                var scrollToId = 'checkout';

                runHook('cartTemplateOverridePostOptions', { postOptions: postOptions, data: data, self: self });

                if (bPost){
                    var postOrderAction = function() {
                        if ($.active > 0) {
                            viewModel.processing(true);
                            $( document ).one("ajaxStop", function(){
                                postInfo(postOptions, 'processOrderAction', scrollToId);
                            });
                        }
                        else {
                            postInfo(postOptions, 'processOrderAction', scrollToId);
                        }
                    }

                    if(data.placeOrderActionType() == 'punch-in'){
                        self.postPunchin(postOrderAction, postOptions);
                    } else {
                        postOrderAction();
                    }
                }
            }
        };

        self.postPunchin = function(fncPostOrderAction, orderActionPostOptions){
            toggleLoadingWidget(true);
            var model = viewModel;
            model.activeAjaxRequestCount(model.activeAjaxRequestCount() + 1);
            model.processing(true);

            var bPostPunchIn = false;
            var punchinXmlUrl = orderInfoPostUrl + '?o_key=' + model.orderKey() + '&ajax=true&pageaction=getPunchinXml';
            var getPunchinXmlPostOptions = {
                url: punchinXmlUrl,
                data: { orderKey: viewModel.orderKey() },
                success: function(data) {
                    if (data.trim().startsWith('<?xml')) {
                        $('input#cxml-urlencoded').val(data);
                        orderActionPostOptions.success = function() {
                            bPostPunchIn = true;
                        }
                        orderActionPostOptions.complete = function() {
                            if(bPostPunchIn) {
                                $('form#punch-in').submit();
                            }
                        }
                        fncPostOrderAction();
                    } else {
                        alert('Error Processing Order Action: Unable to retrieve valid punchin data.');
                    }
                },
                error: function() {
                    alert('Error Processing Order Action: Unable to retrieve punchin data.');
                },
                complete: function() {
                    model.activeAjaxRequestCount(model.activeAjaxRequestCount() - 1);
                    if(model.activeAjaxRequestCount() < 1) {
                        model.processing(false);
                    }
                    toggleLoadingWidget(false);
                }
            };
            
            if ($.active > 0) {
                viewModel.processing(true);
                $( document ).one("ajaxStop", function(){
                    $.ajax(getPunchinXmlPostOptions);
                });
            }
            else {
                $.ajax(getPunchinXmlPostOptions);
            }
        };

        self.saveRecurringOrderSettings = function(){
            var postData = [];
            var order = [];

            var orderKey = viewModel.orderKey();

            var roRunDate = viewModel.roRunDate();
            var roRecCase = viewModel.roRecCase();
            var roRecQty  = viewModel.roRecQty();
            //This has to be set before processOrderAction or the OO will "unset" the values above when the record_type isn't set to recurring.
            var recordType = "recurring";


            order.push({
                "o_key" : orderKey,
                "ro_rundate": roRunDate,
                "ro_rec_case": roRecCase,
                "ro_rec_qty": roRecQty,
                "record_type": recordType
            });

            postData =
            {
                "Tables": [
                    {
                        "TableName"           : "orders",
                        "TableKeyField"       : "o_key",
                        "UserKeyField"        : "o_key",
                        "UserKeyIsPrimaryKey" : "True",
                        "Data"                : order
                    }
                ]
            }

            jQuery.ajax({
                url: 'payment.asp' + '?o_key=' + viewModel.orderKey() + '&ajax=true&pageaction=postLogicJSON&randomnum=' + new Date().getTime()
                , data: "postLogicJSON=" + encodeURIComponent(JSON.stringify(postData))
                , cache: false
                , type: 'POST'
                , dataType: 'json'
                , success: function(data,status,request){
                    viewModel.roRunDate(roRunDate);
                    viewModel.roRecCase(roRecCase);
                    viewModel.roRecQty(roRecQty);
                }
                , error: function(data) {
                    alert('Error posting Recurring Order Settings');
                }
                , complete: function(data) {
                }
            });
        };

        self.saveCustomerComment = function(){

            var postData = [];
            var noteBook = [];
            var note = [];
            var order = [];

            var notebookTableName = ofConfig.useNewNotebooks ? 'Notebooks' : 'note_books';
            var notebookKeyField = ofConfig.useNewNotebooks ? 'Id' : 'nb_key';
            var notebookRefField = ofConfig.useNewNotebooks ? 'ReferenceId' : 'ref_id';
            var ref_id = self.noteBook().refId || generateRefId();
            var n_key = utils.createGuid();

            if(ofConfig.useNewNotebooks) {
                var notebookId = parseInt(self.noteBook().id || 0) || ref_id;

                noteBook.push({
                    "Id": notebookId,
                    "ReferenceId": notebookId
                });

                order.push({
                    "o_key" : self.orderKey(),
                    "NotebookId": notebookId
                });

                note.push({
                    "n_key"          : n_key,
                    "ref_id"         : generateRefId(),
                    "NotebookId"     : notebookId,
                    "a_id"           : ofConfig.superUserAccountKey || ofConfig.AccountKey,
                    "c_id"           : ofConfig.superUserCustomerKey || ofConfig.CustomerKey,
                    "create_date"    : new Date().toDateString(),
                    "external_notes" : self.customerComment()
                });

            } else {
                var nb_key = self.noteBook().nbKey || utils.createGuid();

                order.push({
                    "o_key" : self.orderKey(),
                    "nb_id" : nb_key
                });

                noteBook.push({
                    "nb_key"        : nb_key,
                    "ref_id"        : ref_id,
                    "create_date"   : new Date().toDateString(),
                    "book_type"     : 'order',
                    "resource_c_id" : ofConfig.superUserCustomerKey || ofConfig.CustomerKey
                });

                note.push({
                    "n_key"          : n_key,
                    "ref_id"         : generateRefId(),
                    "nb_id"          : nb_key,
                    "a_id"           : ofConfig.superUserAccountKey || ofConfig.AccountKey,
                    "c_id"           : ofConfig.superUserCustomerKey || ofConfig.CustomerKey,
                    "create_date"    : new Date().toDateString(),
                    "external_notes" : self.customerComment()
                });
            }

            postData =
            {
                "Tables": [
                    {
                        "TableName"           : notebookTableName,
                        "TableKeyField"       : notebookKeyField,
                        "UserKeyField"        : notebookRefField,
                        "UserKeyIsPrimaryKey" : "False",
                        "Data"                : noteBook
                    },
                    {
                        "TableName"           : "orders",
                        "TableKeyField"       : "o_key",
                        "UserKeyField"        : "o_key",
                        "UserKeyIsPrimaryKey" : "True",
                        "Data"                : order
                    },
                    {
                        "TableName"           : "notes",
                        "TableKeyField"       : "n_key",
                        "UserKeyField"        : "ref_id",
                        "UserKeyIsPrimaryKey" : "False",
                        "Data"                : note
                    }
                ]
            }

            jQuery.ajax({
                url: 'payment.asp' + '?o_key=' + viewModel.orderKey() + '&ajax=true&pageaction=postLogicJSON&randomnum=' + new Date().getTime()
                , data: "postLogicJSON=" + encodeURIComponent(JSON.stringify(postData))
                , cache: false
                , type: 'POST'
                , dataType: 'json'
                , success: function(data,status,request){
                    viewModel.updateNoteBook(note[0]);
                    viewModel.customerComment('');
                    return data.PostedKeys[notebookTableName][0];
                }
                , error: function(data) {
                    alert('Error posting Comment');
                }
                , complete: function(data) {
                }
            });
        };

        self.updateNoteBook = function(note) {
            var item = {};

            item.internalNotes    = note.internal_notes;
            item.externalNotes    = note.external_notes;
            item.postedByName     = ofConfig.superUserName || ofConfig.customerName;
            item.postedByUsername = ofConfig.superUserUsername || ofConfig.customerUsername;
            item.createDate       = note.create_date;

            viewModel.noteBook().notes.unshift(item);
        };

        self.genericModalComplete = function(data){
            var postOptions = {
                success: function(data) {
                },
                error: function() {
                    alert('error reloading shipping accounts');
                }
            }

            postInfo(postOptions, 'getOrderJSON');
            return true;
        };

        self.removeProduct = function(data){
            if(ofConfig.bUseRemoveMsg){
                var bRemove = confirm(ofConfig.sRemoveProductMessage);
            }
            else{
                bRemove = true;
            }

            if (bRemove) {
                viewModel.processing(true);
                var itemToDelete = data;
                var removeType = itemToDelete.removeType();
                var orderDetailKey = itemToDelete.orderDetailKey();
                var instance = itemToDelete.instance();
                var sRemoveUrl = 'i_i_add_to_cart.asp?type=remove&unq=' + orderDetailKey + '&o_id=' + data.orderID() + '&modal=1';

                jQuery.ajax({
                    url: sRemoveUrl
                    , cache: false
                    , type: 'GET'
                    , success: function(data,status,request){
                        //Calling getOrderJSON here to handle removing reward products 
                        // From the UI when the target is removed.
                        viewModel.genericModalComplete();
                    }
                    , error: function(data) {
                        alert('Error removing item.');
                        viewModel.processing(false);
                    }
                    , complete: function(data) {
                        viewModel.processing(false);
                    }
                });
            }
        };

        self.resetProductEdits = function(editingDetailLine) {
            viewModel.detailLines()
                .forEach(function(detailLine) {
                    detailLine.editing(false);
                });
            viewModel.shipments()
                .forEach(function(shipment) {
                    shipment.details()
                        .forEach(function(detail) {
                            detail.orderDetail.editing(false);
                        });
                });
            viewModel.mainProduct(undefined);
            utils.removeActiveQuote();

            if(editingDetailLine) {
                editingDetailLine.editing(true);
            }
        }

        self.editProduct = function(data) {
            self.resetProductEdits();
            var detailLine = data;
            if(data.orderDetail) {
                detailLine = data.orderDetail;
            }
            var detailKey  = detailLine.orderDetailKey();
            var productKey = detailLine.parentProductID().trim() || detailLine.productID();

            var productInfo = {
                productKey: detailLine.productID(),
                productSku: detailLine.sku(),
                parentChildType: detailLine.commodity.parentChildType() || '',
                parentProductId: detailLine.parentProductID().trim()
            }

            self.loadProductForEdit(productInfo, detailKey, detailLine)
        }

        self.cancelEditProduct = function(data) {
            self.resetProductEdits();
        }

        self.loadProductForEdit = function(productInfo, detailLineId, detailLine) {
            viewModel.processing(true);
            self.resetProductEdits(detailLine);

            var postOptions = {
                success: function(data) {
                    var response = JSON.parse(data);
                    var product = response.product;
                    switch(product.childDisplayType) {
                        case 'input-qty':
                        case 'exploded-view':
                        case 'matrix-all':
                        case 'add-row':
                            product.childDisplayType = 'droplist'
                            break;
                        default:
                    };

                    if(!!response.childSkuMatch) {
                        oConfig.childSkuMatch = response.childSkuMatch;
                    }

                    utils.setActiveQuote(viewModel.orderKey());
                    viewModel.mainProduct(ko.mapping.fromJS(product, productMapping));
                    viewModel.addToSavedCart();
                },
                url: orderInfoPostUrl + '?o_key=' + self.orderKey() + '&p_key=' + (productInfo.parentProductId || productInfo.productKey) + '&od_key=' + detailLineId + '&sku=' + productInfo.productSku + '&ajax=true&pageaction=getProductData&modal=1',
                type: 'GET',
                error: function() {
                    alert('Error loading product data!');
                }
            }

            $.ajax({
                url      : postOptions.url,
                success  : postOptions.success,
                type     : postOptions.type,
                error    : postOptions.error,
                complete : function() { viewModel.processing(false); }
            });
        }

        self.productFinderSearchTerm = ko.observable('');

        self.productFinderUrl = ko.computed(function() {
            var searchTerm = self.productFinderSearchTerm();
            var search2 = ofConfig.productFinderSearchstring.replace(/__query__/g, searchTerm);
            return 'bulk_atc.asp?find_product=1&s=' + searchTerm + '&search2=' + search2;
        });

        self.showRemoveLink = function(data){
            if(!ofConfig.allowProductRemoves){
                return false;
            }

            var instanceKeys = viewModel.detailLines().reduce(function(instanceKeys, line) {
                if(instanceKeys.indexOf(line.instance()) == -1) {
                    instanceKeys.push(line.instance());
                }
                return instanceKeys;
            }, []);

            if(!ofConfig.allowRemoveLastItem && instanceKeys.length < 2) {
                return false;
            }

            if(data.removeType() == 'hide'){
                return false;
            }
            return true;
        };

        self.breadcrumbMarkup = ko.computed(function(){
            var thisIsCartPage = ofConfig.cartPage === getOriginalPageName();
            var isComplete = self.completed() || (self.lifecycleStage() == 'punchout_punched_in')

            var paymentCrumbLabel = "Payment";
            if(!ofConfig.showPaymentMethodsSection) {
                paymentCrumbLabel = thisIsCartPage ? "Cart" : "Finalize"
            }
            var completeCrumbLabel = ofConfig.isPunchoutSession ? "Sent to Procurement" : "Order Placed";

            var cartCrumb = "<li><a href=\"" + ofConfig.cartPage + "\"><i class=\"icon-shopping-cart\"></i> Cart</a> <span class=\"divider\"><i class=\"icon-chevron-right\"></i></span></li>";
            if(thisIsCartPage) {
                cartCrumb = "";
            }
            var accountCrumb = "<li><a href=\"account.asp\"><i class=\"icon-info-sign\"></i> Billing / Shipping</a> <span class=\"divider\"><i class=\"icon-chevron-right\"></i></span></li>";
            var paymentCrumb = "<li class=\"" + (isComplete ? "" : "active") + "\"><i class=\"icon-lock\"></i> " + paymentCrumbLabel + " <span class=\"divider\"><i class=\"icon-chevron-right\"></i></span></li>";
            var completeCrumb = "<li class=\"" + (isComplete ? "active" : "") + "\"><i class=\"icon-ok\"></i> " + completeCrumbLabel + "</li>";

            var dynamicBreadcrumbMarkup = "<ul class=\"breadcrumb breadcrumb-cart\">" + cartCrumb + accountCrumb + paymentCrumb + completeCrumb + "</ul>";

            if(self.cartType != 'recurring' && (thisIsCartPage || ofConfig.isPunchoutSession || !ofConfig.showPaymentMethodsSection)) {
                return dynamicBreadcrumbMarkup;
            }            

            if(self.recordType() == 'cart') {
                return ofConfig.sPaymentHeaderLabel;
            } else if (self.recordType() == 'recurring') {
                return '<ul class="breadcrumb breadcrumb-cart"><li class="active">Order</li><li><i class="icon-ok"></i> Order Placed</li></ul>';
            } else {
                return '<ul class="breadcrumb breadcrumb-cart"><li class="active">Order</li><li><i class="icon-ok"></i> Order Placed</li></ul>';
            }
        });

        self.expirationDate = ko.observable(function() {
            return new moment(self.quoteExpirationDate()).format("MM/DD/YYYY");
        }());

        self.expirationDate.subscribe(function(newExpirationDate) {
            self.buildOrderHeaderFields(["QuoteExpirationDate"]);
        });

        // Used for Google Analytics and Tag Manager, etc
         /*
        Runs when order is completed but payment page doesn't re-fresh
        - Example user pays with a credit card or bill me payment option
        */
        self.orderPlaced.subscribe(function() {
            self.postOrderToGoogleAnalytics();
            postOrderHeaderField('tracked', '1');
            self.tracked('1');
        });
        /*
        Runs when order is completed and the payment page re-freshes
        - Happens when user checks out with paypal and is re-directed back to payment page
        - with a completed order
        */
        if (self.completed() && self.tracked() != '1' ) {
            self.postOrderToGoogleAnalytics();
            postOrderHeaderField('tracked', '1');
            self.tracked('1');
         }

        addTimer("orderModelBottom");
        runHook('orderModelBottom', { self: self, order: self });
        addTimer("end order mapping");
    } // end of var Order = blah blah blah 

    function processShippingAddresses(data){
        var addressArray = data;
        var newshippingAddresses;
        if(Array.isArray(addressArray)) {
            newshippingAddresses = addressArray.map(function(item, index, arr) {
                address = new addressInfo();
                address.name(item.sha_nm);
                address.company(item.s_company || '');
                address.attention(item.attention || '');
                address.address1(item.s_add1 || '');
                address.address2(item.s_add2 || '');
                address.address3(item.s_add3 || '');
                address.address4(item.s_add4 || '');
                address.address5(item.s_add5 || '');
                address.country(item.s_country || 'USA');
                address.city(item.s_city || '');
                address.state(item.s_state || undefined);
                address.zipCode(item.s_zip || '');
                address.phone(item.s_phone || '');
                address.email(item.em || '');
                address.global(item.globaladdress || '0');
                address.opt1(item.s_opt1 || '');
                address.opt2(item.s_opt2 || '');
                address.opt3(item.s_opt3 || '');
                address.opt4(item.s_opt4 || '');
                address.opt5(item.s_opt5 || '');
                address.key(item.sha_key);
                address.editing(false);
                return address;
            });
        }

        /*
        Changing to an observable array (was an obeservable object that contained an array)
        */
        var observableAryOfSHA = ko.observableArray([]);
        newshippingAddresses.forEach(function(obj){
            observableAryOfSHA.push(ko.mapping.fromJS(obj, shippingAddressMappingOptions));
        });

        return observableAryOfSHA; //ko.mapping.fromJS(newshippingAddresses, shippingAddressMappingOptions);
    }

    var orderMapping = {
        create: function(options) {
            var newOrder = new Order(options.data);

            return newOrder; // new Order(options.data);
        }
    }

    //Build Json for order header field and optional additional tables.
    //Pass the Json to the Post Logic Ajax function
    var postOrderHeaderField = function(sOrderField, viewModelProperty, extraTablesJson, callbacks) {
        console.log(sOrderField);
        var postData = {};
        var order = [];

        order = [{
            "o_key" : ofConfig.OKey
        }];

        order[0][sOrderField] = viewModelProperty;

        postData =
        {
            "Tables": [
                {
                    "TableName"           : "orders",
                    "TableKeyField"       : "o_key",
                    "UserKeyField"        : "o_key",
                    "UserKeyIsPrimaryKey" : "True",
                    "Data"                : order
                }
            ]
        };

        //Optional additional tables on the order
        if (!!extraTablesJson) {
            postData["Tables"].push(extraTablesJson);
        }

        self.postLogicJsonAjax(postData, false, callbacks);
    };

    var postOrderDetailFields = function(sOrderDetailKey, sDetailFields, viewModelProperties, callbacks) {

        var postData = {};
        var orderLine = [];

        orderLines = [{
            "od_key" : sOrderDetailKey
        }];

        for(var i = 0; i < sDetailFields.length; i++){
            orderLines[0][sDetailFields[i]] = viewModelProperties[i];
        }

        postData =
        {
            "Tables": [
                {
                    "TableName"           : "order_detail",
                    "TableKeyField"       : "od_key",
                    "UserKeyField"        : "od_key",
                    "UserKeyIsPrimaryKey" : "True",
                    "Data"                : orderLines
                }
            ]
        };
        self.postLogicJsonAjax(postData, false, callbacks);
    };

    //Posts Json directly to table(s) using Post Logic
    //Should be used for fields that do not affect other properties in the ViewModel (i.e. text areas)
    //No actions on success.
    var postLogicJsonAjax = function(postData, getOrderJSON, callbacks) {
        toggleLoadingWidget(true);
        callbacks = callbacks || {};

        jQuery.ajax({
            url: 'payment.asp' + '?o_key=' + ofConfig.OKey + '&getorderJSON=' + getOrderJSON + '&ajax=true&pageaction=postLogicJSON&randomnum=' + new Date().getTime(),
            data: "postLogicJSON=" + encodeURIComponent(JSON.stringify(postData)),
            cache: false,
            type: 'POST',
            dataType: 'json',
            success: callbacks.success,
            error: function(jqXHR, textStatus, errorThrown) {
                console.log(jqXHR.status);
                if (typeof callbacks.error == 'function') {
                    callbacks.error(jqXHR, textStatus, errorThrown);
                } else {
                    if(jqXHR.status == 401){
                        location.href = 'showcart.asp?section=ValidateOrderOwnership&err=not-logged-in'
                    }else{
                        alert('Error posting to ' + (postData["Tables"][0]["TableName"] || "postLogicJsonAjax") + '.');
                    }
                }
            },
            complete: function(){
                toggleLoadingWidget(false);
            }
        });
    };


    var apiRoutedPageActions = [
        /*
        // can't be converted yet
        'placeOrder',
        'processOrderAction',
        */
        'setSha',
        'setCCN',
        'setPmId',
        'setCoupon',
        'setGiftCertCode',
        'setOrderHeaderFields',
        'setRequestedShipDate',
        'setShippingPrice',
        'getOrderJSON',
        'resetShippingPrice',
        'setShipmentShipVia'
    ];
    
    apiRoutedPageActions = apiRoutedPageActions.concat([
        'updateShipToAddress',
        'setOrderDetailFields',
        'assignOwnership'
    ]);
    

    var postInfo = function(ajaxOptions, pageAction, scrollToId, model) {
        toggleLoadingWidget(true);
        if(!model) {
            model = viewModel;
        }

        model.activeAjaxRequestCount(model.activeAjaxRequestCount() + 1);

        model.processing(true);

        model.errorMessages.removeAll();

        var existingSuccess = ajaxOptions.success;
        var existingError = ajaxOptions.error;
        var existingComplete = ajaxOptions.complete;
        
        var querystring;
        var comUrl;
        if(ajaxOptions.url){
            querystring = ajaxOptions.url.substring(ajaxOptions.url.indexOf('?'))
            comUrl = orderInfoPostUrl + querystring;
        }else{
            querystring = '?o_key=' + model.orderKey();
            comUrl = orderInfoPostUrl + '?o_key=' + model.orderKey() + '&ajax=true&pageaction=' + pageAction;
        } 
        var apiUrl = orderInfoPostApiUrl + pageAction + querystring;
                
        if (apiRoutedPageActions.includes(pageAction)) {
            ajaxOptions.url = apiUrl;
        } else if (!ajaxOptions.url) {
            ajaxOptions.url = comUrl;
        }

        if(!ajaxOptions.type) {
            ajaxOptions.type = 'POST';
        }

        ajaxOptions.success = function(data) {
            try{
                var response = JSON.parse(data);
            } catch (error){
                /*
                * if we get invalid json here, it's most likely a redirect due to
                * an expired session or other issue causing an html page to be
                * returned instead of processing the ajax request
                */
                location.href = 'payment.asp?o_key=' + model.orderKey();
            }
            if(Array.isArray(response) && Object.keys(response[0].Errors).length > 0) {
                for (var msg in response[0].Errors) {
                    if(response[0].Errors.hasOwnProperty(msg)) {
                        scrollToId = 'errorList';

                        model.errorMessages.push(
                            {
                                "errorType": msg,
                                "message": response[0].Errors[msg]
                            }
                        );
                    }
                }
            }

            ko.mapping.fromJS(response[1], orderMapping, model);

            model.updateOrderAccess();
            if(model.isOverLineLimit()){
                model.shippingComplete(true);
            }
            if(typeof existingSuccess === 'function') {
                existingSuccess(data);
            }
            
            _.each(model.itemShipToMap(), function(item){
                item.qty.subscribe(function () { 
                    model.isMinimumOrderTotalMet(checkOrderTotal(model));
                });   
            });

            if(scrollToId) {
                model.showConfirmationMessage(true);
                scrollToSection('#' + scrollToId);
            }
            
            model.detailLines().forEach(function(detailLine) {
                // We can't debounce/trottle an observable
                // so we add a computed to track qty changes.
                // We use this in a subscription to call
                // update cart to update qty change in bulk atc
                // to avoid loading the qty change without posting
                // and updating the entire order model (for perf)
                detailLine.qtyTracker = ko.computed(function() {
                    return detailLine.qty();
                }).extend({throttle: 500});
                detailLine.qtyTracker.subscribe(function (newQty) {
                    model.postQtyUpdate();
                }, detailLine);
                detailLine.workerPriceTracker = ko.computed(function() {
                    return detailLine.price();
                }).extend({throttle: 500});
                detailLine.workerPriceTracker.subscribe(function (newQty) {
                    model.setWorkerPriceOverride(detailLine);
                }, detailLine);
                detailLine.workerQtyLimitsTracker = ko.computed(function() {
                    return {
                        removeType: detailLine.removeType(),
                        minQty: detailLine.minQty(),
                        maxQty: detailLine.maxQty(),
                        qtyIncrement: detailLine.qtyIncrement()
                    };
                }).extend({throttle: 500});
                detailLine.workerQtyLimitsTracker.subscribe(function (newValue) {
                    model.setWorkerQtyLimits(detailLine);
                });
            });
        };

        ajaxOptions.error = function(jqXHR, textStatus, errorThrown) {
            if(jqXHR.status == 401){
                location.href = 'showcart.asp?section=ValidateOrderOwnership&err=not-logged-in'
            }
            if (ajaxOptions.url != comUrl) {
                // if API call fails, retry with COM AJAX
                ajaxOptions.url = comUrl;
                $.ajax({
                    url: orderInfoPostUrl + '?o_key=' + model.orderKey() + '&ajax=true&pageaction=logError&url=' + encodeURIComponent(apiUrl) + '&method=' + pageAction + '&message=API error (' + jqXHR.status + ')',
                    success: function() {
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        if(jqXHR.status == 401){
                            location.href = 'showcart.asp?section=ValidateOrderOwnership&err=not-logged-in'
                        }
                    }
                });
                $.ajax(ajaxOptions);
                return;
            }
            if(typeof existingError === 'function') {
                existingError(); //data is not defined
            }
        };

        ajaxOptions.complete = function() {
            runHook('postInfoAjaxCompleteFunction', { model: model, ajaxOptions: ajaxOptions, pageAction: pageAction, scrollToId: scrollToId });
            if(typeof existingComplete === 'function') {
                existingComplete();
            }
            model.activeAjaxRequestCount(model.activeAjaxRequestCount() - 1);
            if(model.activeAjaxRequestCount() < 1) {
                model.processing(false);

                /* EJ - 2016-11-14
                   Ugly hack to get around ko.mapping not initializing the Account
                   property correctly when re-mapping on existing object.  I think
                   it may have to do with manually converting the Account property
                   to a validatedObservable the first time through.
                */
                fixUpNulls(model.Account());
            }
            toggleLoadingWidget(false);
        };

        $.ajax(ajaxOptions);
    }

    var fixUpNulls = function(account) {
        for(var prop in account) {
            if(account.hasOwnProperty(prop) && typeof account[prop] === 'function') {
                if(account[prop]() === null || account[prop]() === 'null') {
                    account[prop]('');
                }
            }
        }
    }

    var postPayPal = function(orderActionKey) {
        viewModel.processing(true);

        jQuery.ajax({
            url: 'payment.asp' + '?paypal_order_submit=1&oa_id=' + orderActionKey + '&o_key=' + viewModel.orderKey() + '&randomnum=' + new Date().getTime()
            , data: "o_key=" + viewModel.orderKey()
            , cache: false
            , type: 'POST'
            , dataType: 'json'
            , success: function(data){
				if(!data.status && data.message != ''){
					viewModel.errorMessages.push(
                            {
                                "errorType": "payment_methods",
                                "message": data.message
                            }
                        );
					viewModel.processing(false);
				}
                else if(data.url != ''){
                    document.location = data.url;
                }

            }
            , error: function(data) {
                alert('Error attempting PayPal checkout');
                viewModel.processing(false);
            }
            , complete: function(data) {
            }
        });
    }

    var addFromProductFinder = function(productInfo) {
        viewModel.productFinderSearchTerm('');

        var productInfo = {
            productKey: productInfo.key,
            productSku: productInfo.sku,
            parentChildType: productInfo.parent_child_type,
            parentProductId: productInfo.parent_p_id
        }

        viewModel.loadProductForEdit(productInfo, '');
    }

    var checkOrderTotal = function(model){
        var orderTotalValid = true;
        var subTotal = 0;
        if(ofConfig.useMinimumOrderTotal && !model.completed()){
            if(model.useMultiShipEditUI && model.useMultiShipEditUI()){
                _.each(model.itemShipToMap(), function(item){
                    subTotal += (item.qty() * item.getPrice());
                });
                console.log('itemShipToMap');
            }else if(model.itemsOnOrder){
                _.each(model.itemsOnOrder(), function(item){
                    subTotal += (item.qty() * item.price());
                });
            }else{
                var subTotal = model.productTotal();
            }
            var minimumTotal = ofConfig.minimumOrderTotal;

            if(ofConfig.useAccountMinimumOrderTotal && ofConfig.accountMinimumOrderTotal > 0){
                minimumTotal = ofConfig.accountMinimumOrderTotal;
            }
            if(subTotal < minimumTotal){
                orderTotalValid = false;
            }
        }
        return orderTotalValid;
    }

    $(function() {
        if(getOriginalPageName() !== 'bulk_atc.asp') {
            addGlobalModalCompletionHandler($('#find_product'), addFromProductFinder);
        }

        runHook('cartTemplatesDocumentReady', {self: viewModel});
    });

    addTimer("end of ko model");
</script>
</div>

<script type="text/html" id="cart.aboveOrderActions_custom">
    <!-- ko if: ofConfig.isSuperUserSession && availableProjects().length > 0--> 
        <fieldset class="checkout-review__fieldset u-checkout-well" id="controlgroup_projectpicker">
            <h3 class="checkout-review__fieldset-content u-checkout-well__title">
                Project
            </h3>

            <div class="control-group">
                <label  class="control-label"for="ro_rec_qty"> <strong>Project ID: </strong></label>
                <div class="controls input-append u-flex">
                    <select id="opt1" style="width: auto;" data-bind="value: project, 
                        options: availableProjects, 
                        optionsText: 'name', 
                        optionsValue: 'id',
                        optionsCaption: 'Select a Project...',
                        disable: viewModel.completed()">
                    </select>
                </div>
            </div>
        </fieldset>
    <!-- /ko -->
</script>

<script type="text/html" id="catalog.inventory_custom">
    <!-- ko if: '' == 'impersonate' || '' == 'quote' || isWorkerDomain -->
        <span data-bind="if: inventory.isInventoryItem() && oConfig.useIdp">
            <span data-bind="template: warehouseTemplate"></span>
        </span>
        <div class="sf5-msg" data-bind="if: searchfields.searchfield5.value() != ''">
            <span data-bind="html: searchfields.searchfield5.label()"></span>:
            <span data-bind="html: searchfields.searchfield5.value()"></span>
        </div>
    <!-- /ko -->
    <!-- ko if: '' == '' && oConfig.pageName == 'pc_product_detail.asp' && !isWorkerDomain-->
        <span data-bind="if: inventory.isInventoryItem() && oConfig.useIdp">
            <span data-bind="template: warehouseTemplate"></span>
        </span>
        <!-- ko if: (inventory.inventoryStatus() === 'out' || inventory.inventoryStatus() === 'onorder') -->
            <div class="sf5-msg" data-bind="if: searchfields.searchfield5.value() != ''">
                <span data-bind="html: searchfields.searchfield5.label()"></span>:
                <span data-bind="html: searchfields.searchfield5.value()"></span>
            </div>
        <!-- /ko -->
    <!-- /ko -->

</script>

<script type="text/html" id="warehouse_off_custom">
    <!-- ko if: (inventory.showQty && inventory.showQty()) || !$.isNumeric(inventory.stock()) && ('' == 'impersonate' || '' == 'quote') || isWorkerDomain -->
        <div class="idp-instock__qty" data-bind="html: inventory.stock"></div>
    <!-- /ko -->

    <!-- ko if: inventory.showMessage -->
        <div class="idp-stock__message" data-bind="html: inventory.stockMessage"></div>
    <!-- /ko -->

    <!-- ko if: (inventory.inventoryStatus() === 'out' || inventory.inventoryStatus() === 'onorder') && oConfig.showInventoryWatch && show_inv_watch-->
        <span class="prod-inv-watch">
            <a
                data-bind="
                    html: oConfig.searchConfig.labels.inventoryWatchLinkText,
                    attr: {
                        href: 'prod_inv_watch_add.asp?p_id=' + key() + '&p_nm=' + name() + '&p_sku=' + sku(),
                        title: utils.decodeHTML(oConfig.labels.inventoryWatch),
                        'data-title': oConfig.labels.inventoryWatch
                    },
                    tooltip: {
                        placement: 'top',
                        container: 'body'
                    }"
                class="prod-inv-watch__btn global-modal btn btn-secondary"
                data-size="small"
            ></a>
        </span>
    <!-- /ko -->

    <!-- ko if: (typeof wmPrice != "undefined" && wmPrice()!="") -->
        <div class="wm-message price" data-bind="html: ('WM Price: '+utils.formatPrice(wmPrice()))"></div>
    <!-- /ko -->
    
    <!-- ko if: (typeof nextShip != "undefined" && nextShip()!="") -->
        <div class="wm-message ship" data-bind="html: ('Next Shipment: ' + nextShip())"></div>
    <!-- /ko -->
</script>

<script type="text/html" id="catalog.searchfields_custom">
    <!-- ko if: oConfig.showSearchFields && $data && show() && field() != 'searchfield5' -->
        <dt class="prod-searchfields__label" data-bind="html: label"></dt>
        <dd class="prod-searchfields__value" data-bind="html: value"></dd>
    <!-- /ko -->
</script>

<script type="text/html" id="catalog.selected_product_price_display_custom">
    <span data-bind="if: unitPrice() === 0 && oConfig.showZeroPriceMessage && showPrice && searchfield9().toLowerCase() != 'no'">
        <ul class="unstyled prod-pricing__wrap">
            <li class="retail">
                <span data-bind="text: oConfig.zeroPriceMessage"></span>
            </li>
            <li class="text-small" data-bind="template: { name: priceBreaksTemplate, if: oConfig.useBreaks }"></li>
        </ul>
    </span>
    <span data-bind="if: !(unitPrice() === 0 && oConfig.showZeroPriceMessage) && showPrice && uomPrice() && searchfield9().toLowerCase() != 'no'">
        <ul class="price-display__wrap unstyled">
            <!-- ko if: priceTrace -->
            <li class="text-small">
                <div class="alert alert-info" data-bind="html: priceTrace().replace(/(?:\r\n|\r|\n)/gi,'<br>') "></div>
            </li>
            <!-- /ko -->
            <li class="retail">
                <b data-bind="html: utils.formatPrice(unitPrice())"></b>
                <span class="prod-uom" data-bind="template: 'catalog.uom'"></span>
            </li>
            <!-- ko if: showYouSave -->
                <li class="sretail">
                    <del data-bind="html: utils.formatPrice(suggestedPrice())"></del>
                    <span class="yousave" data-bind="spacedtext: oConfig.labels.youSaveLabel + ' ' + youSavePercent()"></span>
                </li>
            <!-- /ko -->
            <li class="text-small" data-bind="template: { name: priceBreaksTemplate, if: oConfig.useBreaks }"></li>
        </ul>
    </span>
    <!-- ko if: (!showPrice || !showAtc) && oConfig.showPricingOrderEntry -->
        <div class="retail" data-bind="html: $data.mapPriceMessage"></div>
    <!-- /ko -->

    <!-- ko if: !oConfig.showPricingOrderEntry -->
        <div class="hidden-price" data-bind="html: utils.drawHidePriceMessage()"></div>
    <!-- /ko -->

    
    <!-- ko if: searchfield9().toLowerCase() == "no" && searchfield14() != "" -->
        <div class="hidden-price" data-bind="html: searchfield14()"></div>
    <!-- /ko -->

</script>

<script type="text/html" id="cart.confirmation_custom">
    <div id="checkout_confirmation" class="checkout-section u-checkout-well" data-bind="visible: viewModel.showConfirmationMessage ">
        <div class="print-hide print-btn__wrapper">
            <!-- ko template: 'cart.continueShoppingButton'--><!-- /ko -->
            <!-- ko template: 'cart.printButton'--><!-- /ko -->
        </div>
        <span style="color:red">Quote Number: <span data-bind="html:referenceId()"></span></span> 
        <div class="confirmation-alert__wrapper">
            <!-- ko if: viewModel.selectedLifecycleStage().confirmationMessage() != '' -->
            <div clas="confirmation-alert" data-bind="attr: { 'class': 'confirmation-alert-box ' + selectedLifecycleStage().displayClass() }">
                <i class="icon-2x pull-left"></i>
                <h4 style="margin-top: 4px;" data-bind="html: selectedLifecycleStage().confirmationMessage()"></h4>
            </div>
            <!-- /ko -->
            <!-- ko if: viewModel.selectedLifecycleStage().confirmationMessage() == '' -->
            <div class="confirmation-alert">
                <h3 class="text-success" data-bind="visible: completed"><i class="icon-ok"></i> Thank you for your order!</h3>
                <h3 class="text-success" data-bind="visible: !completed() && viewModel.recordType() == 'recurring' "><i class="icon-save"></i> Recurring Order Saved.</h3>
                <h3 class="text-success" data-bind="visible: !completed() && viewModel.lifecycleStage() == 'pending' "><i class="icon-refresh"></i> This order is pending approval.</h3>
                <h3 class="text-success" data-bind="visible: !completed() && viewModel.lifecycleStage() != 'pending' && viewModel.lifecycleStage() != 'recurring' && viewModel.lifecycleStage() != 'cancelled' "><i class="icon-save"></i> This order has been saved.</h3>
                <h3 class="text-error" data-bind="visible: viewModel.lifecycleStage() == 'cancelled' "><i class="icon-remove"></i> This order was rejected.</h3>
            </div>
            <!-- /ko -->
        </div>

        <div class="checkout-section-body">

            <div class="order-number checkout-section-inner well">

                <!-- ko if: completed() && orderNumber() -->
                <h3>Your order number is&nbsp;<span data-bind="text: ofConfig.orderNumberPrefix"></span><span data-bind="text: orderNumber"></span></h3>
                <!-- /ko -->
                <div class="row-fluid checkout-section__row">

                    <div class="span3 checkout-section__span" data-bind="if: ofConfig.showPaymentMethodsSection">
                        <!-- ko template: 'cart.paymentMethodSummary' --><!-- /ko -->
                    </div>

                    <div class="span3 checkout-section__span">
                        <div id="controlgroup_invoice_ponumber_summary" class="control-group checkout-section__summary-wrapper" data-bind="if: poNumber">
                            <div class="checkout-section__summary">
                                <b for="ponumber_summary" class="po-label" data-bind="html: ofConfig.POLabel"></b>
                                <div class="controls">
                                    <span id="ponumber_summary" class="" data-bind="text: poNumber"></span>
                                </div>
                            </div>
                        </div>
                        <div id="controlgroup_invoice_comments_summary" class="control-group checkout-section__summary-wrapper" data-bind="if: comments">
                            <div class="checkout-section__summary">
                                <label for="order_comments_summary" class="control-label">
                                    <strong data-bind="text: ofConfig.fulfillmentCommentsText"></strong>
                                </label>
                                <div class="controls">
                                    <div id="order_comments_summary" data-bind="text: comments"></div>
                                </div>
                            </div>
                        </div>
                    </div>

                </div>
            </div>
        </div>
    </div>
</script>

<script type="text/html" id="catalog.atc_form_submit_custom">
		<div id="atc_large" class="atc-large">
			<!-- ko if: atcErrorText() != "" -->
				<div class="alert alert-error" data-bind="html:atcErrorText"></div>
			<!-- /ko -->
            <div data-bind="visible: !fromWorkerPortal()">
                <div data-bind="template: 'catalog.product_quantity'"></div>
                <div class="media-body">
                    <div class="btn-group">
                        
                            <button
                                type="button"
                                class="btn btn-large btn-primary product-detail-atc__button"
                                data-bind="
                                    click: addToCart,
                                    html: atcButtonText()"
                            ></button> 
                    </div>
                    <div data-bind="template: { name: 'catalog.saved_cart_form_submit', if: oConfig.useSavedCarts }"></div>
                </div>
            </div>
		</div>
		<div data-bind="template: { name: 'catalog.qty_restrictions', if: hasQuantityRestrictions }"></div>
</script>
<script type="text/html" id="catalog.paging_custom">
   <select id="catalogPagingSelect" style="width: 100px; padding: 5px; font-size: 14px;"></select>
</script>


<script type="text/html" id="cart.itemCard_custom">

        <tr class="item-card" data-bind="attr: { 'data-test' : 'shipment-item__' + sku() }">

            <td class="item-card__cart-item">
                <b class="tablesaw-cell-label">Description</b>
                <figure class="item-card__cart-item-img" data-bind="visible: !oConfig.fastTrack && parentProductID().trim() && hasParentInCart()"></figure>
                <figure class="item-card__prod-info-wrapper">
                    <img data-bind="attr : { alt: sku() + ' - ' + name(),
                                    src: utils.buildImagePath(thumbnail()) },
                                    visible: !oConfig.fastTrack && (!parentProductID().trim() || !hasParentInCart() )"
                        class="item-card__prod-info-img"
                        onerror="utils.handleImageError(this)">

                    <figcaption class="item-card__prod-text-wrapper">
                          <a  data-size='large'  style="text-decoration: none; color: black;" class="global-modal" data-bind="attr: { href: 'pc_product_detail.asp?key=' + productID() + '&modal=1&modalaction=addProduct&quote=1', title: utils.decodeHTML(name()), 'data-key': productID() }, html: name"></a>


                        <!-- ko if : promoID -->
                        <span class="item-card__promo-icon promo-icon" data-bind="attr: { 'title' : promoDescription() } ">P</span>
                        <!-- /ko -->

                        <div class="item-card__prod-sku text-xsmall muted" data-bind="text: sku, visible: !parentProductID().trim() || !hasParentInCart()"></div>

                        <p data-bind="if: ofConfig.AllowWarehouse && productWarehouse">
                            <span data-bind="html: ofConfig.warehouseLabel + ' ' + productWarehouse.name()"></span>
                            <span data-bind="if: viewModel.allowWarehouseSelection() && !viewModel.useMultiShipEditUI()">
                                <!-- ko template: { name: 'cart.productEditLink', data: { title: 'Change Warehouse', linkText: 'Change Warehouse', detailLine: $data } } --><!-- /ko -->
                            </span>
                        </p>
                        <!-- ko template: { name: 'cart.backorderMessage', if: ofConfig.showBackorderMessage } --><!-- /ko -->
                        <!-- ko template: 'cart.productLeadTimes' --><!-- /ko -->
                        <!-- ko template: { name: 'cart.softgoods', if: ofConfig.showSoftGoodAuthorizations } --><!-- /ko -->

                        <div class="item-card__prod-edit" data-bind="if: viewModel.allowWarehouseSelection() && !viewModel.useMultiShipEditUI">
                            <!-- ko template: { name: 'cart.productEditLink', data: { title: 'Change Warehouse', linkText: 'Change Warehouse', detailLine: $data } } --><!-- /ko -->
                        </div>

                        <!-- ko template: { name: 'cart.additionalDetailLineInfo', data: $data } --><!-- /ko -->
                    </figcaption>
                </figure>
            </td>

            <td class="item-card__price">
                <b class="tablesaw-cell-label">Unit Price</b>
                <span class="item-card__original-price" data-bind="html: ofConfig.showPricingOrderEntry ? instanceUnitPriceDisplay : utils.drawHidePriceMessage()"></span>
            </td>

            <!-- ko if :  viewModel.useMultiShipEditUI() || $data.qtyDisplayType() =='text' || !viewModel.allowProductAdds() && !$data.editing() -->
                <td class="cart-item-price__qty">
                    <b class="tablesaw-cell-label">Qty</b>
                    <label>
                        <input class="cart-item-price__qty-input" type="text" data-bind="attr: { value: viewModel.getTotalQty(instance()) }" disabled/>
                        <!-- ko if:ofConfig.showUom -->
                            <span class="cart-item-price__uom" data-bind="text: $data.uom"></span>&nbsp;
                        <!-- /ko -->
                    </label>
                </td>
            <!-- /ko -->

            <!-- ko if :  !viewModel.useMultiShipEditUI() && $data.qtyDisplayType() =='hide' -->
                <td class="cart-item-price__qty">&nbsp;</td>
            <!-- /ko -->

            <!-- ko if : !viewModel.useMultiShipEditUI() && $data.qtyDisplayType() =='input' &&  viewModel.allowProductAdds() && !$data.editing()-->
                <td class="cart-item-price__qty">
                    <b class="tablesaw-cell-label">Qty</b>
                    <div class="cart-item-price__qty-wrapper">
                        <input type="number" class="cart-item-price__qty-input"
                            data-bind="
                                value: qty,
                                attr: {
                                    'id' : 'qty-ship-' + orderDetailKey(),
                                    'min' : minQty() || oConfig.defaultMinimumQuantity,
                                    'step' : qtyIncrement() || oConfig.defaultQuantityIncrement,
                                    'max' : maxQty() || undefined,
                                    'data-test' : 'shipment-item-qty__' + sku()
                                },
                                validateQty
                            ">
                        <label class="cart-item-price__qty-label">
                            <!-- ko if:ofConfig.showUom -->
                                <span class="cart-item-price__uom" data-bind="text: $data.uom"></span>
                            <!-- /ko -->
                            <span
                                class="cart-item-price__order-detail-key"
                                data-bind="
                                    attr:
                                    {
                                        'data-key' : orderDetailKey()
                                    },
                                    visible:  hasQuantityRestrictions">
                                <a href="#0"
                                    class="cart-item-price__info-btn"
                                    data-trigger="hover"
                                    data-html="true"
                                    data-placement="top"
                                    data-bind="popover, attr: {
                                        'data-content': quantityRestrictionsHtml
                                    }">
                                    <i class="fas fa-question-circle"></i>
                                </a>
                            </span>
                        </label>
                    </div>
                </td>
            <!-- /ko -->

            <!-- ko if: viewModel.recordType() === 'recurring' && !editing()-->
            <td class="item-card__order-items-recurring">
                <b class="tablesaw-cell-label">Qty</b>
                <div class="item-card__order-items-label visible-xs visible-sm">Schedule</div>
                <!-- ko template: { name: 'cart.recurringLineDetails' } --><!-- /ko -->
            </td>
            <!-- /ko -->

            <td class="item-card__total">
                <b class="tablesaw-cell-label">Total</b>
                <div class="item-card__cart-ext-price" data-bind="html: ofConfig.showPricingOrderEntry ? utils.formatMoney((priceBeforeAdjustment() || price()) * viewModel.getTotalQty(instance())) : utils.drawHidePriceMessage()"></div>
            </td>

            <!-- ko if:$data.editing() && viewModel.mainProduct() -->
                <td class="item-card__edit">
                    <!-- ko template: { name: 'catalog.detail_info', data:viewModel.mainProduct } --><!-- /ko -->

                    <div class="item-card__child-selector-wrapper">
                        <span class="item-card__child-selector" data-bind="template: { name: 'catalog.child_selectors', if: viewModel.mainProduct().childSelectors, data: viewModel.mainProduct().childSelectors }"></span>
                    </div>

                    <!-- ko template: { name:'catalog.atc_full', data:viewModel.mainProduct().selectedProduct } --><!-- /ko -->
                </td>
            <!-- /ko -->

            <td class="item-card__prod-edit prod-edit">
                <!-- ko if: viewModel.showRemoveLink($data) -->
                    <a class="prod-edit__del-btn text-danger" role="button" data-bind="event: { click: viewModel.removeProduct }, attr: { 'data-test' : 'shipment-item-del__' + sku() }, visible: !editing()" title="Remove this product from your Order?">
                        Remove
                    </a>
                <!-- /ko -->

                <!-- ko if: viewModel.allowProductAdds() && ofConfig.allowProductEditInEditView -->
                    <!-- ko template: { name: 'cart.productInlineEditLinks' } --><!-- /ko -->
                <!-- /ko -->

            </td>
        </tr>

        <tr class="config-options__row -no-border-top" data-bind="if: configuratorJson.choices">
            <td class="config-options__column" colspan="5">
                <!-- ko template: { name: 'cart.configOptions', data: $data } --><!-- /ko -->
            </td>
        </tr>

        <tr id="cartAdvSettings" class="item-card__adv-settings collapse -adv-settings">
            <td class="item-card__adv-settings-cell"></td>
            <td class="item-card__adv-settings-cell">
                    <b class="tablesaw-cell-label">Change Price</span>&nbsp;<a href="javascript:void(0);" class="price-helper__help-tip-tooltip" data-bind="tooltip:{}" title="Change any input below and all other inputs will update automatically.">

                    <i class="fas fa-question-circle"></i></a></b>
                <!-- ko if : viewModel.superUserOrderFormMode() && ofConfig.superUserAllowProductPriceEdits && $data.qtyDisplayType() == 'input' && !$data.editing() -->
                    <!-- ko if: ofConfig.showSUPriceControls && (!isNaN(commodity.cost()) || !isNaN(commodity.retailPrice())) -->
                        <!-- ko template: 'cart.priceHelper' --><!-- /ko -->
                    <!-- /ko -->
                <!-- /ko -->
            </td>
            <td class="item-card__adv-settings-cell">
                <b class="tablesaw-cell-label">Change Qty Limits</span>&nbsp;<a href="javascript:void(0);" class="qty-ctrl-settings__help-tip-tooltip" data-bind="tooltip:{}" title="Customers can change the qty, but only within these limits.">

                <i class="fas fa-question-circle"></i></a></b>
                <!-- ko if : viewModel.allowQtyControls() -->
                    <!-- ko template: 'cart.qtyControlSettings' --><!-- /ko -->
                <!-- /ko -->
            </td>
            <td class="item-card__adv-settings-cell"></td>
            <td class="item-card__adv-settings-cell"></td>
        </tr>

</script>

<script type="text/html" id="catalog.product_view_custom">
	<!-- ko template: { name: "catalog.product_title", data: selectedProduct } --><!-- /ko -->
	<div id="product_view" class="row-fluid product_view">
		<div data-bind="attr: { class: oConfig.detailConfig.gridLeftColumnClass + ' product-detail__left' }">
			<!-- ko if: !oConfig.detailConfig.showMainImage -->
				<!-- ko template: 'catalog.detail_info_container' --><!-- /ko -->
			<!-- /ko -->
			<!-- ko if: oConfig.detailConfig.showMainImage -->
				<div id="product_images" data-bind="template: { name: 'catalog.image_gallery', data: selectedProduct }"></div>
			<!-- /ko -->
		</div>
		<div data-bind="attr: { class: oConfig.detailConfig.gridRightColumnClass  + ' product-detail__right'}">
			<!-- ko if: !oConfig.detailConfig.showMainImage -->
				<span data-bind="template: { name: 'catalog.smart_lists', data: selectedProduct() }"></span>
			<!-- /ko -->
			<!-- ko if: oConfig.detailConfig.showMainImage -->
				<!-- ko template: 'catalog.detail_info_container' --><!-- /ko -->
			<!-- /ko -->
		</div>
			 	<button type="button"
				class="prod-card__atc btn btn-primary btn-cart-add"
				data-bind="
					html: atcButtonText(),
					click: addToCart,
					enable: isQtyValid(),
                    visible: (new URLSearchParams(window.location.search)).get('modal') === '1'
				">
			</button>
	</div>
	<!-- ko template: 'catalog.additional_info' --><!-- /ko -->
</script>

<script type="text/html" id="catalog.atc_qty_input_custom">
    <div class="prod-actions" data-bind="template: 'catalog.product_actions', visible: (!isWorkerDomain || '' == 'quote')"></div>
</script>

<script type="text/html" id="catalog.product_card_custom">
	<div class="prod-card">
		<!-- ko if: isActiveLayout('gallery') || oConfig.pageName == 'pc_product_detail.asp' -->
			<div class="prod-thumb" data-bind="visible: oConfig.searchConfig.showThumb, css: { '-using-flags': oConfig.usingFlags } ">
				<span class="prod-thumb-img" data-bind="template: 'catalog.image_link'"></span>
				<span class="prod-thumb-flags" data-bind="template: 'catalog.flags_thumb'"></span>
			</div>

			<div class="prod-info">
				<div class="prod-desc">
					<div class="prod-nm">
						<!-- ko template: 'catalog.name_link' --><!-- /ko -->
					</div>
					<!-- ko template: 'catalog.rating_score' --><!-- /ko -->
				</div>
				<div class="prod-idp" data-bind="template: 'catalog.inventory', visible: inventory.isInventoryItem() && oConfig.useIdp"></div>
				<!-- ko if: $data.isDangerous -->
					<!-- ko template: 'catalog.hazmat' --><!-- /ko -->
				<!-- /ko -->
				<!-- ko template: { name: 'catalog.product_account_history_short',data: accountHistory, if: accountHistory && accountHistory.accountName }--><!-- /ko -->
				<div class="prod-pricing" data-bind="template: { name: priceDisplay, data: selectedProduct } "></div>
				<!-- ko if: oConfig.showChildrenSelection && $data.children -->
					<div class="prod-children" data-bind= "template:{
						name: 'catalog.children_selection',
						if: oConfig.showChildrenSelection && $data.children && children().length
					}"></div>
				<!-- /ko -->
				<div class="prod-actions" data-bind="template: 'catalog.product_actions', visible: (!isWorkerDomain || '' == 'quote')"></div>
				<div class="prod-details">
					<!-- ko template: { name: 'catalog.detail_info' } --><!-- /ko -->
				</div>
				<!-- ko if: utils.getParameter('favorites') && oConfig.pageName == 'pc_combined_results.asp' -->
					<!-- ko template: 'catalog.remove_fav_button' --><!-- /ko -->
				<!-- /ko -->
			</div>
		<!-- /ko -->
		<!-- ko if: isActiveLayout('list') -->
			<div class="prod-thumb" data-bind="visible: oConfig.searchConfig.showThumb, css: { '-using-flags': oConfig.usingFlags } ">
				<span class="prod-thumb-img" data-bind="template: 'catalog.image_link'"></span>
				<span class="prod-thumb-flags" data-bind="template: 'catalog.flags_thumb'"></span>
			</div>
			<div class="prod-info">
				<div class="prod-desc">
					<div class="prod-nm">
						<!-- ko template: 'catalog.name_link' --><!-- /ko -->
					</div>
					<div class="prod-ds" data-bind="
						html: selectedProduct().description,
						visible: oConfig.searchConfig.showDescription && selectedProduct().description()">
					</div>
					<!-- ko template: 'catalog.rating_score' --><!-- /ko -->
					<div class="prod-details">
						<!-- ko template: { name: 'catalog.detail_info' } --><!-- /ko -->
					</div>
				</div>
			</div>
			<!-- ko template: { name: 'catalog.product_account_history_short',data: accountHistory, if: accountHistory && accountHistory.accountName }--><!-- /ko -->
			<div class="prod-atc">
				<div class="prod-idp" data-bind="template: 'catalog.inventory'"></div>
				<div class="prod-pricing" data-bind="template: { name: priceDisplay, data: selectedProduct } "></div>
				<!-- ko if: oConfig.showChildrenSelection && $data.children -->
					<div class="prod-children" data-bind= "template:{
						name: 'catalog.children_selection',
						if: oConfig.showChildrenSelection && $data.children && children().length
					}"></div>
				<!-- /ko -->
				<div class="prod-actions" data-bind="template: 'catalog.product_actions', visible: (!isWorkerDomain || '' == 'quote')"></div>
			</div>
			<!-- ko if: utils.getParameter('favorites') && oConfig.pageName == 'pc_combined_results.asp' -->
				<!-- ko template: 'catalog.remove_fav_button' --><!-- /ko -->
			<!-- /ko -->
		<!-- /ko -->
	</div>
</script>

<script type="text/html" id="catalog.layout_custom">
    <div data-bind="if: oConfig.searchConfig.showLayout && !(utils.getParameter('modalaction')=='addProduct')">
        <span data-bind="html: oConfig.searchConfig.labels.layout"></span>
        <div class="btn-group btn-group-small layout-control__wrap">
            <button
                type="button"
                id="gallery_layout_toggle"
                data-bind="
                    click: function () { setLayout('gallery') },
                    css: { active: isActiveLayout('gallery') },
                    html: oConfig.searchConfig.labels.galleryView
                "
                class="btn btn-default"
            ></button>
            <button
                type="button"
                id="list_layout_toggle"
                data-bind="
                    click: function () { setLayout('list') },
                    css: { active: isActiveLayout('list') },
                    html: oConfig.searchConfig.labels.listView
                "
                class="btn btn-default"
            ></button>
        </div>
        <div class="results-download" data-bind="template: { if: oConfig.searchConfig.showDownload && oConfig.isLoggedIn}">
            <a href='javascript: window.location = utils.pageUrl + (utils.pageUrl.indexOf("?")>0?"&":"?") + "downloadasfile=1";' class='cust_download' title='Download'>
                <img data-bind="attr: { src: utils.buildImagePath('common_images/ddt_download.png') }" border="0" align="absmiddle">
                Download
            </a>
        </div>
    </div>
</script>

<script type="text/html" id="catalog.recently_viewed_custom">
    <div id="recently_viewed_products"
                class="clearfix detail_related recently-viewed"
                data-bind="if: oConfig.detailConfig.useRecentlyViewedProducts && $data && $data.length > 0 && oConfig.searchConfig.pageType != 'favlist' && utils.getParameter('pc_id') != '0B6C8C4D168242CA866AA73DF34AAFF8'">
        <span data-bind="if: oConfig.centerRecentlyViewed">
            <h2 class="recently-viewed__title -centered" data-bind="html: oConfig.detailConfig.labels.recentlyViewedProducts"></h2>
            <span class="recently-viewed__gallery -centered" data-bind="template: 'catalog.simple_product_gallery'"></span>
        </span>
        <span data-bind="if: !oConfig.centerRecentlyViewed">
            <h2 class="recently-viewed__title" data-bind="html: oConfig.detailConfig.labels.recentlyViewedProducts"></h2>
            <span class="recently-viewed__gallery" data-bind="template: 'catalog.simple_product_gallery'"></span>
        </span>
    </div>
</script>

<script type="text/html" id="catalog.name_link_custom">
    <a class="detail_link" data-bind="attr: { href: getPersistantQuery(link()), title: utils.decodeHTML(name()), 'data-key': key }, html: name"></a>
</script>

    <script>
        document.addEventListener('DOMContentLoaded', function() {

            var totalPages = (oConfig.searchConfig ? Math.ceil(oConfig.searchConfig.total / oConfig.searchConfig.rpp) : 0);
            var customPages = [];
            for (var i = 1; i <= totalPages; i++) {
                customPages.push({ value: i, text: i });
            }

            var selectElement = document.getElementById('catalogPagingSelect');
            if(selectElement){
                customPages.forEach(function(page) {
                    var option = document.createElement('option');
                    option.value = page.value;
                    option.textContent = page.text;
                    selectElement.appendChild(option);
                });

                selectElement.addEventListener('change', function() {
                    var selectedPage = this.value;
                    window.location.href = setParameter('page', selectedPage);
                });

            }

            function setParameter(name, value) {
                var url = new URL(window.location.href);
                url.searchParams.set(name, value);
                return url.toString();
            }
        })
    </script>


<script>
    function newQuote() {
        if(confirm("Leave this quote as-is and start a new quote for this customer?")){
            try{
                $('#appLoadingWidget').show();
                $.ajax({ 
                    url: 'showcart.asp'
                    , type: 'GET'
                    , dataType: 'json'
                    , data: {	
                        pageaction : 'newquotecustom'
                        , modal: 1
                    } 
                    ,success: function(data){
                        debugger;
                        window.location.href = "payment.asp?o_key="+data.id;
                    }
                    ,complete: function(data) {
                        $('#appLoadingWidget').hide();     
                    }
                });
            } catch (err){}

        }

    }

    //modalaction=addProduct - override
    function getActiveLayout() {
		var layout = null;
		if(oConfig.pageName === 'pc_combined_results.asp'){
			var defaultLayout = oConfig.searchConfig.layout;
			var cookieLayout;
			var categoryLayout;
            if(utils.getParameter("modalaction")=="addProduct"){
                cookieLayout = "list";
            }
			else if(oConfig.searchConfig.showLayout){
				cookieLayout = utils.getCookie('productLayout');
			}
			if(viewModel.category){
				if(viewModel.category.defaultView != ''){
					categoryLayout = viewModel.category.defaultView;
				}
			}
			//Priority = cookie > category > default
			layout = cookieLayout || categoryLayout || defaultLayout;
		}else{
			layout = null;
		}
		return layout;
	}

    registerHook({
        name: 'productModelBottom',
        func: function (oArgs) {
            var self = oArgs.self;

            if (self.searchfield9().toLowerCase() == 'no') {
                self.atcButtonText("Request Quote");
            }

            self.fromWorkerPortal = ko.computed(function() {
                return window.location.href.indexOf('mycim') > -1;
            });
        }
    });

    registerHook({
        name: 'shipmentModelBottom',
        func: function (oArgs) {
            var self = oArgs.self;

            if (viewModel) {
                viewModel.updateProducts();

                viewModel.checkCartRestrictions();                
            }
        }
    });

    registerHook({
        name: 'ItemsOnOrderArrayBeforeReturn',
        func: function (oArgs) {
            var itemsOnOrder = oArgs.detailLines;

            if (viewModel) {
                viewModel.updateProducts();
            }
        }
    });

    registerHook({
        name: 'orderModelBottom',
        func: function (oArgs) {
            var self = oArgs.self;

            self.checkCartRestrictions = function() {
                // Check if the order can be placed. This could happen when an order updates the product prices.
                $.ajax({
                    url: 'i_i_payment.asp',
                    type: 'GET',
                    dataType:'json',
                    async: false,
                    data: {
                        ajax: 'checkCartRestrictions',
                        o_key: self.orderKey()
                    }
                });
            }

            self.checkCartRestrictions();

            self.updatePriceDisplay = function(detailLine, prod) {
                // If searchfield14 does not exist, create it as an observable
                if (detailLine.searchfield14 === undefined) {
                    detailLine.searchfield14 = ko.observable(prod.searchfield14);
                } else {
                    // If searchfield14 already exists, update its value
                    detailLine.searchfield14(prod.searchfield14);
                }

                // Update the price display if searchfield9 = no AND the price = 0 (price has not been overridden)
                if (detailLine.searchfield9().toLowerCase() == "no" && detailLine.instanceUnitPrice() == 0) {
                    detailLine.instanceUnitPriceDisplay = ko.computed(function(){
                        if (detailLine.searchfield14() != "") {
                            priceDisplay = detailLine.searchfield14();
                        } else {
                            priceDisplay = "-";
                        }

                        return priceDisplay;
                    });
                }
            }

            self.updateProducts = function() {
                // This is handled in project_picker_hooks.asp with the other ajax handlers
                $.ajax({
                    url: 'i_i_payment.asp',
                    type: 'GET',
                    dataType:'json',
                    async: false,
                    data: {
                        ajax: 'getProdFields',
                        o_key: self.orderKey()
                    },
                    success: function(response) {
                        if (!response) {
                            // No products on the order yet
                            return;
                        }
                        
                        response.forEach(function(prod) {
                            // Find the corresponding item in the order details by sku
                            var detailLineIndex = self.detailLines().findIndex(function(detail) {
                                return detail.sku() === prod.sku.trim();
                            });

                            // If a corresponding item is found, add or update the searchfield14 property
                            if (detailLineIndex !== -1) {
                                var detailLine = self.detailLines()[detailLineIndex];
                                
                                self.updatePriceDisplay(detailLine, prod);
                                
                                // Update the item in the observable array to trigger UI updates
                                self.detailLines().splice(detailLineIndex, 1, detailLine);
                            }

                            // We also have to update the details on the shipments. Yes, this is mostly duplicated code.
                            self.shipments().forEach(function(shipment) {
                                // Find the corresponding item in the shipment details by sku
                                var shipDetailLineIndex = shipment.details().findIndex(function(detail) {
                                    return detail.orderDetail.sku() === prod.sku.trim();
                                });

                                // If a corresponding item is found, add or update the searchfield14 property
                                if (shipDetailLineIndex !== -1) {
                                    var shipDetailLine = shipment.details()[shipDetailLineIndex];
                                    
                                    self.updatePriceDisplay(shipDetailLine.orderDetail, prod);
                                
                                    // Update the item in the observable array to trigger UI updates
                                    shipment.details().splice(shipDetailLineIndex, 1, shipDetailLine);
                                }
                            });
                        });
                    }
                });
            }

            if (ofConfig.isSuperUserSession) {

                var projects = [];

                $.ajax({
                    url: 'i_i_payment.asp',
                    type: 'GET',
                    dataType:'json',
                    async: false,
                    data: {
                        ajax: 'getProjectList',
                        o_key: self.orderKey(),
                        accountID: ofConfig.AccountKey
                    },
                    success: function(response) {
                        projects = response;
                    }
                });

                self.availableProjects = ko.observableArray(projects)

                self.project = ko.observable();    

                if (self.opt1()) {
                    self.project(self.opt1());
                }

                // Create subscribe to observable
                self.project.subscribe(function(projectID) {
                    if(projectID === undefined) {
                        projectID = ""
                    }
                    self.opt1(projectID);
                    $.ajax("i_i_payment.asp?ajax=updateProject&projectId=" + projectID + "&o_key=" + self.orderKey());
                });

            }

            self.updateProducts();

            if(recalc){ // <-- toggled true if saved-cart older than 30 days
                toggleLoadingWidget(true);
                utils.popToastr("Quote Pricing", "Your quote's prices are being refreshed...", { "positionClass" : "toast-top-center" });
                $.ajax({
                    url: "i_i_add_to_cart.asp?ajax=updatequote&custom=1&modal=1&o_key="+self.orderKey(),
                    success: function(data){
                        var response = JSON.parse(data);
                        if(Array.isArray(response) && Object.keys(response[0].Errors).length > 0) {
                            for (var msg in response[0].Errors) {
                                if(response[0].Errors.hasOwnProperty(msg)) {                                    
                                    viewModel.errorMessages.push(
                                        {
                                            "errorType": msg,
                                            "message": response[0].Errors[msg]
                                        }
                                    );
                                }
                            }
                        }
                        ko.mapping.fromJS(response[1], orderMapping, viewModel);
                        viewModel.updateOrderAccess(); 
                        
                    },
                    complete: function(){
                        toggleLoadingWidget(false);
                    }
                });
            }
        }
    });

    registerHook({
        name: 'processUniversalSearchUrl',
        func: function (oArgs) {
            var sProductSearchTerm = 'products';
            var isWorkerDomain = false;
            var sSearchType = jQuery('#u_search_type option:selected').text().toLowerCase();

            if (isWorkerDomain && sSearchType == sProductSearchTerm) {
                oArgs.url = oArgs.url + '&u_search_type=' + jQuery('#u_search_type option:selected').text();
            }
        }
    });

    function getPersistantQuery(link){
        //&search_keyword=CONV-0490&u_search_type=Products
        var url = 'search_keyword=' + utils.getParameter("search_keyword");
        url += '&u_search_type=' + utils.getParameter("u_search_type");
        if(link){
            if(link.includes('?')){
                url = link +'&' + url;
            } else {
                url = link + '?' + url;
            }

        }
        return url;

    }

    //override
    function redirectToProduct(product) {
		hideSidebar()
		var url = product.link
        var qsChar = "";

        if(url.includes('?')) {
            qsChar = '&';
        } else {
            qsChar = '?';
        }

		if(oConfig.isModal && !url.includes("modal=1")) {
			url += qsChar + 'modal=1&modalaction=' + utils.getParameter("modalaction");
            qsChar = "&";
		}

        url += qsChar + getPersistantQuery();

		if (window.location.replace) {
			window.location.replace(url);
		} else {
			window.location.href = url;
		}

		//Add a breadcrumb for this scenario.
		var productKey = product.key;
		var defaultHomeLink = "default.asp";
		var defaultBreadcrumb = [{ name: 'Home', link: defaultHomeLink }];
		var productBreadcrumb = defaultBreadcrumb;
		productBreadcrumb.push({
			name: product.name,
			link: ''
		});
		var breadcrumbObj = utils.getStoredBreadcrumbs() || {breadcrumbs: {}};
		breadcrumbObj.breadcrumbs[productKey] = productBreadcrumb;
		utils.setStoredBreadcrumbs(breadcrumbObj);
	}
</script>
    </body>

</html>
