Giter Site home page Giter Site logo

mivaecommerce / readytheme-colossus Goto Github PK

View Code? Open in Web Editor NEW
6.0 6.0 0.0 25.51 MB

The Colossus ReadyTheme is designed with the larger catalog store in mind.

Home Page: https://colossus.mivareadythemes.com/

License: MIT License

HTML 77.75% CSS 12.70% JavaScript 9.55%
miva readytheme shadows elements wcag-a wcag-aa section-508

readytheme-colossus's People

Contributors

influxweb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

readytheme-colossus's Issues

Product in basket is being removed instead of the quantity being updated.

Provide a general summary of the issue in the Title above

Expected Behavior

On the basket page. When I input a number into the quantity field it should update the product's quantity.

Current Behavior

On the basket page. When I input a number into the quantity field it should update the product's quantity. But instead removes it from the basket.

Steps to Reproduce (for bugs)

Video of the steps:
https://drive.google.com/file/d/1aHushPOM3o_Xz6u2TrxmrZBYx8zcExmn/view

  1. Add a product to your basket with a quantity of "1". Link to product https://colossus.mivareadythemes.com/y5-iperespresso-espresso-coffee-machine.html
  2. On the BASK page, focus on the quantity input field and enter "2". This should remove the product from the basket.

*Note The hidden field action within the form is using the value of "RGRP" instead of "QTYG". Using "QTYG" should fix the issues.

  • Version used:
  • Chrome 75
  • macOS Mojave 10.14.5

PROD: Disappearing Thumbnails

Due to a buq with Attribute Machine, when clicking on a subscription option, it triggers the variant_change event however, it does not trip Image Machine like when you click on other attributes. This would cause the thumbnail element to update to opacity: 0; but not be updated after Image Machine ran to opacity: 1;.

Correcting the Issue

I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to find the Product Imagery functionality in theme.js and update with the following:

User Interface -> JavaScript Resources -> theme:

/**
 * Product Imagery
 * This set of functions control the creation and operation of the product image gallery carousels.
 */
let productThumbnails = document.querySelector('[data-hook="product-thumbnails"]');
let debouncedThumbnailSlider = $.debounced(function () {
    if ($(productThumbnails).hasClass('slick-initialized')) {
        $(productThumbnails).css('opacity', 0).removeClass('slick-slider slick-vertical slick-initialized');
    }

    $(productThumbnails).on('init', function (event, slick) {
        $(event.target).css('opacity', 0);
        setTimeout(function () {
            $(event.target).css('opacity', 1);
        }, 50);
    });

    $(productThumbnails).not('.slick-initialized').slick({
        draggable: false,
        infinite: false,
        slidesToScroll: 5,
        slidesToShow: 5,
        vertical: true,
        verticalSwiping: true,
        responsive: [
            {
                breakpoint: 768,
                settings: {
                    slidesToScroll: 4,
                    slidesToShow: 4,
                    vertical: false,
                    verticalSwiping: false
                }
            }
        ]
    });
}, 250);


/**
 * Update Display When Attribute Machine Fires
 */
MivaEvents.SubscribeToEvent('variant_changed', function () {
});

PROD: Show More links repeat

When the screen is resized, the Show More links may become duplicated and make for a confusing layout.

Correcting the Issue

This issue is due to there not being a reset to remove previously added toggles. I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to find the peekaboo() function in theme.js and update with the following:

User Interface -> JavaScript Resources -> theme:

/**
 * This is the Show/Hide functionality for the Show More links in the
 * product description section.
 */
(function () {
    'use strict';

    function vw(v) {
        let w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
        return (v * w) / 100;
    }

    let showMoreSections = document.querySelectorAll('[data-hook="show-more"]');

    function peekaboo (reset) {
        if (showMoreSections.length > 0) {
            if (reset) {
                document.querySelectorAll('[data-hook="show-more-toggle"]').forEach(function (toggle) {
                    toggle.parentNode.removeChild(toggle);
                });
                return;
            }

            showMoreSections.forEach(function (showMore) {
                if (showMore.clientHeight > vw(30)) {
                    let showMoreToggle = document.createElement('button');
                    let showContent = '<span class="t-show-more__toggle-text">See More <span class="u-icon-chevron-down u-font-small"></span></span>';
                    let lessContent = '<span class="t-show-more__toggle-text">See Less <span class="u-icon-chevron-up u-font-small"></span></span>';

                    showMoreToggle.innerHTML = showContent;
                    showMoreToggle.classList.add('t-show-more__toggle');
                    showMoreToggle.setAttribute('data-hook', 'show-more-toggle');
                    showMore.classList.add('t-show-more');
                    showMore.appendChild(showMoreToggle);

                    showMoreToggle.addEventListener('click', function (clickEvent) {
                        clickEvent.preventDefault();
                        showMore.classList.toggle('t-show-more--active');
                        if (showMoreToggle.innerHTML === showContent) {
                            showMoreToggle.innerHTML = lessContent;
                        }
                        else {
                            showMoreToggle.innerHTML = showContent;
                        }
                    });
                }

            });
        }
    }
    peekaboo();

    let peekabooTimeout;

    window.addEventListener('resize', function () {
        peekaboo(true);
        if (peekabooTimeout) {
            window.cancelAnimationFrame(peekabooTimeout);
        }

        peekabooTimeout = window.requestAnimationFrame(function () {
            peekaboo();
        });
    }, false);

}());

WLGN - Not a smooth process

When adding a product to your wish list from either the product page or your basket, and you are not logged into your account, the logic of WLGN page is not optimal.

Expected Behavior

When creating an account, you should be returned back to your point of origin so you can add the product(s) to your wish list.

Correcting the Issue

To improve the experience, the code for WLGN needs to be update. I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code on WLGN:

User Interface -> Pages -> WLGN -> Template:

<mvt:item name="html_profile" />
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<base href="&mvt:global:basehref;">
	<mvt:if expr="NOT ISNULL l.settings:page:title">
		<title>&mvt:page:title;</title>
	<mvt:else>
		<title>&mvt:store:name;: &mvt:page:name;</title>
	</mvt:if>
	<mvt:item name="head" param="css_list" />
	<mvt:item name="head" param="head_tag" />
</head>
<body id="js-&mvte:page:code;" class="o-site-wrapper t-page-&mvt:global:pageClass;">
	<mvt:item name="hdft" param="global_header" />

	<section class="o-layout">
		<div class="o-layout__item">
			<mvt:item name="hdft" param="header" />
		</div>
	</section>

	<section class="o-layout">
		<div class="o-layout__item u-width-12">
			<section class="t-expanded-block t-account-landing-section u-bg-white">
				<div class="o-layout__item t-expanded-block__item t-account-landing-section__header u-width-12">
					<mvt:item name="readytheme" param="contentsection( 'messages' )" />
					<div class="o-layout o-layout--wide o-layout--justify-around u-text-center">
						<div class="o-layout__item u-width-12 u-width-6--l u-width-5--w t-customer-profile">
							<div class="t-account-landing-section__header">
								<span class="c-heading-delta t-account-landing-section__heading">Current Customer?</span>
								<p class="u-font-small">Please sign in to view your wish lists.</p>
							</div>
							<form class="u-inline-block u-text-left" method="post" action="&mvte:urls:WISH:secure;" autocomplete="off">
								<fieldset>
									<legend>&mvt:page:name;</legend>
									<mvt:if expr="ISNULL g.Action">
										<mvt:assign name="g.Action" value="'LOGN,ATWL'" />
									<mvt:else>
										<mvt:assign name="g.Action" value="'LOGN,' $ g.Action" />
									</mvt:if>
									<input type="hidden" name="Action" value="&mvte:global:Action;" />
									<input type="hidden" name="Quantity" value="&mvte:global:Quantity;" />
									<input type="hidden" name="Line_ID" value="&mvte:global:Line_ID;" />
									<input type="hidden" name="Group_ID" value="&mvte:global:Group_ID;" />
									<input type="hidden" name="Wish_ID" value="&mvte:global:Wish_ID;" />
									<input type="hidden" name="Product_Code" value="&mvte:global:Product_Code;" />
									<mvt:assign name="l.settings:attribute_index" value="0" />
									<mvt:foreach iterator="attribute" array="global:Product_Attributes">
										<mvt:assign name="l.settings:attribute_index" value="l.settings:attribute_index + 1" />
										<input type="hidden" name="Product_Attributes[ &mvt:attribute_index; ]:code" value="&mvte:attribute:code;" />
										<input type="hidden" name="Product_Attributes[ &mvt:attribute_index; ]:value" value="&mvte:attribute:value;" />
										<mvt:if expr="l.settings:attribute:template_code NE 0">
											<input type="hidden" name="Product_Attributes[ &mvt:attribute_index; ]:template_code" value="&mvte:attribute:template_code;" />
										</mvt:if>
									</mvt:foreach>
									<ul class="c-form-list">
										<li class="c-form-list__item">
											<label class="c-form-label u-font-small u-text-bold u-text-uppercase is-required &mvt:global:invalid_credentials;" for="l-Customer_LoginEmail">Email Address</label>
											<input id="l-Customer_LoginEmail" class="c-form-input" type="email" name="Customer_LoginEmail" value="&mvte:global:Customer_LoginEmail;" autocomplete="email" required>
										</li>
										<li class="c-form-list__item">
											<label class="c-form-label u-font-small u-text-bold u-text-uppercase is-required &mvt:global:invalid_credentials;" for="l-Customer_Password">Password:</label>
											<mvt:if expr="g.Customer_Temporary_Password">
												<input type="hidden" name="Customer_Temporary_Password" value="&mvte:global:Customer_Temporary_Password;">
												<input id="l-Customer_Password" class="c-form-input" type="password" name="Customer_Password" value="&mvte:global:Customer_Temporary_Password;" required>
											<mvt:else>
												<input id="l-Customer_Password" class="c-form-input" type="password" name="Customer_Password" autocomplete="current-password" required>
											</mvt:if>
										</li>
										<li class="c-form-list__item u-text-right">
											<mvt:item name="buttons" param="Login" />
										</li>
									</ul>
								</fieldset>
							</form>
							<p class="u-font-small"><a data-mini-modal data-mini-modal-type="inline" data-mini-modal-content="data-forgot-password" href="&mvte:urls:FPWD:secure;" title="Forgot Password">Forgot your password?</a></p>
							<br>
						</div>
						<div class="o-layout__item u-width-12 u-width-6--l u-width-5--w">
							<div class="t-account-landing-section__header">
								<span class="c-heading-delta t-account-landing-section__heading">No Account? No Problem&hellip;</span>
								<p class="u-font-small">Create an account and add products to your wish list.</p>
							</div>
							<form class="u-inline-block u-text-left" method="post" action="&mvte:urls:CACT:secure;" autocomplete="off">
								<fieldset>
									<legend>Create Account</legend>
									<mvt:if expr="'MAWL' CIN g.Action OR 'MPWL' CIN g.Action">
										<input type="hidden" name="current_location" value="&mvte:urls:BASK:secure_sep;">
									<mvt:else>
										<input type="hidden" name="current_location" value="&mvte:urls:PROD:secure_sep;Product_Code=&mvte:global:Product_Code;&">
									</mvt:if>
									<ul class="c-form-list">
										<li class="c-form-list__item">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required" for="l-register_fname">First Name</label>
											<input id="l-register_fname" class="c-form-input c-form-input--large" type="text" name="register_fname" value="" autocomplete="name given-name" required>
										</li>
										<li class="c-form-list__item">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required" for="l-register_lname">Last Name</label>
											<input id="l-register_lname" class="c-form-input c-form-input--large" type="text" name="register_lname" autocomplete="name family-name" required>
										</li>
										<li class="c-form-list__item">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required" for="l-register_email">Email Address</label>
											<input id="l-register_email" class="c-form-input c-form-input--large" type="email" name="register_email" value="&mvte:global:register_email;" autocomplete="email" required>
										</li>
										<li class="c-form-list__item">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required" for="l-register_password">Password</label>
											<input id="l-register_password" class="c-form-input c-form-input--large" type="password" name="register_password" autocomplete="new-password" required>
										</li>
										<li class="c-form-list__item u-text-right">
											<input class="c-button c-button--full c-button--hollow c-button--huge u-bg-white u-color-gray-50 u-font-small u-text-bold u-text-uppercase" type="submit" value="Create My Account">
										</li>
									</ul>
								</fieldset>
							</form>
							<br>
						</div>
					</div>
			</section>
		</div>
	</section>
	
	<section class="o-layout">
		<div class="o-layout__item">
			<mvt:item name="hdft" param="footer" />
			<mvt:item name="readytheme" param="contentsection( 'forgot-password' )" />
		</div>
	</section>

	<mvt:item name="hdft" param="global_footer" />
</body>
</html>

Refinery: Small Amount of Facets Not Displaying

When there are less facets to display on a page than the maximum defined to display, they will not display.

Correcting the Issue

In the refinery function, the conditional for display only checks for a greater-than. I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update refinery with the following:

User Interface -> JavaScript Resources -> refinery:

/**
 +-+-+-+-+-+-+-+-+
 |r|e|f|i|n|e|r|y|
 +-+-+-+-+-+-+-+-+
 *
 * This extension will display only the facets which will fit on the screen,
 * all others are hidden from view but can been seen via an off-canvas element.
 * Additionally, it will identify which facet sets have active selections and
 * display the active facets in a list below the main facet container. The
 * active facets can be clicked on to clear them as well as there being a
 * "Clear All" button.
 */

(function ($, window) {
	'use strict';

	let $facets = $.hook('refinery');
	let $refinerySetToggles = $.hook('refinery-set-toggle');
	let $triggerArea = $.hook('refinery__view-target');
	let $displayButton = $.hook('refinery__view-button');
	let $visibleLinks = $.hook('refinery__list');
	let $hiddenLinks = $.hook('refinery__overflow');
	let $annex = $.hook('refinery-annex');
	let numOfItems = 0;
	let totalSpace = 176;
	let breakWidths = [];
	let availableSpace;
	let numOfVisibleItems;
	let requiredSpace;
	let rangeSliders = document.querySelectorAll('[data-mm-facet-rangeslider-name]');
	let minDisplayWidth = 1040;
	let maxDisplay;
	let numOfVisibleItems2;

	/**
	 * Check the total space required for the facet sets and how many there are.
	 */
	if ($visibleLinks) {
		$visibleLinks.children().each(function () {
			totalSpace += $(this).outerWidth(true) + (parseInt($visibleLinks.css('padding-left')));
			numOfItems += 1;
			breakWidths.push(totalSpace);
		});
	}

	/**
	 * Compare the needed space with the available space and move breadcrumbs accordingly.
	 * @returns {jQuery}
	 */
	$.fn.checkFacetOverflow = function () {
		if ($facets) {
			if (window.innerWidth >= minDisplayWidth && window.innerWidth < 1173) {
				maxDisplay = 3;
			}
			else if (window.innerWidth >= 1173) {
				maxDisplay = 4;
			}
			else {
				maxDisplay = 2;
			}

			availableSpace = $visibleLinks.outerWidth(true) - parseInt($visibleLinks.css('padding-right'));
			numOfVisibleItems = $visibleLinks.children().length;
			requiredSpace = breakWidths[numOfVisibleItems - 1];
			numOfVisibleItems2 = $visibleLinks.children('[data-hook="refinery-set"]').length;

			if (numOfVisibleItems2 > maxDisplay) {
				// There is not enough space
				$visibleLinks.children('[data-hook="refinery-set"]').last().prependTo($hiddenLinks);
				numOfVisibleItems -= 1;
				$facets.addClass('is-loaded');
				this.checkFacetOverflow();
			}
			else if (numOfVisibleItems2 <= maxDisplay) {
				$facets.addClass('is-loaded');
				this.checkFacetOverflow();
			}

			// Update the button accordingly
			if ($hiddenLinks.children().length > 0) {
				$triggerArea.removeClass('u-hidden');
			}
			else {
				$triggerArea.addClass('u-hidden');
			}

			if (rangeSliders.length > 0) {
				MMFacet_RangeSlider_Initialize();
			}

			return this;
		}
	};


	/**
	 * Toggle the visibility of the refinery-annex
	 * @private
	 */
	let toggleAnnex = function (event) {
		event.preventDefault();
		// Prevent window scrolling
		$(document).toggleClass('u-overflow-hidden');
		// Open the annex element
		$annex.toggleClass('x-refinery-annex--open');
	};


	$.fn.refinery = function () {
		/**
		 * This allows for only one refinery set to be open at a time.
		 */
		$refinerySetToggles.on('change', function () {
			$refinerySetToggles.not(this).prop('checked', false);
		});

		/**
		 * This closes all refinery sets if you click outside of the refinery section.
		 */
		$(document).on('click', function (documentClick) {
			if (!$visibleLinks.is(documentClick.target) && $visibleLinks.has(documentClick.target).length === 0) {
				$refinerySetToggles.prop('checked', false);
			}
		});

		/**
		 * Check for resize and control button click.
		 */
		let initElement = this;
		let refineryTimeout;

		window.addEventListener('resize', function () {
			if (refineryTimeout) {
				window.cancelAnimationFrame(refineryTimeout);
			}

			refineryTimeout = window.requestAnimationFrame(function () {
				initElement.checkFacetOverflow();
				$.hook('open-refinery-annex').off('click');
				$.hook('close-refinery-annex').off('click');
				$annex.off('click');
			});
		}, false);

		// Element.matches() Polyfill
		if (!Element.prototype.matches) {
			Element.prototype.matches = Element.prototype.msMatchesSelector;
		}

		// Open the annex when clicking the trigger
		$.hook('open-refinery-annex').on('click', function (event) {
			$refinerySetToggles.prop('checked', false);
			toggleAnnex(event);
			if (typeof rangeSlidersAnnex !== 'undefined') {
				setTimeout(function () {
					MMFacet_RangeSlider_Initialize();
				}, 200);
			}
		});

		// Close the annex when clicking any 'close' triggers
		$.hook('close-refinery-annex').on('click', function (event) {
			toggleAnnex(event);
		});

		// Close the annex when clicking on the background.
		$annex.on('click', function (event) {
			if (event.target === this) {
				toggleAnnex(event);
			}
		});

		// Close the annex when the `Esc` key is pressed
		/*$(document).keydown(function (event) {
			let key = event.key || event.keyCode;

			if (key === 'Escape' || key === 27) {
				toggleAnnex(event);
			}
		});*/

		initElement.checkFacetOverflow();

	};

	/**
	 * Initialize the extension.
	 */
	if ($facets.length > 0 || $annex.length > 0) {
		$facets.refinery();


		/**
		 * This function will set the class of any filter set with an active selection to `is-active`.
		 * @type {NodeListOf<Element>}
		 */
		let filterSets = document.querySelectorAll('[data-hook="refinery-selections"]');

		for (let filterID = 0; filterID < filterSets.length; filterID++) {
			let filterSetActive = filterSets[filterID].querySelector('.is-selected');
			let filterSetSelect = filterSets[filterID].querySelector('select');
			let filterSetSelectActive;

			if (filterSetSelect) {
				filterSetSelectActive = filterSetSelect.selectedIndex;
			}

			if ((filterSetActive && !filterSetSelect) || (filterSetSelect && filterSetSelectActive >= 1)) {
				filterSets[filterID].previousElementSibling.classList.add('is-active');
			}
		}

		let selectedFilters = document.querySelector('[data-hook="selected-filters"]');

		/**
		 * This function will create trigger the clearing of the separate, selected filters.
		 * @type {Element}
		 */
		if (selectedFilters) {
			window.scrollTo(0, document.querySelector('[data-hook="refinery"]').getBoundingClientRect().top + window.scrollY);

			let filterTags = selectedFilters.querySelectorAll('[data-hook="filter-tag"]');

			for (let id = 0; id < filterTags.length; id++) {
				let filter = filterTags[id];

				filter.addEventListener('click', function (event) {
					event.preventDefault();

					let cancelledFilter = event.target;
					let filterCode = cancelledFilter.getAttribute('data-facet-code');
					let filterType = cancelledFilter.getAttribute('data-facet-type');

					if (filterType === 'checkbox' || filterType === 'radio') {
						let filterItem = document.querySelectorAll('input[type=' + filterType + '][name=' + filterCode + ']');

						for (let radios = 0; radios < filterItem.length; radios++) {
							if (filterItem[radios].checked === true) {
								filterItem[radios].checked = false;
								MMProdList_UpdateQuery(filterItem[radios]);
							}
						}
						return true;
					}
					else if (filterType === 'select') {
						let filterItem = document.querySelector('select[name=' + filterCode + ']');

						filterItem.value = '';
						MMProdList_UpdateQuery(filterItem);
						return true;
					}
					else if (filterType === 'rangeslider') {
						let filterItem = document.querySelector('input[name=' + filterCode + ']');

						filterItem.value = '';
						MMProdList_UpdateQuery(filterItem);
						return true;
					}

				});
			}
		}
	}

})(jQuery, window);

PROD: Swatch Select Active Class Not Working

The first thumbnail is 'highlighted' and uses the 'active' class. But if you select any other option, the active class does not move - it stays with the first thumbnail.

Expected Behavior

When selecting a different swatch, the active state should switch to that swatch.

Correcting the Issue

This appears to be an intermittent issue. However, a solution has been created which you can use regardless if you are experiencing the issue I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code in User Interface -> PROD -> Attribute Machine -> Head Template:

AttributeMachine.prototype.Generate_Discount = function (discount) {
	var discount_div;

	discount_div = document.createElement('div');
	discount_div.innerHTML = discount.descrip + ': ' + discount.formatted_discount;

	return discount_div;
};

AttributeMachine.prototype.Generate_Swatch = function (product_code, attribute, option) {
	var swatch_container = document.querySelector('#&mvt:attributemachine:swatch_element_id;');
	var swatch = document.createElement('li');
	var img = document.createElement('img');

	img.src = option.image;
	img.setAttribute('alt', option.prompt);
	img.setAttribute('title', option.prompt);
	swatch.classList.add('o-list-inline__item');
	swatch.setAttribute('data-code', option.code);
	swatch.setAttribute('data-color', option.prompt);
	swatch.appendChild(img);

	setTimeout(function () {
		if (swatch_container) {
			var swatch_element = swatch_container.querySelector('ul');
			var swatch_select = document.querySelector('[data-hook="attribute-swatch-select"]');
			var swatch_selected = swatch_select.options[swatch_select.selectedIndex].text;
			var swatch_name_element = document.querySelector('[data-hook="attribute-swatch-name"]');
			var swatchElements = swatch_element.querySelectorAll('li');

			swatch_element.removeAttribute('style');
			swatch_element.classList.add('o-list-inline');
			/**
			 * Adds the selected swatch name to the label.
			 */
			swatch_name_element.textContent = swatch_selected;

			/**
			 * Adds an active class to the selected swatch.
			 */
			swatchElements.forEach(function (swatchElement) {
				var swatchColor = swatchElement.getAttribute('data-code');
				var swatchImage = swatchElement.querySelector('img');

				swatchImage.classList.remove('x-product-layout-purchase__swatches--active');
				if (swatchColor === swatch_select.options[swatch_select.selectedIndex].value) {
					swatchImage.classList.add('x-product-layout-purchase__swatches--active');
				}
			});
		}
	}, 0);

	return swatch;
};

AttributeMachine.prototype.Swatch_Click = function(input, attribute, option) {
	var swatch_name_element = document.querySelector('[data-hook="attribute-swatch-name"]');
	var i;

	for (i = 0; i < input.select.options.length; i++) {
		if (input.select.options[i].value === option.code) {
			input.select.selectedIndex = i;
		}
	}

	if (attribute.inventory) {
		this.Attribute_Changed(input);
	}

	swatch_name_element.innerHTML = option.prompt;
	this.Generate_Swatch(input, attribute, option);
};

PROD: Original/Regular Price Not Showing

Expected Behavior

If a product is on sale, the original price should show regardless if the product has attributes/variants or not.

Current Behavior

If a product is on sale, the original price only shows if the product has attributes/variants.

Correcting the Issue

For non-variant products, the token &mvt:product:formatted_base_price; was missing from the Product Display Layout. I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code in User Interface -> PROD -> Product Display Layout:

<div class="t-expanded-block u-bg-white" data-hook="product-content">
	<div class="o-wrapper t-wrapper">
		<section class="o-layout o-layout--wide x-product-layout">
			<div class="o-layout__item u-width-12 u-hidden--l">
				<br>
				<p class="x-product-layout-purchase__name">
					<span class="x-product-layout-purchase__sku">
						<mvt:if expr="NOT ISNULL l.settings:product:sku">
							SKU: &mvt:product:sku;
						<mvt:else>
							SKU: &mvt:product:code;
						</mvt:if>
					</span>
					<span>&mvt:product:name;</span>
				</p>
				<br>
			</div>
			<div class="o-layout__item u-text-center x-product-layout-images u-width-12 u-width-6--l u-width-7--w">
				<mvt:if expr="NOT ISNULL l.settings:product:customfield_values:customfields:product_label AND l.settings:product:customfield_values:customfields:product_label NE 'none'">
					<span class="x-product-label u-bg-black u-color-white u-font-tiny u-text-uppercase">&mvt:product:customfield_values:customfields:product_label;</span>
				</mvt:if>
				<div class="x-product-imagery">
					<div id="thumbnails" class="x-product-imagery__thumbnail-list" data-hook="product-thumbnails"></div>
					<figure class="x-product-imagery__showcase">
						<img id="main_image" class="x-product-layout-images__image" data-hook="product-image" data-index="0" data-mini-modal data-mini-modal-content="data-hook=photo-gallery-template" data-mini-modal-type="inline" src="graphics/en-US/cssui/blank.gif" alt="&mvte:product:name;" title="&mvte:product:name;">
					</figure>
				</div>
				<mvt:item name="product_display_imagemachine" param="body_deferred:product:id"/>
				<mvt:item name="readytheme" param="thirdpartysharing" />
			</div>
			<!-- end .x-product-layout-images -->

			<form class="o-layout__item x-product-layout-purchase u-width-12 u-width-6--l u-width-5--w" data-hook="purchase" action="&mvte:urls:BASK:auto;" method="post" name="add">
				<input type="hidden" name="Old_Screen" value="&mvte:global:Screen;" />
				<input type="hidden" name="Old_Search" value="&mvte:global:Search;" />
				<input type="hidden" name="Action" value="ADPR" />
				<input type="hidden" name="Product_Code" value="&mvte:product:code;" />
				<input type="hidden" name="Category_Code" value="&mvte:global:category_code;" />
				<input type="hidden" name="Offset" value="&mvte:global:Offset;" />
				<input type="hidden" name="AllOffset" value="&mvte:global:AllOffset;" />
				<input type="hidden" name="CatListingOffset" value="&mvte:global:CatListingOffset;" />
				<input type="hidden" name="RelatedOffset" value="&mvte:global:RelatedOffset;" />
				<input type="hidden" name="SearchOffset" value="&mvte:global:SearchOffset;" />
				<mvt:comment>
					<!--
						This `fieldset` element has been commented out due to a potential bug in the attribute machine
						JavaScript which can cause the form functions to not load properly in Internet Explorer and Edge.
					-->
				<fieldset>
				</mvt:comment>
					<legend>Purchase &mvt:product:name;</legend>
					<ul class="c-form-list">
						<li class="c-form-list__item c-form-list__item--full u-hidden u-block--l">
							<h1 class="x-product-layout-purchase__name">
								<span class="x-product-layout-purchase__sku">
									<mvt:if expr="NOT ISNULL l.settings:product:sku">
										SKU: &mvt:product:sku;
									<mvt:else>
										SKU: &mvt:product:code;
									</mvt:if>
								</span>
								<span itemprop="name">&mvt:product:name;</span>
							</h1>
						</li>
						<li class="c-form-list__item c-form-list__item--full">
							<p class="x-product-layout-purchase__pricing">
								<span class="x-product-layout-purchase__pricing-current">
									<span id="price-value" itemprop="price" content="&mvt:product:price;">&mvt:product:formatted_price;</span>
								</span>
								<span class="x-product-layout-purchase__pricing-original">
									<span id="price-value-additional" data-prompt="regular:"><mvt:if expr="l.settings:product:base_price GT l.settings:product:price">&mvt:product:formatted_base_price;</mvt:if></span>
								</span>
							</p>
							<div id="product-discounts" class="x-product-layout-purchase__product-discounts u-font-small">
								<mvt:foreach iterator="discount" array="product:discounts">
									<p class="x-product-layout-purchase__product-discounts">
										&mvt:discount:descrip;: <strong>&mvt:discount:formatted_discount;</strong>
									</p>
								</mvt:foreach>
							</div>

							<mvt:comment>
								<!-- If you would like to display the product weight, uncomment this section. -->
								<mvt:if expr="l.settings:product:weight NE 0">
									<p class="x-product-layout-purchase__product-weight u-font-small">
										Shipping Weight: <strong>&mvt:product:weight;</strong> &mvt:store:wtunits;
									</p>
								</mvt:if>
							</mvt:comment>

							<mvt:if expr="l.settings:product:inv_active OR l.settings:attributemachine:product:inv_active">
								<link itemprop="availability" href="http://schema.org/&mvt:product:inv_short;">
							</mvt:if>
							<div id="inventory-message" class="x-product-layout-purchase__inventory-message"><mvt:if expr="l.settings:product:inv_active OR l.settings:attributemachine:product:inv_active">&mvt:product:inv_long;</mvt:if></div>

							<mvt:comment>
								<!-- If you would like to display the product quantity in basket, uncomment this section. -->
								<p class="x-product-layout-purchase__product-quantity u-font-tiny">
									Quantity in Basket:&nbsp;
									<mvt:if expr="l.settings:product:quantity EQ 0">
										<em>None</em>
									<mvt:else>
										<strong>&mvt:product:quantity;</strong>
									</mvt:if>
								</p>
							</mvt:comment>

						</li>
						<mvt:item name="discount_volume" param="product:id" />
						<li class="o-layout c-form-list__item x-product-layout-purchase__options">
							<mvt:item name="product_attributes" param="product:id" />
						</li>
						<li class="c-form-list__item c-form-list__item--full x-product-layout-purchase__cta">
							<div class="x-product-layout-purchase__message" data-hook="purchase-message"></div>
							<div class="o-layout">
								<mvt:if expr="l.settings:subscription:enabled AND l.settings:subscription:mandatory">
									<mvt:if expr="l.settings:product:inv_level NE 'out'">
										<div class="o-layout__item c-control-group x-product-layout-purchase__options-quantity">
											<div class="c-control-group__field t-product-layout-purchase__quantity">
												<label class="c-form-label u-text-bold u-font-small u-color-gray-40 is-required" for="l-quantity">qty</label>
												<input id="l-quantity" class="c-form-input u-text-center u-color-gray-40" type="tel" name="Quantity" value="1">
											</div>
											<span class="t-product-layout-purchase__add-to-cart" onclick="document.forms.add.action = '&mvtj:urls:BASK:auto;'; document.forms.add.elements.Action.value = 'ADPR';">
												<mvt:item name="buttons" param="AddToBasket"/>
											</span>
										</div>
									</mvt:if>
								<mvt:else>
									<div class="o-layout__item c-control-group x-product-layout-purchase__options-quantity">
										<div class="c-control-group__field t-product-layout-purchase__quantity">
											<label class="c-form-label u-text-bold u-font-small u-color-gray-50 is-required" for="l-quantity">qty</label>
											<input id="l-quantity" class="c-form-input u-text-center u-color-gray-50" type="tel" name="Quantity" value="1">
										</div>
										<mvt:if expr="l.settings:product:inv_level NE 'out'">
											<span class="t-product-layout-purchase__add-to-cart" onclick="document.forms.add.action = '&mvtj:urls:BASK:auto;'; document.forms.add.elements.Action.value = 'ADPR';">
												<mvt:item name="buttons" param="AddToBasket"/>
											</span>
										</mvt:if>
										<span class="t-product-layout-purchase__add-to-wish" data-mmnodisable="true">
											<span onclick="document.forms.add.action = '&mvtj:urls:WISH:secure;'; document.forms.add.elements.Action.value = 'ATWL';">
												<mvt:item name="buttons" param="AddToWishList"/>
											</span>
										</span>
									</div>
								</mvt:if>
							</div>
						</li>
					</ul>
				<mvt:comment>
				</fieldset>
				</mvt:comment>
			</form>
		</section>
	</div>
	<div class="x-photo-gallery-template" data-hook="photo-gallery-template"></div>
</div>

<section class="o-layout x-product-description">
	<div class="o-layout__item">
		<article class="t-product-description__article" data-hook="show-more">
			<header>
				<p class="c-heading-delta">Description</p>
			</header>
			&mvt:product:descrip;
		</article>

		<mvt:if expr="NOT ISNULL l.settings:product:customfield_values:customfields:shipping_returns">
			<article class="t-product-description__article" data-hook="show-more">
				<header>
					<p class="c-heading-delta">&mvte:customfield_names:customfields:shipping_returns;</p>
				</header>
				&mvt:product:customfield_values:customfields:shipping_returns;
			</article>
		</mvt:if>

		<mvt:item name="customfields" param="Read_Product_ID(l.settings:product:id, '', l.settings:product:specs)" />
		<mvt:assign name="l.settings:has_specs" value="0"/>
		<mvt:foreach iterator="spec" array="product:specs">
			<mvt:if expr="('coffee_' CIN l.settings:spec:code AND l.settings:spec:value NE '') OR ('tea_' CIN l.settings:spec:code AND l.settings:spec:value NE '')">
				<mvt:assign name="l.settings:has_specs" value="1"/>
			</mvt:if>
		</mvt:foreach>
		<mvt:if expr="NOT ISNULL l.settings:product:customfield_values:customfields:specifications OR l.settings:has_specs NE 0">
			<article class="t-product-description__article" data-hook="show-more">
				<header>
					<p class="c-heading-delta">Specifications</p>
				</header>
				&mvt:product:customfield_values:customfields:specifications;
				<table class="c-table-responsive">
					<mvt:foreach iterator="spec" array="product:specs">
						<mvt:if expr="('coffee_' CIN l.settings:spec:code AND l.settings:spec:value NE '') OR ('tea_' CIN l.settings:spec:code AND l.settings:spec:value NE '')">
							<tr class="c-table-simple__row">
								<td class="c-table-responsive__cell c-table-responsive__cell--wide u-color-gray-50 u-font-tiny u-text-medium u-text-uppercase">&mvte:spec:name;</td>
								<td class="c-table-responsive__cell c-table-responsive__cell--wide u-font-large u-text-bold">&mvte:spec:value;</td>
							</tr>
						</mvt:if>
					</mvt:foreach>
				</table>
			</article>
		</mvt:if>

	</div>
</section>

XSS Vulnerability Found

There is a global variable which adds a class to hide header and navigation elements when you are on a checkout page; this allows for the "distraction free" checkout process we introduced in Shadows. Unfortunately, the output of the variable is not entity-encoded which could open an page up for exploit.

Correcting the Issue

The quickest way to correct this issue is to install the Template Search and Replace Module, search for &mvt:global:checkout_hidden;, and replace it with &mvte:global:checkout_hidden; then search for &mvt:global:checkout_shown;, and replace it with &mvte:global:checkout_shown;. I have added this update and it will be included in the next maintenance release.

XSS Vunerability Found

Current Behavior

When creating a quick account, there is a possible XSS error.

Correcting the Issue

I have added some additional data scrubbing to the inputs on CACT. I have added this update, it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code in User Interface -> Pages -> CACT -> Template:

<mvt:assign name="l.settings:new_customer:login" value="''" />
<mvt:assign name="l.settings:new_customer:pw_email" value="miva_html_strip(g.register_email, '')" />
<mvt:assign name="l.settings:new_customer:password" value="miva_html_strip(g.register_password, '')" />
<mvt:assign name="l.settings:new_customer:pgrpcount" value="0" />
<mvt:assign name="l.settings:new_customer:ship_fname" value="miva_html_strip(g.register_fname, '')" />
<mvt:assign name="l.settings:new_customer:ship_lname" value="miva_html_strip(g.register_lname, '')" />
<mvt:assign name="l.settings:new_customer:saved_password" value="l.settings:new_customer:password" />
<mvt:do file="g.Module_Feature_CUS_DB" name="l.settings:test" value="Customer_Load_Email(l.settings:new_customer:pw_email, l.settings:existing_customer)" />

<mvt:if expr="g.current_location">
	<mvt:assign name="g.return_link" value="miva_html_strip(g.current_location, '')" />
<mvt:else>
	<mvt:assign name="g.return_link" value="l.settings:urls:SFNT:rr_sep" />
</mvt:if>

<mvt:if expr="l.settings:existing_customer:id GT 0">
	<mvt:comment>USER EXISTS</mvt:comment>
	<mvt:assign name="g.Customer_Password" value="g.register_password" />
	<mvt:assign name="g.Customer_LoginEmail" value="g.register_email" />
	<mvt:do file="g.Module_Feature_CUS_RT" name="l.settings:login_success" value="Action_Customer_Login()" />
	&mvte:global:MvDO_Error;
	<mvt:if expr="g.Customer_Login_Invalid EQ 1 OR g.Customer_Password_Invalid EQ 1">
	<mvt:else>
		<meta http-equiv="refresh" content="0;url=&mvte:global:return_link;logon=1" />
	</mvt:if>
<mvt:else>
	<mvt:do file="g.module_library_utilities" name="g.is_valid_email" value="Email_Validate(l.settings:new_customer:pw_email)" />
	<mvt:do file="g.Module_Feature_CUS_DB" name="l.settings:testPW" value="CustomerSettings_Load(l.customersettings)" />
	<mvt:do file="g.Module_Admin" name="g.is_valid_pw" value="Validate_Password(l.customersettings, l.settings:new_customer:password)" />
	<mvt:assign name="g.invalidEmailMessage" value="crypto_base64_encode('You have entered an invalid email address.')" />
	<mvt:assign name="g.invalidPasswordMessage" value="crypto_base64_encode(g.Validation_Message)" />
	<mvt:if expr="g.is_valid_email EQ 1 AND g.is_valid_pw EQ 1">
		<mvt:do file="g.Module_Feature_CUS_UT" name="l.settings:test" value="CustomerLogin_Generate_Email(l.settings:new_customer:pw_email, l.settings:new_customer:login)" />
		<mvt:do file="g.Module_Feature_CUS_DB" name="l.settings:test" value="Customer_Insert(l.settings:new_customer)" />
		<mvt:assign name="g.Customer_Password" value="l.settings:new_customer:saved_password" />
		<mvt:assign name="g.Customer_LoginEmail" value="l.settings:new_customer:pw_email" />
		<mvt:do file="g.Module_Feature_CUS_RT" name="l.settings:login_success" value="Action_Customer_Login()" />
		&mvte:global:MvDO_Error;
		<meta http-equiv="refresh" content="0;url=&mvte:global:return_link;registration=1&NewAccount=1&Customer_ShipFirstName=&mvte:global:register_fname;" />
	<mvt:elseif expr="g.is_valid_email EQ 0 AND g.is_valid_pw EQ 1">
		<meta http-equiv="refresh" content="0;url=&mvte:global:return_link;registration=0&iem=&mvte:global:invalidEmailMessage;" />
	<mvt:elseif expr="g.is_valid_email EQ 1 AND g.is_valid_pw EQ 0">
		<meta http-equiv="refresh" content="0;url=&mvte:global:return_link;registration=0&ipm=&mvte:global:invalidPasswordMessage;" />
	<mvt:else>
		<meta http-equiv="refresh" content="0;url=&mvte:global:return_link;registration=0&iem=&mvte:global:invalidEmailMessage;&ipm=&mvte:global:invalidPasswordMessage;" />
	</mvt:if>
</mvt:if>

Facets - Clearing of individual values is not working.

Expected Behavior

When clicking on a facet button, that value should clear from the list of selected facets.

Current Behavior

When clicking on a facet button, they refresh the page but don't do anything.

Correcting the Issue

It appears there was some errant code in the refinery.js file. I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code in User Interface -> JavaScript Resources -> refinery:

/**
 +-+-+-+-+-+-+-+-+
 |r|e|f|i|n|e|r|y|
 +-+-+-+-+-+-+-+-+
 *
 * This extension will display only the facets which will fit on the screen,
 * all others are hidden from view but can been seen via an off-canvas element.
 * Additionally, it will identify which facet sets have active selections and
 * display the active facets in a list below the main facet container. The
 * active facets can be clicked on to clear them as well as there being a
 * "Clear All" button.
 */

(function ($, window) {
	'use strict';

	let $facets = $.hook('refinery');
	let $refinerySetToggles = $.hook('refinery-set-toggle');
	let $triggerArea = $.hook('refinery__view-target');
	let $visibleLinks = $.hook('refinery__list');
	let $hiddenLinks = $.hook('refinery__overflow');
	let $annex = $.hook('refinery-annex');
	let numOfItems = 0;
	let totalSpace = 176;
	let numOfVisibleItems;
	let rangeSliders = document.querySelectorAll('[data-mm-facet-rangeslider-name]');
	let minDisplayWidth = 1040;
	let maxDisplay;

	/**
	 * Check the total space required for the facet sets and how many there are.
	 */
	if ($visibleLinks) {
		$visibleLinks.children().each(function () {
			totalSpace += $(this).outerWidth(true) + (parseInt($visibleLinks.css('padding-left')));
			numOfItems += 1;
		});
	}

	/**
	 * Compare the needed space with the available space and move breadcrumbs accordingly.
	 * @returns {jQuery}
	 */
	$.fn.checkFacetOverflow = function () {
		if ($facets) {
			if (window.innerWidth >= minDisplayWidth && window.innerWidth < 1173) {
				maxDisplay = 3;
			}
			else if (window.innerWidth >= 1173) {
				maxDisplay = 4;
			}
			else {
				maxDisplay = 2;
			}

			numOfVisibleItems = $visibleLinks.children('[data-hook="refinery-set"]').length;

			if (numOfVisibleItems > maxDisplay) {
				// There is not enough space
				$visibleLinks.children('[data-hook="refinery-set"]').last().prependTo($hiddenLinks);
				$facets.addClass('is-loaded');
				this.checkFacetOverflow();
			}
			else if (numOfVisibleItems <= maxDisplay) {
				$facets.addClass('is-loaded');
			}

			// Update the button accordingly
			if ($hiddenLinks.children().length > 0) {
				$triggerArea.removeClass('u-hidden');
			}
			else {
				$triggerArea.addClass('u-hidden');
			}

			if (rangeSliders.length > 0) {
				MMFacet_RangeSlider_Initialize();
			}

			return this;
		}
	};


	/**
	 * Toggle the visibility of the refinery-annex
	 * @private
	 */
	let toggleAnnex = function (event) {
		event.preventDefault();
		// Prevent window scrolling
		$(document).toggleClass('u-overflow-hidden');
		// Open the annex element
		$annex.toggleClass('x-refinery-annex--open');
	};


	$.fn.refinery = function () {
		/**
		 * This allows for only one refinery set to be open at a time.
		 */
		$refinerySetToggles.on('change', function () {
			$refinerySetToggles.not(this).prop('checked', false);
		});

		/**
		 * This closes all refinery sets if you click outside of the refinery section.
		 */
		$(document).on('click', function (documentClick) {
			if (!$visibleLinks.is(documentClick.target) && $visibleLinks.has(documentClick.target).length === 0) {
				$refinerySetToggles.prop('checked', false);
			}
		});

		/**
		 * Check for resize and control button click.
		 */
		let initElement = this;
		let refineryTimeout;

		window.addEventListener('resize', function () {
			if (refineryTimeout) {
				window.cancelAnimationFrame(refineryTimeout);
			}

			refineryTimeout = window.requestAnimationFrame(function () {
				initElement.checkFacetOverflow();
				$.hook('open-refinery-annex').off('click');
				$.hook('close-refinery-annex').off('click');
				$annex.off('click');
			});
		}, false);

		// Element.matches() Polyfill
		if (!Element.prototype.matches) {
			Element.prototype.matches = Element.prototype.msMatchesSelector;
		}

		// Open the annex when clicking the trigger
		$.hook('open-refinery-annex').on('click', function (event) {
			$refinerySetToggles.prop('checked', false);
			toggleAnnex(event);
			if (typeof rangeSlidersAnnex !== 'undefined') {
				setTimeout(function () {
					MMFacet_RangeSlider_Initialize();
				}, 200);
			}
		});

		// Close the annex when clicking any 'close' triggers
		$.hook('close-refinery-annex').on('click', function (event) {
			toggleAnnex(event);
		});

		// Close the annex when clicking on the background.
		$annex.on('click', function (event) {
			if (event.target === this) {
				toggleAnnex(event);
			}
		});

		initElement.checkFacetOverflow();

	};

	/**
	 * Initialize the extension.
	 */
	if ($facets.length > 0 || $annex.length > 0) {
		$facets.refinery();


		/**
		 * This function will set the class of any filter set with an active selection to `is-active`.
		 * @type {NodeListOf<Element>}
		 */
		let filterSets = document.querySelectorAll('[data-hook="refinery-selections"]');

		for (let filterID = 0; filterID < filterSets.length; filterID++) {
			let filterSetActive = filterSets[filterID].querySelector('.is-selected');
			let filterSetSelect = filterSets[filterID].querySelector('select');
			let filterSetSelectActive;

			if (filterSetSelect) {
				filterSetSelectActive = filterSetSelect.selectedIndex;
			}

			if ((filterSetActive && !filterSetSelect) || (filterSetSelect && filterSetSelectActive >= 1)) {
				filterSets[filterID].previousElementSibling.classList.add('is-active');
			}
		}

		let selectedFilters = document.querySelector('[data-hook="selected-filters"]');

		/**
		 * This function will create trigger the clearing of the separate, selected filters.
		 * @type {Element}
		 */
		if (selectedFilters) {
			window.scrollTo(0, document.querySelector('[data-hook="refinery"]').getBoundingClientRect().top + window.scrollY);

			let filterTags = selectedFilters.querySelectorAll('[data-hook="filter-tag"]');

			for (let id = 0; id < filterTags.length; id++) {
				let filter = filterTags[id];

				filter.addEventListener('click', function (event) {
					event.preventDefault();

					let cancelledFilter = event.target;
					let filterCode = cancelledFilter.getAttribute('data-facet-code');
					let filterType = cancelledFilter.getAttribute('data-facet-type');

					if (filterType === 'checkbox' || filterType === 'radio') {
						let filterItem = document.querySelectorAll('input[type=' + filterType + '][name=' + filterCode + ']');

						for (let radios = 0; radios < filterItem.length; radios++) {
							if (filterItem[radios].checked === true) {
								filterItem[radios].checked = false;
								MMProdList_UpdateQuery(filterItem[radios]);
							}
						}
						return true;
					}
					else if (filterType === 'select') {
						let filterItem = document.querySelector('select[name=' + filterCode + ']');

						filterItem.value = '';
						MMProdList_UpdateQuery(filterItem);
						return true;
					}
					else if (filterType === 'rangeslider') {
						let filterItem = document.querySelector('input[name=' + filterCode + ']');

						filterItem.value = '';
						MMProdList_UpdateQuery(filterItem);
						return true;
					}

				});
			}
		}
	}

})(jQuery, window);

Category Tree: Shows "inactive" Categories

Expected Behavior

"inactive" categories should not show in the category tree.

Current Behavior

The category tree structure is not honoring the active status of a category.

Correcting the Issue

It appears the code using the incorrect reference when pulling the categories. The code should include the Runtime_ prefix so it would respect the active flag as well as availability groups. I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code in User Interface -> Settings -> Category Tree Template:

<nav class="x-accordion-category-tree t-accordion-category-tree">
	<mvt:comment>
		<!-- Check if the current, parent category has any children. -->
	</mvt:comment>
	<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_Category_Load_Code(l.settings:breadcrumbs:links[1]:code, l.settings:current_parent)"/>
	<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_CategoryList_Load_Parent(l.settings:current_parent:id, l.settings:children)"/>
	<mvt:if expr="l.settings:children GT 0">
		<mvt:comment>
			<!--
			If the parent category has children, show all child and grandchild categories in an accordion navigation.
			-->
		</mvt:comment>
		<ul class="x-accordion-category-tree__row">
			<mvt:foreach iterator="child" array="children">
				<mvt:assign name="l.uri:store_id" value="g.Store:id"/>
				<mvt:assign name="l.uri:cat_id" value="l.settings:child:id"/>
				<mvt:assign name="l.settings:grandchildren" value="0"/>
				<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_Category_Load_Code(l.settings:child:code, l.settings:current_child)"/>
				<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_CategoryList_Load_Parent(l.settings:current_child:id, l.settings:grandchildren)"/>
				<mvt:do file="g.Module_Feature_URI_DB" name="l.have_uri" value="URI_Load_Item_Canonical(l.uri, l.settings:child:link)"/>
				<mvt:foreach iterator="grandchild" array="grandchildren">
					<mvt:if expr="l.settings:grandchild:code EQ l.settings:breadcrumbs:current_item:code">
						<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_Category_Load_ID(l.settings:grandchild:parent_id, l.settings:parent_category)" />
					</mvt:if>
				</mvt:foreach>
				<mvt:if expr="(l.settings:child:code EQ l.settings:breadcrumbs:current_item:code) OR (l.settings:child:code EQ l.settings:parent_category:code)">
					<mvt:assign name="l.settings:current:active" value="'is-active'"/>
					<mvt:assign name="l.settings:current:child" value="'x-accordion-category-tree__link--current'"/>
				<mvt:else>
					<mvt:assign name="l.settings:current:active" value="''"/>
					<mvt:assign name="l.settings:current:child" value="''"/>
				</mvt:if>
				<li class="x-accordion-category-tree__list x-accordion-category-tree__list--level-1 &mvte:current:active;">
					<div class="x-accordion-category-tree__summary">
						<a class="x-accordion-category-tree__link &mvte:current:child; u-text-medium" href="&mvte:child:link:uri;">&mvte:child:name;</a>
						<mvt:if expr="l.settings:grandchildren GT 0">
							<button class="x-accordion-category-tree__toggle c-button u-bg-transparent u-font-tiny" data-hook="accordion-toggle" type="button" aria-label="View More &mvte:child:name;"><span class="u-icon-add"></span></button>
						</mvt:if>
					</div>
					<mvt:if expr="l.settings:grandchildren GT 0">
						<ul class="x-accordion-category-tree__row x-accordion-category-tree__details">
							<mvt:foreach iterator="grandchild" array="grandchildren">
								<mvt:assign name="l.uri:store_id" value="g.Store:id"/>
								<mvt:assign name="l.uri:cat_id" value="l.settings:grandchild:id"/>
								<mvt:do file="g.Module_Feature_URI_DB" name="l.have_uri" value="URI_Load_Item_Canonical(l.uri, l.settings:grandchild:link)"/>
								<mvt:if expr="l.settings:grandchild:code EQ l.settings:breadcrumbs:current_item:code">
									<mvt:assign name="l.settings:current:grandchild" value="'x-accordion-category-tree__link--current'"/>
								<mvt:else>
									<mvt:assign name="l.settings:current:grandchild" value="''"/>
								</mvt:if>
								<li class="x-accordion-category-tree__list x-accordion-category-tree__list--level-2">
									<a class="x-accordion-category-tree__link &mvte:current:grandchild;" href="&mvte:grandchild:link:uri;">&mvte:grandchild:name;</a>
								</li>
							</mvt:foreach>
						</ul>
					</mvt:if>
				</li>
			</mvt:foreach>
		</ul>
	<mvt:else>
		<mvt:comment>
			<!--
			If the parent category does not have children, show all parent and, applicable,
			child categories in an accordion navigation.
			-->
		</mvt:comment>
		<ul class="x-accordion-category-tree__row">
			<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_CategoryList_Load_Parent('', l.settings:parent_categories)" />
			<mvt:foreach iterator="parent" array="parent_categories">
				<mvt:assign name="l.uri:store_id" value="g.Store:id"/>
				<mvt:assign name="l.uri:cat_id" value="l.settings:parent:id"/>
				<mvt:assign name="l.settings:children" value="0"/>
				<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_Category_Load_Code(l.settings:parent:code, l.settings:current_parent)"/>
				<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_CategoryList_Load_Parent(l.settings:current_parent:id, l.settings:children)"/>
				<mvt:do file="g.Module_Feature_URI_DB" name="l.have_uri" value="URI_Load_Item_Canonical(l.uri, l.settings:parent:link)"/>
				<mvt:foreach iterator="child" array="children">
					<mvt:if expr="l.settings:child:code EQ l.settings:breadcrumbs:current_item:code">
						<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_Category_Load_ID(l.settings:child:parent_id, l.settings:parent_category)" />
					</mvt:if>
				</mvt:foreach>
				<mvt:if expr="(l.settings:parent:code EQ l.settings:breadcrumbs:current_item:code) OR (l.settings:parent:code EQ l.settings:parent_category:code)">
					<mvt:assign name="l.settings:current:active" value="'is-active'"/>
					<mvt:assign name="l.settings:current:parent" value="'x-accordion-category-tree__link--current'"/>
				<mvt:else>
					<mvt:assign name="l.settings:current:active" value="''"/>
					<mvt:assign name="l.settings:current:parent" value="''"/>
				</mvt:if>
				<li class="x-accordion-category-tree__list x-accordion-category-tree__list--level-1 &mvte:current:active;">
					<div class="x-accordion-category-tree__summary">
						<a class="x-accordion-category-tree__link &mvte:current:parent; u-text-medium" href="&mvte:parent:link:uri;">&mvte:parent:name;</a>
						<mvt:if expr="l.settings:children GT 0">
							<button class="x-accordion-category-tree__toggle c-button u-bg-transparent u-font-tiny" data-hook="accordion-toggle" type="button" aria-label="View More &mvte:parent:name;"><span class="u-icon-add"></span></button>
						</mvt:if>
					</div>
					<mvt:if expr="l.settings:children GT 0">
						<ul class="x-accordion-category-tree__row x-accordion-category-tree__details">
							<mvt:foreach iterator="child" array="children">
								<mvt:assign name="l.uri:store_id" value="g.Store:id"/>
								<mvt:assign name="l.uri:cat_id" value="l.settings:child:id"/>
								<mvt:do file="g.Module_Feature_URI_DB" name="l.have_uri" value="URI_Load_Item_Canonical(l.uri, l.settings:child:link)"/>
								<mvt:if expr="l.settings:child:code EQ l.settings:breadcrumbs:current_item:code">
									<mvt:assign name="l.settings:current:child" value="'x-accordion-category-tree__link--current'"/>
								<mvt:else>
									<mvt:assign name="l.settings:current:child" value="''"/>
								</mvt:if>
								<li class="x-accordion-category-tree__list x-accordion-category-tree__list--level-2">
									<a class="x-accordion-category-tree__link &mvte:current:child;" href="&mvte:child:link:uri;">&mvte:child:name;</a>
								</li>
							</mvt:foreach>
						</ul>
					</mvt:if>
				</li>
			</mvt:foreach>
		</ul>
	</mvt:if>
</nav>
<!-- end .x-accordion-category-tree -->

CTLG: Shows "inactive" Categories

Expected Behavior

On the catalog page, "inactive" categories should not show.

Current Behavior

The catalog page is not honoring the active status of a category.

Correcting the Issue

It appears the code using the incorrect reference when pulling the categories. The code should include the Runtime_ prefix, so it would respect the active flag as well as availability groups. I have added this update, it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code in User Interface -> Pages -> CTLG -> Template:

<mvt:item name="html_profile" />
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<base href="&mvt:global:basehref;">
	<mvt:if expr="NOT ISNULL l.settings:page:title">
		<title>&mvt:page:title;</title>
	<mvt:else>
		<title>&mvt:store:name;: &mvt:page:name;</title>
	</mvt:if>
	<mvt:item name="head" param="css_list" />
	<mvt:item name="head" param="head_tag" />
</head>
<body id="js-&mvte:page:code;" class="o-site-wrapper t-page-&mvt:global:pageClass;">
	<mvt:item name="hdft" param="global_header" />

	<section class="u-bg-white t-expanded-block">
		<div class="o-wrapper t-wrapper">
			<mvt:item name="facets" />

			<div class="o-layout o-layout--column-reverse o-layout--row-reverse--l o-layout--wide">
				<div class="o-layout__item u-width-12 u-width-9--l">
					<section class="o-layout">
						<div class="o-layout__item">
							<mvt:item name="hdft" param="header" />
						</div>
					</section>

					<section class="o-layout u-grids-2 u-grids-3--l x-product-list">
						<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_CategoryList_Load_Parent('', l.settings:parent_categories)" />
						<mvt:foreach iterator="parent" array="parent_categories">
							<mvt:assign name="l.settings:catalog_item" value="''" />
							<mvt:do name="l.result" file="g.Module_Library_DB" value="Runtime_Category_Load_Code(l.settings:parent:code, l.settings:catalog)" />
							<mvt:do name="l.module_loaded" file="g.Module_Library_DB" value="Module_Load_Code('cmp-cssui-cattree', l.module)" />
							<mvt:if expr="l.module_loaded">
								<mvt:do name="l.result" file="g.Module_Root $ l.module:module" value="CSSUI_CatTree_Load(l.settings:catalog:id, l.settings:catalog_item)" />
							</mvt:if>
							<mvt:do file="g.Module_Feature_URI_UT" name="l.settings:catalog:link" value="Store_Category_URL(l.settings:catalog, NULL)" />
							<div class="o-layout__item u-text-center x-product-list__item">
								<a class="u-block x-product-list__link" href="&mvte:catalog:link;">
									<figure class="x-product-list__figure">
										<mvt:if expr="l.settings:catalog_item:image">
											<img class="x-product-list__image" src="&mvt:catalog_item:image;" alt="&mvt:catalog:name;">
										</mvt:if>
										<figcaption>
											<strong class="x-product-list__name u-text-uppercase">&mvte:catalog:name;</strong>
										</figcaption>
									</figure>
								</a>
							</div>
						</mvt:foreach>
					</section>

					<section class="o-layout">
						<div class="o-layout__item">
							<mvt:item name="hdft" param="footer" />
						</div>
					</section>
				</div>
				<aside class="o-layout__item u-block--l u-hidden u-width-3--l">
					<mvt:item name="category_tree" />
				</aside>

			</div>
		</div>
	</section>
	<br>

	<mvt:item name="hdft" param="global_footer" />
</body>
</html>

Image Machine - Single quotes in product names

Expected Behavior

The use of non-alphanumeric characters in the product name should not interfere with the Image Machine functionality.

Current Behavior

If a product name has a single quote in it, i.e. Let's Go Team, then the productName variable causes a SyntaxError: unexpected token: identifier in the console which in turns breaks Image Machine.

Correcting the Issue

This issue is due to the incorrect Output Encoding being passed to the variable; it should be &mvtj instead of &mvte. I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code for Image Machine:

User Interface -> Pages -> PROD -> Product Display Layout Image Machine:

window.gallery = [];
let generate_thumbnail_event = new CustomEvent('ImageMachine_Generate_Thumbnail');
let thumbnailIndex = 0;
let thumbnail_width;
let thumbnail_height;
let gallery_container;
let gallery_Index = 0;
let productName = '&mvtj:product:name;';

gallery_container = document.createElement('div');
gallery_container.classList.add('x-product-photo-gallery');
gallery_container.setAttribute('data-hook', 'photo-gallery');


/**
 * This function allows you to prepend or append code to an existing function.
 * https://stackoverflow.com/questions/9134686/adding-code-to-a-javascript-function-programmatically
 */
function functionExtender(container, funcName, prepend, append) {
	(function () {
		'use strict';
		let cachedFunction = container[funcName];

		container[funcName] = function () {
			if (prepend) {
				prepend.apply(this);
			}

			let result = cachedFunction.apply(this, arguments);

			if (append) {
				append.apply(this);
			}

			return result;
		};
	})();
}


ImageMachine.prototype.oninitialize = function (data) {
	window.gallery = [];
	gallery_container.innerHTML = '';
	gallery_Index = 0;
	thumbnailIndex = 0;
	this.Initialize(data);
	this.main_image.setAttribute('data-index', '0');
};


ImageMachine.prototype.ImageMachine_Generate_Thumbnail = function (thumbnail_image, main_image, closeup_image, type_code) {
	let img;
	let thumbnail;
	let gallery_element;
	let gallery_image;

	if (!thumbnail_width && !thumbnail_height) {
		thumbnail_width = this.thumb_width + 'px';
		thumbnail_height = this.thumb_height + 'px';
	}

	thumbnail = document.createElement('span');
	thumbnail.classList.add('x-product-layout-images__thumbnail-image');
	thumbnail.setAttribute('data-index', thumbnailIndex++);
	thumbnail.setAttribute('data-main', main_image);
	thumbnail.setAttribute('data-type', type_code);
	thumbnail.setAttribute('data-zoom', closeup_image);
	thumbnail.setAttribute('style', 'width: ' + thumbnail_width + '; display: inline-block; height: ' + thumbnail_height +';');

	if (typeof( thumbnail_image ) === 'string' && thumbnail_image.length > 0) {
		img = document.createElement('img');
		img.src = thumbnail_image;
		img.setAttribute('alt', productName);
		img.setAttribute('itemprop', 'thumbnail');
		thumbnail.appendChild(img);
	}

	/**
	 * Create a scrollable gallery, append it to the page, and open on main image click.
	 */
	gallery_image = (typeof (closeup_image) === 'string' && closeup_image.length > 0) ? closeup_image : main_image;

	if (typeof( closeup_image ) === 'string' && closeup_image.length > 0) {
		gallery_element = document.createElement('img');
		gallery_element.src = gallery_image;
		gallery_element.setAttribute('data-index', gallery_Index++);
		gallery_element.setAttribute('alt', productName);
		gallery_container.appendChild(gallery_element);
	}

	gallery.push({
		src: gallery_image,
		title: productName
	});

	document.dispatchEvent(generate_thumbnail_event);

	return thumbnail;
};


let gallery_template = document.querySelector('[data-hook="photo-gallery-template"]');

if (gallery_template) {
	gallery_template.appendChild(gallery_container);
}


/**
 * This controls what happens when you click a thumbnail.
 */
functionExtender(
	ImageMachine.prototype,
	'onthumbnailimageclick',
	function () {},
	function () {
		let clickedElement = event.target;

		//console.log(clickedElement.getAttribute('data-index'));
		this.main_image.setAttribute('data-index', clickedElement.getAttribute('data-index'));
	}
);


/**
 * This disables the default Miva image zoom functionality.
 * @return {boolean}
 */
ImageMachine.prototype.Closeup_Open = function () {
	return false;
};

ImageMachine.prototype.onmainimageclick = function () {
};

Subscriptions: Enforce Account Requirement

When purchasing a product as a subscription item, you are required to have, or create, a customer account.

Expected Behavior

Upon transitioning from BASK to OCST, with ORDL not in use, you should be prompted to either log in or create an account.

Current Behavior

Upon transitioning from BASK to OCST, with ORDL not in use, you are able to enter your information in on OCST and proceed. However, you are then directed to ORDL which prompts you to either log in or create an account. This is a sub-optimal checkout process.

Correcting the Issue

To improve the experience, we will perform a check on BASK and determine if you can proceed to OCST or if you need to be diverted to ORDL. I have added this update and it will be included in the next maintenance release.

To make the update prior to the next release, you will need to update the code on BASK and ORDL:

User Interface -> Pages -> BASK -> Basket Contents:

<mvt:if expr="g.Action EQ 'RGRP'">
	<mvt:assign name="g.Quantity" value="miva_variable_value('Quantity' $ g.Restore_Counter)" />
	<mvt:assign name="g.Restore_Name" value="miva_variable_value('Restore_Name' $ g.Restore_Counter)" />
	<mvt:assign name="g.Restore_Link" value="miva_variable_value('Restore_Link' $ g.Restore_Counter)" />
	<script>
		function clickAndDisable(link) {
			// disable subsequent clicks
			link.onclick = function (event) {
				event.preventDefault();
			}
		}
	</script>
	<div class="x-messages x-messages--info">
		<strong>&mvte:global:Restore_Name;</strong> has been removed from your cart. <a href="&mvte:global:Restore_Link;" onclick="clickAndDisable(this);">Undo?</a>
	</div>
	<mvt:assign name="g.Restore_Counter" value="g.Restore_Counter + 1" />
</mvt:if>
<mvt:item name="customfields" param="Read_Basket('continue_url', l.settings:continue_url)" />
<mvt:if expr="g.request_cookies:continue_url">
	<mvt:assign name="g.continue_url" value="g.request_cookies:continue_url" />
<mvt:else>
	<mvt:assign name="g.continue_url" value="l.settings:urls:SFNT:auto" />
</mvt:if>
<mvt:if expr="l.settings:basket:empty">
	<div class="x-messages x-messages--info">
		<strong>Your shopping cart is currently empty.</strong> <a href="&mvt:global:continue_url;">Start Shopping</a>
	</div>
	<mvt:exit />
</mvt:if>

<section class="o-layout o-layout--wide t-basket" data-hook="basket" data-item-count="&mvt:global_minibasket:basket_count;" data-subtotal="&mvt:global_minibasket:formatted_total;">
	<div class="o-layout__item u-width-12 u-width-8--l">
		<br>
		<table class="c-table-responsive t-basket__product-summary">
			<thead class="c-table-responsive_thead u-text-left">
				<tr class="c-table-responsive__row u-color-black u-font-tiny u-text-bold u-text-uppercase">
					<th class="c-table-responsive__cell" scope="col">Product</th>
					<th class="c-table-responsive__cell u-text-center" scope="col">Quantity</th>
					<th class="c-table-responsive__cell u-text-right" scope="col">Subtotal</th>
					<td class="c-table-responsive__cell" scope="col">&nbsp;</td>
				</tr>
			</thead>
			<tbody>
				<mvt:assign name="g.basket_subtotal" value="0" />
				<mvt:foreach iterator="group" array="basket:groups">
					<mvt:assign name="l.settings:group:restore:link" value="l.settings:urls:BASK:auto_sep $ 'Action=ADPR&Product_Code=' $ l.settings:group:product:code $ '&Quantity=' $ l.settings:group:quantity" />
					<tr class="c-table-responsive__row u-color-gray-50">
						<td class="c-table-responsive__cell" data-label="">
							<div class="o-layout">
								<div class="o-layout__item u-width-12 u-width-4--s u-width-3--l u-text-center">
									<picture>
										<img src="&mvte:group:imagetypes:main;" alt="&mvt:group:name;">
									</picture>
								</div>
								<div class="o-layout__item u-width-12 u-width-8--s u-width-9--l t-basket__product-details">
									<a class="u-text-bold u-color-black" href="&mvte:group:link;" title="&mvt:group:name;" rel="nofollow">&mvt:group:name;</a>
									<br>
									<div>
										<mvt:if expr="l.settings:group:upsold">
											<span class="u-font-small">(Special Offer)</span><br>
										</mvt:if>
										<span class="u-font-small">SKU: &mvt:group:code;</span><br>
										<mvt:foreach iterator="discount" array="group:discounts">
											<mvt:if expr="l.settings:discount:display">
												<mvt:if expr="'sale' CIN l.settings:discount:descrip">
													<mvt:assign name="l.settings:discount:descrip" value="'Savings'"/>
												<mvt:else>
													<mvt:assign name="l.settings:discount:descrip" value="l.settings:discount:descrip"/>
												</mvt:if>
												<span class="u-font-small u-flex u-color-red">
													<span class="o-layout--grow">&mvt:discount:descrip;</span>
													<span>&mvt:discount:formatted_discount;</span>
												</span>
											</mvt:if>
										</mvt:foreach>
										<mvt:assign name="l.settings:attr_ref" value="''" />
										<mvt:foreach iterator="option" array="group:options">
											<mvt:assign name="l.settings:group:option_counter" value="l.settings:group:option_counter + 1" />
											<span class="u-font-small u-flex">
												<mvt:if expr="l.settings:option:attmpat_id">
													<mvt:assign name="l.settings:group:restore:link" value="l.settings:group:restore:link $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:template_code=' $ l.settings:option:attr_code" />
													<mvt:assign name="l.settings:attr_ref" value="l.settings:option:attr_prompt" />
												<mvt:else>
													<mvt:assign name="l.settings:attr_ref" value="l.settings:option:attr_code" />
												</mvt:if>
												<mvt:if expr="l.settings:option:option_id">
													<mvt:assign name="l.settings:group:restore:link" value="l.settings:group:restore:link $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:code=' $ l.settings:attr_ref $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:value=' $ l.settings:option:opt_code" />
													<span class="o-layout--grow">&mvt:option:attr_prompt;: &mvt:option:opt_prompt;</span>
													<mvt:if expr="l.settings:option:subtotal GT 0">
														<span>&mvt:option:formatted_subtotal;</span>
													</mvt:if>
												<mvt:elseif expr="NOT ISNULL l.settings:option:data">
													<mvt:assign name="l.settings:group:restore:link" value="l.settings:group:restore:link $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:code=' $ l.settings:attr_ref $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:value=' $ l.settings:option:data" />
													<span class="o-layout--grow">&mvt:option:attr_prompt;: &mvt:option:data;</span>
													<mvt:if expr="l.settings:option:subtotal GT 0">
														<span>&mvt:option:formatted_subtotal;</span>
													</mvt:if>
												<mvt:elseif expr="NOT ISNULL l.settings:option:data_long">
													<mvt:assign name="l.settings:group:restore:link" value="l.settings:group:restore:link $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:code=' $ l.settings:attr_ref $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:value=' $ l.settings:option:data_long" />
													<span class="o-layout--grow">&mvt:option:attr_prompt;: &mvt:option:data_long;</span>
													<mvt:if expr="l.settings:option:subtotal GT 0">
														<span>&mvt:option:formatted_subtotal;</span>
													</mvt:if>
												<mvt:else>
													<mvt:assign name="l.settings:group:restore:link" value="l.settings:group:restore:link $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:code=' $ l.settings:attr_ref $ '&Product_Attributes[' $ l.settings:group:option_counter $ ']:value=' $ l.settings:option:attr_prompt" />
													<span class="o-layout--grow">&mvt:option:attr_prompt;</span>
													<mvt:if expr="l.settings:option:subtotal GT 0">
														<span>&mvt:option:formatted_subtotal;</span>
													</mvt:if>
												</mvt:if>
											</span>
											<mvt:foreach iterator="discount" array="option:discounts">
												<mvt:if expr="l.settings:discount:display">
													<span class="u-font-small u-flex u-color-red">
														<span class="u-color-red o-layout--grow">&mvt:discount:descrip;</span>
														<span class="u-color-red">&mvt:discount:formatted_discount;</span>
													</span>
												</mvt:if>
											</mvt:foreach>
										</mvt:foreach>
									</div>
									<mvt:if expr="l.settings:group:subterm_id">
										<div class="u-font-small">Subscription: &mvte:group:productsubscriptionterm:descrip;</div>
									</mvt:if>
									<mvt:if expr="l.settings:group:product:id AND (NOT l.settings:group:product:productsubscriptionsettings:enabled OR NOT l.settings:group:product:productsubscriptionsettings:mandatory)">
										<br>
										<div class="u-font-small">
											<a class="u-color-gray-50 u-text-bold u-text-uppercase" href="&mvte:urls:WISH:secure_sep;Action=MPWL&Group_ID=&mvta:group:group_id;">&#8942; Save to Wish List</a>
										</div>
									</mvt:if>
								</div>
							</div>
						</td>
						
						<td class="c-table-responsive__cell c-table-responsive__cell--flex u-text-center" data-label="Quantity">
							<mvt:if expr="l.settings:group:upsold">
								&mvt:group:quantity;
							<mvt:else>
								<form class="t-basket-update" method="post" action="&mvte:urls:BASK:auto;" data-hook="group-&mvte:group:group_id;">
									<fieldset>
										<legend>&mvt:group:name; Quantity</legend>
										<input type="hidden" name="Action" value="QTYG" />
										<input type="hidden" name="Basket_Group" value="&mvte:group:group_id;" />
										<input type="hidden" name="Old_Screen" value="BASK" />
										<input type="hidden" name="Offset" value="&mvte:global:Offset;" />
										<input type="hidden" name="AllOffset" value="&mvte:global:AllOffset;" />
										<input type="hidden" name="CatListingOffset" value="&mvte:global:CatListingOffset;" />
										<input type="hidden" name="RelatedOffset" value="&mvte:global:RelatedOffset;" />
										<input type="hidden" name="SearchOffset" value="&mvte:global:SearchOffset;" />
										<input type="hidden" name="Restore_Name" value="&mvt:group:name;">
										<input type="hidden" name="Restore_Link" value="&mvte:group:restore:link;">
										<ul class="c-form-list">
											<li class="c-form-list__item">
												<label class="u-hide-visually" for="l-quantity-&mvte:group:group_id;">Quantity</label>
												<div class="x-quantify c-control-group" data-hook="quantify">
													<button class="c-button c-control-group__button u-bg-white u-color-gray-50 u-icon-subtract" data-action="decrement" aria-label="Decrease Quantity"></button>
													<input id="l-quantity-&mvte:group:group_id;" class="c-form-input c-control-group__field u-text-bold u-text-center" data-group="group-&mvte:group:group_id;" data-hook="group-quantity" type="tel" name="Quantity" value="&mvt:group:quantity;" required>
													<button class="c-button c-control-group__button u-bg-white u-color-gray-50 u-icon-add" data-action="increment" aria-label="Increase Quantity"></button>
												</div>
											</li>
										</ul>
									</fieldset>
								</form>
							</mvt:if>
						</td>
						
						<td class="c-table-responsive__cell c-table-responsive__cell--flex u-text-right" data-label="Subtotal">
							<span class="u-inline-block">
								<span class="u-text-bold u-block">&mvt:group:formatted_subtotal_comprehensive;</span>
								<mvt:assign name="g.basket_subtotal" value="g.basket_subtotal + l.settings:group:subtotal_comprehensive" />
								<mvt:if expr="l.settings:group:subtotal_base_price NE l.settings:group:subtotal">
									<s class="c-heading--subheading u-block">&mvt:group:formatted_subtotal_base_price;</s>
								</mvt:if>
								<mvt:if expr="l.settings:group:upsold">
									<span class="c-heading--subheading u-block">&nbsp;</span>
								</mvt:if>
							</span>
						</td>
						
						<td class="c-table-responsive__cell c-table-responsive__cell--flex u-text-right" data-label="Remove from Cart">
							<a class="c-button c-button--hollow c-button--small u-bg-white u-color-gray-20 t-basket__product-remove" href="&mvte:urls:BASK:auto_sep;Action=RGRP&Basket_Group=&mvta:group:group_id;&Restore_Name=&mvta:group:name;&Restore_Link=&mvta:group:restore:link;" aria-label="Remove &mvt:group:name; from Cart">
								<span class="u-color-gray-50 u-icon-cross"></span>
							</a>
						</td>
					</tr>
				</mvt:foreach>
			</tbody>
		</table>
		
		<ul class="o-list-inline u-text-bold u-text-uppercase">
			<li class="o-list-inline__item">
				<a class="u-color-gray-50" href="&mvte:urls:WISH:secure_sep;Action=MAWL" title="Save Cart for Later"><span class="u-font-small u-icon-history"></span>&nbsp;<span class="u-font-tiny">Save Cart for Later</span></a>
			</li>
			<li class="o-list-inline__item">
				<a class="u-color-gray-50" href="&mvte:urls:BASK:auto_sep;Clear_Cart=1" title="Delete Entire Cart"><span class="u-font-small u-icon-remove"></span>&nbsp;<span class="u-font-tiny">Delete Entire Cart</span></a>
			</li>
		</ul>
		<br>

		<p class="u-text-bold u-font-small u-text-uppercase">
			<script>
				function continueShopping(link) {
					if (sessionStorage.getItem('continue_url')) {
						link.href = sessionStorage.getItem('continue_url');
					}
				}
			</script>
			<a class="u-color-black" href="&mvte:urls:SFNT:auto;" onclick="continueShopping(this);" title="Continue Shopping"><span class="u-icon-arrow-left"></span>&nbsp;Continue Shopping</a>
		</p>
		
		<hr class="c-keyline">
	</div>
	
	<aside class="o-layout__item u-width-12 u-width-4--l">
		<mvt:do file="g.Module_Store_Module_Currency" name="l.settings:basket:formatted_subtotal" value="CurrencyModule_AddFormatting(g.Store:currncy_mod, g.basket_subtotal)" />
		<table class="c-table-simple t-basket__order-summary">
			<thead>
				<tr class="c-table-simple__row">
					<td class="c-table-simple__cell">
						<span class="c-heading-delta u-text-bold u-text-uppercase">Order Summary</span>
					</td>
				</tr>
			</thead>
			<tbody>
				<tr class="c-table-simple__row u-color-gray-50">
					<td class="c-table-simple__cell u-flex o-layout--justify-between">
						<span>
							<mvt:if expr="l.settings:global_minibasket:basket_count GT 1">
								<span class="u-color-black">Subtotal:</span> &mvte:global_minibasket:basket_count; Items
							<mvt:else>
								<span class="u-color-black">Subtotal:</span> &mvte:global_minibasket:basket_count; Item
							</mvt:if>
						</span>
						<span>&mvt:basket:formatted_subtotal;</span>
					</td>
				</tr>
				
				<mvt:foreach iterator="charge" array="basket:charges">
					<tr class="c-table-simple__row u-color-gray-50">
						<td class="c-table-simple__cell u-flex o-layout--justify-between">
							<span>&mvt:charge:descrip;</span>
							<span>&mvt:charge:formatted_disp_amt;</span>
						</td>
					</tr>
				</mvt:foreach>

				<tr class="c-table-simple__row u-color-gray-50">
					<td class="c-table-simple__cell">
						<input id="basket-shipping-form-toggle" class="u-hidden t-basket__basket-shipping-form-toggle" type="checkbox">
						<label class="u-flex o-layout--justify-between" for="basket-shipping-form-toggle">
							<span>Shipping</span>
							<span class="u-text-underline">Estimate</span>
						</label>
						<mvt:item name="shipestimate" />
					</td>
				</tr>
				
				<mvt:foreach iterator="coupon" array="basket:coupons">
					<tr class="c-table-simple__row u-color-gray-50">
						<td class="c-table-simple__cell u-flex o-layout--justify-between">
							<form method="post" action="&mvte:urls:BASK:auto;">
								<fieldset>
									<legend>Remove Promo Code &mvt:coupon:code;</legend>
									<ul class="c-form-list">
										<li class="c-form-list__item c-form-list__item--full">
											<input type="hidden" name="Action" value="RCPN"/>
											<input type="hidden" name="Coupon_Code" value="&mvte:coupon:code;"/>
											<button class="c-button c-button--clear u-bg-transparent u-color-red" type="submit">&mvt:coupon:code; <span class="u-icon-remove"></span></button>
										</li>
									</ul>
								</fieldset>
							</form>
							<mvt:if expr="NOT ISNULL l.settings:coupon:descrip">
								<span>&mvt:coupon:descrip;</span>
							</mvt:if>
						</td>
					</tr>
				</mvt:foreach>
				
				<tr class="c-table-simple__row u-color-gray-50">
					<td class="c-table-simple__cell">
						<input id="basket-coupon-form-toggle" class="u-hidden t-basket__basket-coupon-form-toggle" type="checkbox">
						<label class="u-flex o-layout--justify-between o-layout--align-center u-text-bold u-text-uppercase" for="basket-coupon-form-toggle">
							<span>Add Promo Code</span>
							<span class="u-icon-add u-font-tiny"></span>
						</label>
						<form class="t-basket__coupon-form" method="post" action="&mvte:urls:_self:auto;">
							<fieldset>
								<legend>Add Promo Code</legend>
								<input type="hidden" name="Action" value="ACPN" />
								<ul class="c-form-list">
									<li class="c-form-list__item c-form-list__item--full c-control-group u-flex">
										<label class="u-hide-visually" for="Coupon_Code">Add Promo Code</label>
										<input id="Coupon_Code" class="c-form-input c-control-group__field u-font-small u-text-regular" type="text" name="Coupon_Code" placeholder="Enter Promo Code" required>
										<input class="c-button c-control-group__button c-button--large u-bg-black u-border-none u-font-tiny u-text-uppercase" type="submit" value="Apply">
									</li>
								</ul>
							</fieldset>
						</form>
					</td>
				</tr>
				
				<tr>
					<td class="c-table-simple__cell">
						<br>
						<div class="u-flex o-layout--align-center o-layout--justify-between c-heading-delta u-text-bold u-text-uppercase">
							<span class="u-font-tiny">Current Total</span>
							<span>&mvt:basket:formatted_total;</span>
						</div>
					</td>
				</tr>
			
				<tr>
					<td>
						<br>
						<mvt:if expr="g.Basket:sub_count GT 0 AND g.Basket:cust_id EQ 0">
							<a class="c-button c-button--full c-button--huge u-bg-primary u-color-black u-text-bold u-text-uppercase" href="&mvte:urls:ORDL:auto;">Secure Checkout</a>
						<mvt:else>
							<a class="c-button c-button--full c-button--huge u-bg-primary u-color-black u-text-bold u-text-uppercase" href="&mvte:urls:OINF:auto;">Secure Checkout</a>
						</mvt:if>
						<br>
						<hr class="c-keyline">
						<mvt:comment>
							<!-- If you are using PayPal Express, this is where the `PaypalExButton` item goes. -->
							<mvt:item name="PaypalExButton"/>
							<br>
							<hr class="c-keyline">
						</mvt:comment>
						<mvt:comment>
							<!-- If you are using Amazon Pay, this is where the `"amazonpay_button" param="body"` item goes. -->
							<p class="u-text-bold">Have an Amazon account?</p>
							<mvt:item name="amazonpay_button" param="body" />
							<br>
							<hr class="c-keyline">
						</mvt:comment>
					</td>
				</tr>
			
				<tr>
					<td>
						<mvt:item name="readytheme" param="contentsection( 'helpful_info' )" />
					</td>
				</tr>
			</tbody>
		</table>
		
	</aside>
</section>

User Interface -> Pages -> ORDL -> Template:

<mvt:item name="html_profile" />
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<base href="&mvt:global:basehref;">
	<mvt:if expr="NOT ISNULL l.settings:page:title">
		<title>&mvt:page:title;</title>
	<mvt:else>
		<title>&mvt:store:name;: &mvt:page:name;</title>
	</mvt:if>
	<mvt:item name="head" param="css_list" />
	<mvt:item name="head" param="head_tag" />
</head>
<body id="js-&mvte:page:code;" class="o-site-wrapper t-page-&mvt:global:pageClass;">
	<mvt:item name="hdft" param="global_header" />

	<section class="o-layout">
		<div class="o-layout__item">
			<mvt:item name="hdft" param="header" />
		</div>
	</section>
	<br>

	<section class="o-layout">
		<div class="o-layout__item u-width-12">
			<section class="t-expanded-block t-account-landing-section u-bg-white">
				<div class="o-layout__item t-expanded-block__item t-account-landing-section__header u-width-12">
					<mvt:item name="readytheme" param="contentsection( 'messages' )" />
					<div class="o-layout o-layout--wide o-layout--justify-around u-text-center">
						<div class="o-layout__item u-width-12 u-width-4--l t-customer-profile">
							<div class="t-account-landing-section__header">
								<span class="c-heading-delta t-account-landing-section__heading">Current Customer?</span>
								<p class="u-font-small">Please sign in to continue your order.</p>
							</div>
							<form class="u-inline-block u-text-left" method="post" action="&mvte:urls:OCST:secure;" autocomplete="off">
								<fieldset>
									<legend>&mvt:page:name;</legend>
									<input type="hidden" name="Action" value="LOGN" />
									<ul class="c-form-list">
										<li class="c-form-list__item c-form-list__item--full">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required &mvt:global:invalid_credentials;" for="l-Customer_LoginEmail">Email Address</label>
											<input id="l-Customer_LoginEmail" class="c-form-input c-form-input--large" type="email" name="Customer_LoginEmail" value="&mvte:global:Customer_LoginEmail;" autocomplete="email" required>
										</li>
										<li class="c-form-list__item c-form-list__item--full">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required &mvt:global:invalid_credentials;" for="l-Customer_Password">Password:</label>
											<input id="l-Customer_Password" class="c-form-input c-form-input--large" type="password" name="Customer_Password" autocomplete="current-password" required>
										</li>
										<li class="c-form-list__item c-form-list__item--full">
											<input class="c-button c-button--full c-button--huge u-bg-gray-50 u-font-small u-text-bold u-text-uppercase" type="submit" value="Log In">
											<span class="u-font-small">
												<a class="c-button c-button--full c-button--huge u-bg-white u-color-gray-50 u-font-tiny u-text-uppercase" data-mini-modal data-mini-modal-type="inline" data-mini-modal-content="data-forgot-password" href="&mvte:urls:FPWD:secure;" title="Forgot Your Password?">Forgot Password?</a>
											</span>
										</li>
									</ul>
								</fieldset>
							</form>
						</div>
						<div class="o-layout__item u-width-12 u-width-4--l t-customer-profile">
							<div class="t-account-landing-section__header">
								<span class="c-heading-delta t-account-landing-section__heading">New Customer?</span>
								<p class="u-font-small">Please create an account to continue your order.</p>
							</div>
							<form class="u-inline-block u-text-left" method="post" action="&mvte:urls:CACT:secure;">
								<fieldset>
									<legend>Customer Log In</legend>
									<input type="hidden" name="current_location" value="&mvte:urls:OCST:secure_sep;">
									<input type="hidden" name="Order" value="&mvte:global:Order;">
									<ul class="c-form-list">
										<li class="c-form-list__item">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required" for="l-register_fname">First Name</label>
											<input id="l-register_fname" class="c-form-input c-form-input--large" type="text" name="register_fname" value="" autocomplete="name given-name" required>
										</li>
										<li class="c-form-list__item">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required" for="l-register_lname">Last Name</label>
											<input id="l-register_lname" class="c-form-input c-form-input--large" type="text" name="register_lname" autocomplete="name family-name" required>
										</li>
										<li class="c-form-list__item">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required" for="l-register_email">Email Address</label>
											<input id="l-register_email" class="c-form-input c-form-input--large" type="email" name="register_email" value="&mvte:global:register_email;" autocomplete="email" required>
										</li>
										<li class="c-form-list__item">
											<label class="c-form-label u-font-tiny u-text-bold u-text-uppercase is-required" for="l-register_password">Password</label>
											<input id="l-register_password" class="c-form-input c-form-input--large" type="password" name="register_password" autocomplete="new-password" required>
										</li>
										<li class="c-form-list__item u-text-right">
											<input class="c-button c-button--full c-button--hollow c-button--huge u-bg-white u-color-gray-50 u-font-small u-text-bold u-text-uppercase" type="submit" value="Create My Account">
										</li>
									</ul>
								</fieldset>
							</form>
							<br>
						</div>
						<mvt:if expr="g.Basket:sub_count EQ 0">
							<div class="o-layout__item u-width-12 u-width-4--l">
								<div class="t-account-landing-section__header">
									<span class="c-heading-delta t-account-landing-section__heading">Guest Checkout</span>
								</div>
								<p class="u-color-gray-50 u-font-small">Finalize your order without creating an account.</p>
								<br>
								<a class="c-button c-button--huge u-bg-gray-50 u-font-small u-text-bold u-text-uppercase" href="&mvte:urls:OCST:secure;" title="Continue As A Guest">Continue As A Guest</a>
								<br>
							</div>
						</mvt:if>
					</div>
			</section>
		</div>
	</section>
	
	<section class="o-layout">
		<div class="o-layout__item">
			<mvt:item name="hdft" param="footer" />
			<mvt:item name="readytheme" param="contentsection( 'forgot-password' )" />
		</div>
	</section>

	<mvt:item name="hdft" param="global_footer" />
</body>
</html>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.