Skip to content

Instantly share code, notes, and snippets.

@stvnrynlds
Created July 6, 2018 21:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stvnrynlds/690a1a46e12e1e2de19ce6746548820a to your computer and use it in GitHub Desktop.
Save stvnrynlds/690a1a46e12e1e2de19ce6746548820a to your computer and use it in GitHub Desktop.
<!-- /templates/product.liquid -->
{% comment %}
Rich snippets (itemscope, itemtype, etc.) for products are a theme requirement,
and allow search engines to easily understand what the content is.
For more information on these Scheme.org tags, visit:
- http://schema.org/docs/gs.html
{% endcomment %}
{% if product.type == 'Gift Message' %}
<script type="text/javascript">
window.location.href = '{{ shop.url }}/404';
</script>
{% else %}
<div class="search-wrap">{% include 'search-bar' %}</div>
<div itemscope itemtype="http://schema.org/Product">
<meta itemprop="url" content="{{ shop.url }}{{ product.url }}">
<meta itemprop="image" content="{{ product.featured_image.src | img_url: 'grande' }}">
{% comment %}
Get first variant, or deep linked one
{% endcomment %}
{% assign current_variant = product.selected_or_first_available_variant %}
<div class="grid grid--wide product-single regular-image-state {% if product.tags contains 'market-exclusive' %}is-market-exclusive{% endif%}">
<div class="grid__item large--one-half text-center left-wrapper">
<div class="product-photo-section">
<div class="mobile-slideshow">
<div class="mobile-slideshow-wrapper">
<div class="mobile-slideshow-worker">
{% for image in product.images %}
{% assign variantid = false %}
{% if image.variants.size > 0 %}
{% for variant in image.variants %}
{% assign variantid = variant.id %}
{% break %}
{% endfor %}
{% endif %}
<div>
<img src="{{ image.src | img_url: '1024x1024' }}" alt="{{ image.alt | escape }}" {% if variantid != false %}data-variant="{{ variantid }}"{% endif %}>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="product-single__photos" id="ProductPhoto">
{% if product.metafields.c_f.joanna_button %}
<div class="joanna-button"><div class="joannas-tips">{{ product.metafields.c_f.joanna_button }}</div><a class="j-tip-toggle" href="#"><img width="84" height="170" src="{{ 'JoannasTips.png' | asset_url }}" /></a></div>
{% endif %}
<div id="ProductPhotoHover">
{% assign featured_image = current_variant.featured_image | default: product.featured_image %}
<img src="{{ featured_image | img_url: '1024x1024' }}" alt="{{ featured_image.alt | escape }}" id="ProductPhotoImg" />
</div>
<div id="ProductPhotoPin">
<a data-pin-do="buttonPin" href="https://www.pinterest.com/pin/create/button/"><img src="//assets.pinterest.com/images/pidgets/pinit_fg_en_rect_gray_20.png" /></a>
<script async defer src="//assets.pinterest.com/js/pinit.js"></script>
</div>
<div id="product-photo-expander"></div>
</div>
{% comment %}
Create thumbnails if we have more than one product image
{% endcomment %}
{% if product.images.size > 1 %}
<div style="overflow:hidden;">
<ul class="product-single__thumbnails grid-uniform" id="ProductThumbs">
{% for image in product.images %}
{% assign variantid = false %}
{% if image.variants.size > 0 %}
{% for variant in image.variants %}
{% assign variantid = variant.id %}
{% break %}
{% endfor %}
{% endif %}
<li class="grid__item one-quarter">
<a href="{{ image.src | img_url: '1024x1024' }}" class="product-single__thumbnail" {% if variantid != false %}data-variant="{{ variantid }}"{% endif %}>
<img src="{{ image.src | img_url: 'compact_cropped' }}" alt="{{ image.alt | escape }}">
</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
<div class="social-bar">
{% include 'social-sharing' %}
</div>
</div>
<div class="grid__item large--one-half">
<div class="product_top">
<h1 itemprop="name" class="product_title flip-quotes">{{ product.title }}</h1>
{% comment %}
- Adding custom texts.
{% endcomment %}
{% if product.tags contains 'customtext1' or product.tags contains 'customtext2' or product.tags contains 'customtext3' %}
<div class="custom-text">
{% if product.tags contains 'customtext1' %}
<p>{{ settings.product_custom_text_1 }}</p>
{% elsif product.tags contains 'customtext2' %}
<p>{{ settings.product_custom_text_2 }}</p>
{% else %}
<p>{{ settings.product_custom_text_3 }}</p>
{% endif %}
</div>
{% endif %}
<div class="row collapse product_main">
{% if product.tags contains 'discontinued:number:1' and product.available == false %}
<div class="small-12 large-6 columns">
<span class="discontinued">DISCONTINUED</span>
</div>
{% else %}
<div class="small-12 large-6 columns small-text-center large-text-left">
<div class="product-price-container {% if product.compare_at_price_max > product.price %}on-sale{% endif %}">
<span class="visually-hidden">{{ 'products.general.sale_price' | t }}</span>
<p class="sale-badge">Sale</p><p class="compare-price">{{ current_variant.compare_at_price | money }}</p>
<span class="visually-hidden">{{ 'products.general.regular_price' | t }}</span>
<span class="h2 product-price" itemprop="price">{{ current_variant.price | money }}</span>
</div>
</div>
{% endif %}
<div class="small-12 large-6 columns small-text-center large-text-left reviews">
<span class="shopify-product-reviews-badge" data-id="{{ product.id }}"></span>
</div>
</div>
<div class="social-bar just-expanded">
{% include 'social-sharing' %}
</div>
</div>
<div itemprop="offers" itemscope itemtype="http://schema.org/Offer" class="product_rest">
<meta itemprop="priceCurrency" content="{{ shop.currency }}">
<link itemprop="availability" href="http://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}">
{% if product.tags contains 'discontinued:number:1' and product.available == false %}
{% else %}
{% if product.available %}
{% comment %}
ID addToCartForm is a selector for the ajax cart plugin
{% endcomment %}
<form action="/cart/add" method="post" enctype="multipart/form-data" id="add-item-form" class="form-vertical" >
{% include 'product-delivery-properties' %}
<p class="market-exclusive-msg">Only Available In Store</p>
<div class="variants-table">
{% comment %}
Add product variants as a dropdown.
- By default, each variant (or combination of variants) will display as its own <option>
- To separate these into multiple steps, which we suggest, use option_selection.js (see below)
You can leverage jQuery to add a callback on page load and each time the select element changes:
- Include option_selection.js (as seen at the bottom of this file)
- This allows you to use JavaScript anytime the variant dropdown changes
- This also separates out your variant options (ie. size, color, etc.) to separate select elements
For more information on products with multiple options, visit:
- http://docs.shopify.com/support/your-website/themes/can-i-make-my-theme-use-products-with-multiple-options#update-product-liquid
{% endcomment %}
<select name="id" id="productSelect" class="product-single__variants">
{% for variant in product.variants %}
{% if variant.available %}
{% comment %}
Note: if you use option_selection.js, your <select> tag will be overwritten, meaning what you have inside <option> will not reflect what you coded below.
{% endcomment %}
<option {% if variant == product.selected_or_first_available_variant %} selected="selected" {% endif %} data-sku="{{ variant.sku }}" value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money_with_currency }}</option>
{% else %}
<option disabled="disabled">
{{ variant.title }} - {{ 'products.product.sold_out' | t }}
</option>
{% endif %}
{% endfor %}
</select>
</div>
{% if product.tags contains 'product-toolbelt' %}
{% include 'magnolia-product-toolbelt' %}
{% endif %}
<div class="mobile-qty-selector">
<div class="mobile-qty-selector-wrapper">
<div class="close">Close</div>
<div class="title">Qty</div>
<div class="options-list" id="qty-skipper">
<ul>
{% for i in (1..30) %}
<li data-val="{{ i }}">{{ i }}</li>
{% endfor %}
</ul>
</div>
</div>
</div>
<div class="row collapse">
<div class="small-12 medium-6 large-5 columns qty-widget">
<div class="row quantity-selector-row">
<div class="quantity-container">
<input type="number" id="Quantity" name="quantity" value="1" min="1" class="quantity-selector large-6 small-6 columns" onblur="$(this).val( isNaN(parseFloat($(this).val())) || parseFloat($(this).val()) < 1 ? 1 : $(this).val() ) ">
<span class="plus"></span>
<span class="minus"></span>
</div>
<label for="Quantity" class="quantity-selector large-6 small-6 columns">QTY</label>
</div>
</div>
<div class="small-12 medium-5 large-6 medium-offset-1 columns combo-button">
<button type="submit" name="add" id="AddToCart" class="btn small-btn-full">
<span id="AddToCartText">{{ 'products.product.add_to_cart' | t }}</span>
</button>
</div>
</div>
</form>
<div class="row">
<div class="large-12 availability clearfix">
{% include 'product-delivery-dates' %}
</div>
</div>
{% else %}
<div class="large-12 availability clearfix"><h6>This item will be back soon.</h6></div>
{% endif %}
{% endif %}
<div class="row" id="ISR-form-wrapper"></div>
<div class="product-review-mobile">
<span class="shopify-product-reviews-badge" data-id="{{ product.id }}"></span>
</div>
<div class="row">
{% if product.tags contains 'discontinued:number:1' and product.available == false %}
<p class="discontinued">
We're sorry, but this item is no longer available! The good news is that new products are added just about every week. Thank you for understanding, and happy shopping!
</p>
{% else %} {% endif %}
<div class="product-description rte" itemprop="description">
<ul class="accordion" data-accordion>
<li class="accordion-navigation">
<a href="#overview" class="accordion-title">Overview</a>
<div id="overview" class="content active">
{{ product.description }}
</div>
</li>
{% if product.metafields.product.details %}
{% unless product.tags contains 'digital' %}
<li class="accordion-navigation">
<a href="#panel2a" class="accordion-title" >Details and Dimensions</a>
<div id="panel2a" class="content">
{{ product.metafields.product.details }}
</div>
</li>
{% endunless %}
{% endif %}
{% unless product.tags contains 'digital' %}
<li class="accordion-navigation">
<a href="#panel3a" class="accordion-title" >Shipping and Return Info</a>
<div id="panel3a" class="content product-shipping-content">
{% if product.tags contains 'alt-returns' %}
{{ pages.alternative-returns.content }}
{% else %}
{{ pages.product-page-shipping.content }}
{% endif %}
</div>
</li>
{% endunless %}
</ul>
</div>
</div>
</div>
{% comment %}
If the user is on a collection product page (ie with /collections/collection-handle/products/product-handle)
in the URL, we can show next/previous links to other products in the collection.
{% endcomment %}
{% comment %}
This is disabled for now.
{% if collection %}
{% if collection.previous_product or collection.next_product %}
<p>
{% if collection.previous_product %}
<span class="left">
{{ 'products.general.previous_product_html' | t | link_to: collection.previous_product }}
</span>
{% endif %}
{% if collection.next_product %}
<span class="right">
{{ 'products.general.next_product_html' | t | link_to: collection.next_product }}
</span>
{% endif %}
</p>
{% endif %}
{% endif %}
{% endcomment %}
</div>
</div>
</div>
<!-- product-page-related -->
{% comment %}
{% include 'product-similar-products' with 'product-page-related' %}
{% endcomment %}
<!-- Loading checkout these other great products -->
<section class="related-products expansible-related-products other-related-products">
<div class="limespot-checkout-these-other-great-products"></div>
<p class="text-center">
<a href="#" class="mg-btn btn-green btn-bordered view-more-btn">View More</a>
</p>
</section>
<div id="shopify-product-reviews" data-id="{{product.id}}">{{ product.metafields.spr.reviews }}</div>
<!-- Loading recently viewed from limespot -->
<section class="related-products six-products no-titles">
<div class="limespot-some-of-our-favorites"></div>
</section>
{% comment %}
<div id="recently-viewed" class="collection clearfix">
<h3 class="text-center">SOME OF OUR FAVORITES</h3>
<ul id="recently-viewed-products" class="small-block-grid-2 medium-block-grid-3 large-block-grid-6">
{% for product in collections.product-page-favorites.products %}
{% assign sold_out = true %}
{% if product.available %}
{% assign sold_out = false %}
{% endif %}
<li class="product">
<div class="image">
<a href="{{ product.url | within: collection }}" style="background-image: url('{{ product.featured_image.src | img_url: 'large' }}')"></a>
</div>
</li>
{% endfor %}
</ul>
</div>
{{ 'jquery.pick.js' | asset_url | script_tag }}
<script>
jQuery(function() {
jQuery('#recently-viewed-products > .product').pick(6);
});
</script>
{% endcomment %}
{% comment %}
To take advantage of a callback on the select dropdown, add option_selection.js
and customize the JS in timber.productPage as needed.
Currently, timber.productPage does the following:
- Hides your <select> tag from abovemag
- Breaks out the product variants into separate product options, if more than one exists
- Generates a <select> tag for each product option
- Enables/disables elements based on variant availability
Callback notes:
- Keep the callback available to the global scope (window.selectCallback) so that advanced
addons can override it.
* E.g. multiple currencies http://docs.shopify.com/manual/configuration/store-customization/currencies-and-translations/currencies/how-to-toggle-between-two-currencies
{% endcomment %}
{% if product.available %}
{% if product.tags contains 'discontinued:number:1' and product.available == false %}
{% else %}
{{ 'option_selection.js' | shopify_asset_url | script_tag }}
{% endif %}
{% endif %}
<script>
var optionSelector = null;
var selectedVariant = null;
{% comment %}
- If it is a fake product, we load a Json object with the real variant ids from the metafields.
{% endcomment %}
{% if product.tags contains 'fake-product' %}
var realVariants = {};
{% for variant in product.variants %}
realVariants[{{ variant.id }}] = '{{ variant.barcode }}';
{% endfor %}
console.log(realVariants);
{% endif %}
var selectCallback = function(variant, selector) {
selectedVariant = variant;
var qtySelectorRow = $(".quantity-selector-row");
var priceElementParent = $(".product-price").parent();
timber.productPage({
money_format: "{{ shop.money_format }}",
variant: variant,
selector: selector
});
// Checks the current Wishlist Status.
if ( typeof checkWishlistStatus != 'undefined' )
checkWishlistStatus( variant.id );
// Set the selected variant to use it globally.
selectedVariant = variant;
var product = {{ product | json }};
// Hide or show the stock container depending if the product is available or not.
$( '.discontinued.variants' ).remove();
$('#ISR-form-wrapper').removeClass('fully-disabled');
$( '.discontinued.dynamic' ).remove();
qtySelectorRow.parents('form').removeClass('discontinued-no-margin');
$(".availability").removeClass('discontinued-no-padding');
qtySelectorRow.parent().removeClass('discontinued-full-width').next().removeClass('discontinued-hide');
priceElementParent.removeClass('discontinued-hide-children');
if (variant.compare_at_price) {
$('.product-price-container').addClass('on-sale');
} else {
$('.product-price-container').removeClass('on-sale');
}
// Hide or show the stock container depending if the product is available or not.
if (variant && variant.available) {
$('.stock-container .stock-status.unavailable').hide();
$('.stock-container .stock-status.available').show();
} else {
$('.stock-container .stock-status.available').hide();
$('.stock-container .stock-status.unavailable').show();
}
if (variant && variant.available)
{
$('.stock-container .stock-status.unavailable').hide();
$('.stock-container .stock-status.available').show();
}
else
{
$('.stock-container .stock-status.available').hide();
// $('.stock-container .stock-status.unavailable').show();
if (product.tags.indexOf('discontinued:number:1') != -1)
{
var pBody = $('<p/>', { 'class': 'discontinued dynamic', 'text': "We're sorry, but this item is no longer available! The good news is that new products are added just about every week. Thank you for understanding, and happy shopping!" });
$('.stock-container .stock-status.unavailable').hide();
$('#ISR-form-wrapper').addClass('fully-disabled');
qtySelectorRow.parent().addClass('discontinued-full-width').next().addClass('discontinued-hide');
priceElementParent.prepend( '<span class="discontinued variants">DISCONTINUED</span>' ).addClass('discontinued-hide-children');
qtySelectorRow.parent().before(pBody);
qtySelectorRow.parents('form').addClass('discontinued-no-margin');
$(".availability").addClass('discontinued-no-padding');
}
}
if ($('body').hasClass('product-tag-product-toolbelt')) {
updateToolbeltProperties($('#productSelect-option-' + ( Shopify.shop == 'magnolia-dev-shop.myshopify.com' ? 0 : 1 )));
}
};
jQuery(function($) {
if (Shopify.OptionSelectors !== undefined && $('#productSelect').length > 0 ) {
optionSelector = new Shopify.OptionSelectors('productSelect', {
product: {{ product | json }},
onVariantSelected: selectCallback,
enableHistoryState: true
});
}
// Add label if only one product option and it isn't 'Title'. Could be 'Size'.
{% if product.options.size == 1 and product.options.first != 'Title' %}
$('.selector-wrapper:eq(0)').prepend('<label for="productSelect-option-0">{{ product.options.first | escape }}</label>');
{% endif %}
// Hide selectors if we only have 1 variant and its title contains 'Default'.
{% if product.variants.size == 1 and product.variants.first.title contains 'Default' %}
$('.variants-table, .selector-wrapper').hide();
{% endif %}
{% if product.variants.size == 1 %}
$('.variants-table').hide();
{% endif %}
$('select.single-option-selector').wrap('<div class="variant-cell"></div>');
});
$('.product-single__thumbnail').click( function( event ) {
var variant = $(this).data('variant');
if (optionSelector) {
if (variant) optionSelector.selectVariant( variant );
}
});
</script>
{% if settings.prod_enable_linked_options %}
{% include 'linked-options' %}
{% endif %}
{% if product.tags contains 'discontinued:number:1' and product.available == false %}
{% else %}
<!-- spurit_isr-added -->
{% include 'spurit_isr-product-snippet' %}
<!-- /spurit_isr-added -->
{% endif %}
{{ 'jquery.validate.min.js' | asset_url | script_tag }}
{{ 'additional-methods.min.js' | asset_url | script_tag }}
<script>
jQuery(function($) {
$(document).on('DOMNodeInserted', function(e) {
if (e.target.id == 'ISR_form') {
$label = $(e.target).find('label[for=ISR_form_email]');
$(e.target).find('#ISR_form_email').attr('placeholder', 'Email address'); //$label.text());
$label.remove();
}
});
$('#add-item-form').validate({
rules: {
'properties[_toolbelt_in_around_waist]': { 'required': true ,'min': 25, 'max': 50 },
'properties[_toolbelt_in_shoulder_waist]': { 'required': true, 'min': 15, 'max': 28 }
}
});
$('#add-item-form').submit(function (event) {
event.preventDefault();
if(!$(this).valid()) {
return false;
}
/**
*
*
*
*/
if ($('body').hasClass('product-tag-fake-product')) {
var qty = $('input.quantity-selector').val();
var variant_id = realVariants[selectedVariant.id];
var properties = {
"_shipping_delivery_days": $('input[name="properties[_shipping_delivery_days]"]').val()
}
Magnolia.Cart.addItem( variant_id, qty, properties, function ( line_item ) {
if ( $('body').data('mini-cart') == 1 ) {
$('#mini-cart').trigger('minicart.show', [line_item, qty] );
} else {
Shopify.onItemAdded ( line_item );
}
}, function( error ) {
alert ( error.responseJSON.description );
});
// console.log ( properties ); return false;
// Shopify.addItem( variant_id, qty );
} else {
Shopify.addItemFromForm( 'add-item-form' );
}
});
});
$(document).ready( function() {
// If the
if (window.location.hash == '#new-review') {
// Let's wait until the badge loads. Once it loads we toggle
// the form.
var $interval = setInterval(function() {
if ($('.spr-summary-actions-newreview').length > 0 && typeof SPR !== 'undefined') {
clearInterval( $interval );
$('.spr-summary-actions-newreview').click();
$('html, body').animate({
scrollTop: $('.spr-form').offset().top - 40
}, 0);
}
}, 1000);
}
});
</script>
{% endif %}
<!-- Logic for .usdz (AR Quicklook) support on Apple iOS 12
Only loads for products in the ar specific collection
-->
{% if collection.handle == 'arkit' %}
<style>
#product-photo-expander {
display: none;
}
.shopify-payment-button__more-options {
display: none;
}
@media only screen and (min-width: 481px) {
.shopify-payment-button__button {
margin-bottom: 8px;
}
}
</style>
<script>
function isIOS12() {
if (/iP(hone|od|ad)/.test(navigator.platform)) {
var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
return parseInt(v[1], 10) >= 12;
} else {
return false;
}
}
function getUSDZURLForVariant(variantId) {
return "https://storage.googleapis.com/magnolia-3d-models/staging/usdz/" + variantId + ".usdz";
}
if (isIOS12()) {
$(document).ready( function() {
// Prevent the hover image from being expandable
$(".product-single").addClass("no-expand");
// Wrap each product image with a usdz link.
// If the image is for a specific variant, it will link to the usdz for that variant
// If the image is not for a specific variant, it links to the default variant's usdz
$("#ProductPhotoHover img, .mobile-slideshow img").each(function(index, el) {
var variantId = $(el).data('variant') || {{current_variant.id}};
$(el).wrap("<a href='" + getUSDZURLForVariant(variantId) + "' rel='ar'></a>");
});
// For screensizes that use the photo hover image, listen to when the image changes
// in order to update the usdz url.
$("#ProductPhotoHover img").on('load', function() {
var variantId = selectedVariant.id || {{current_variant.id}};
$(this).parent().attr("href", getUSDZURLForVariant(variantId));
});
// For the usdz demo, show the Apple Pay button. This uses Shopify's dynamic checkout.
$("#AddToCart").parent().before("<div class='small-12 medium-5 large-6 medium-offset-1 columns combo-button'><div data-shopify='payment-button'></div></div>");
});
}
</script>
{% endif %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment