├── .editorconfig ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── clean_data ├── _custom_overrides.scss.liquid ├── additional_footer_content.liquid ├── additional_header_content.liquid └── settings_data.json ├── gulpfile.js ├── package.json ├── settings_schema ├── advanced.json ├── blog.json ├── cart.json ├── collections.json ├── colors.json ├── footer.json ├── general.json ├── homepage.json ├── homepage_banner.json ├── homepage_video_banner.json ├── layout.json ├── list_collections.json ├── navigation.json ├── newsletter.json ├── pages.json ├── products.json ├── search.json ├── social.json ├── theme_info.json └── thumbnails.json ├── src ├── js │ ├── _polyfills.js │ ├── app.js │ ├── bsify.banner.js │ ├── bsify.banner_video.js │ ├── bsify.cart.js │ ├── bsify.image_switcher.js │ ├── bsify.js │ ├── bsify.linked_product_options.js │ ├── bsify.lookbook_gallery.js │ ├── bsify.media_queries.js │ ├── bsify.mega_menu.js │ ├── bsify.modal.js │ ├── bsify.password_recover.js │ ├── bsify.scroll_to.js │ ├── bsify.social_feed_instagram.js │ ├── bsify.social_feeds.js │ ├── bsify.thumbnails.js │ ├── bsify.touch.js │ └── bsify.variant_option_switcher.js └── scss │ ├── base │ ├── _foundation.scss.liquid │ ├── _mixins.scss.liquid │ ├── _typography.scss.liquid │ └── _variables.scss.liquid │ ├── giftcard.scss │ ├── layout │ ├── _headers.scss.liquid │ ├── _layout.scss.liquid │ ├── _navigation.scss.liquid │ ├── _sticky_footer.scss.liquid │ ├── collection_headers │ │ ├── _banner.scss.liquid │ │ └── _split.scss.liquid │ └── site_headers │ │ ├── _centered.scss.liquid │ │ ├── _logo_left.scss.liquid │ │ ├── _navbar.scss.liquid │ │ └── _stacked.scss.liquid │ ├── module │ ├── _banner.scss.liquid │ ├── _forms.scss.liquid │ ├── _gift_card.scss.liquid │ ├── _images.scss.liquid │ ├── _mega_menu.scss.liquid │ └── _social.scss.liquid │ ├── styles.scss │ └── theme │ ├── _custom_overrides.scss.liquid │ ├── _custom_variables.scss.liquid │ └── _overrides.scss.liquid ├── theme ├── assets │ ├── _polyfills.js │ ├── app.js │ ├── app.min.js │ ├── bootstrap.min.js │ ├── bsify.banner.js │ ├── bsify.banner_video.js │ ├── bsify.cart.js │ ├── bsify.image_switcher.js │ ├── bsify.js │ ├── bsify.linked_product_options.js │ ├── bsify.lookbook_gallery.js │ ├── bsify.media_queries.js │ ├── bsify.mega_menu.js │ ├── bsify.modal.js │ ├── bsify.password_recover.js │ ├── bsify.scroll_to.js │ ├── bsify.social_feed_instagram.js │ ├── bsify.social_feeds.js │ ├── bsify.thumbnails.js │ ├── bsify.touch.js │ ├── bsify.variant_option_switcher.js │ ├── cart.js │ ├── giftcard.scss.liquid │ ├── html5shiv.js │ ├── ico-gift-card.svg.liquid │ ├── jquery.min.js │ ├── jquery.min.map │ ├── modernizr.js │ ├── picturefill.min.js │ ├── respond-proxy.html │ ├── respond.min.js │ ├── styles.scss.liquid │ ├── styles_ie_1.css │ └── styles_ie_2.css ├── config │ ├── settings_data.json │ └── settings_schema.json ├── layout │ └── theme.liquid ├── locales │ └── en.default.json ├── snippets │ ├── _collection_title_filters.liquid │ ├── _extract_images.liquid │ ├── _has_menu.liquid │ ├── _page_url.liquid │ ├── _price.liquid │ ├── _price_with_currency.liquid │ ├── additional_footer_content.liquid │ ├── additional_header_content.liquid │ ├── advanced_hook.liquid │ ├── advanced_layout.liquid │ ├── advanced_layout_banner.liquid │ ├── advanced_layout_banner_video.liquid │ ├── advanced_layout_blog.liquid │ ├── advanced_layout_collections.liquid │ ├── advanced_layout_page.liquid │ ├── advanced_layout_products.liquid │ ├── advanced_layout_search.liquid │ ├── advanced_layout_section.liquid │ ├── advanced_layout_social.liquid │ ├── advanced_layout_sub_blog.liquid │ ├── advanced_layout_sub_page.liquid │ ├── advanced_layout_sub_search.liquid │ ├── advanced_layout_sub_social.liquid │ ├── article_comment.liquid │ ├── article_comments.liquid │ ├── article_comments_form.liquid │ ├── article_item.liquid │ ├── article_meta.liquid │ ├── article_recent.liquid │ ├── article_social_share_links.liquid │ ├── collection_controls.liquid │ ├── collection_controls_filter_by.liquid │ ├── collection_controls_filter_by_tag.liquid │ ├── collection_controls_filter_by_tag_group.liquid │ ├── collection_controls_sort_by.liquid │ ├── collection_controls_template_switcher.liquid │ ├── collection_header_component_title.liquid │ ├── collection_header_layout_banner_description.liquid │ ├── collection_header_layout_banner_image.liquid │ ├── collection_header_layout_split.liquid │ ├── collection_header_layout_standard.liquid │ ├── collection_item.liquid │ ├── collection_page.liquid │ ├── contact_form.liquid │ ├── content_sections.liquid │ ├── content_split.liquid │ ├── content_tabs.liquid │ ├── customer_addresses_form.liquid │ ├── form_errors.liquid │ ├── header_component_brand.liquid │ ├── header_component_megamenu.liquid │ ├── header_component_mobile_navbar.liquid │ ├── header_component_navbar.liquid │ ├── header_layout_centered.liquid │ ├── header_layout_logo_left.liquid │ ├── header_layout_navbar.liquid │ ├── header_layout_stacked.liquid │ ├── homepage_onboarding.liquid │ ├── link_list.liquid │ ├── nav_list.liquid │ ├── page_banner.liquid │ ├── pagination.liquid │ ├── pagination_limit.liquid │ ├── product_badges.liquid │ ├── product_description.liquid │ ├── product_item.liquid │ ├── product_item_list.liquid │ ├── product_price.liquid │ ├── product_price_variant.liquid │ ├── product_social_share_links.liquid │ ├── respond.liquid │ ├── search_form.liquid │ ├── search_result_page.liquid │ ├── search_results_empty.liquid │ ├── site_blog_list.liquid │ ├── site_footer.liquid │ ├── site_js_vars.liquid │ ├── site_newsletter_form.liquid │ ├── site_social_links.liquid │ ├── site_title.liquid │ ├── site_vars.liquid │ ├── social_content.liquid │ ├── social_feed.liquid │ ├── social_feed_facebook.liquid │ ├── social_feed_instagram.liquid │ ├── social_feed_twitter.liquid │ ├── social_feed_vimeo.liquid │ ├── social_feed_youtube.liquid │ └── social_service.liquid └── templates │ ├── 404.liquid │ ├── article.liquid │ ├── blog.liquid │ ├── cart.liquid │ ├── collection.liquid │ ├── collection.list.liquid │ ├── collection.lookbook.liquid │ ├── collection.nested-collections.liquid │ ├── collection.nested-products.liquid │ ├── customers │ ├── account.liquid │ ├── activate_account.liquid │ ├── addresses.liquid │ ├── login.liquid │ ├── order.liquid │ ├── register.liquid │ └── reset_password.liquid │ ├── gift_card.liquid │ ├── index.liquid │ ├── list-collections.liquid │ ├── page.contact-modal.liquid │ ├── page.contact.liquid │ ├── page.full-width.liquid │ ├── page.liquid │ ├── page.modal.liquid │ ├── page.split.liquid │ ├── password.liquid │ ├── product.liquid │ └── search.liquid └── utils ├── blessify.js └── sass_import.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | insert_final_newline = false 10 | ensure_newline_at_eof_on_save = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | Icon? 37 | ehthumbs.db 38 | Thumbs.db 39 | 40 | # Custom # 41 | ###################### 42 | *.sass-cache* 43 | /node_modules 44 | /bower_components 45 | grunt-config.json 46 | theme/config*.yml 47 | /_site 48 | assets/logo.png 49 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'shopify_theme' -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.3.6) 5 | filewatcher (0.3.6) 6 | trollop (~> 2.0) 7 | httparty (0.13.3) 8 | json (~> 1.8) 9 | multi_xml (>= 0.5.2) 10 | json (1.8.2) 11 | launchy (2.4.3) 12 | addressable (~> 2.3) 13 | mimemagic (0.2.1) 14 | multi_xml (0.5.5) 15 | shopify_theme (0.0.21) 16 | filewatcher 17 | httparty (~> 0.13.0) 18 | json (~> 1.8.0) 19 | launchy 20 | mimemagic 21 | thor (>= 0.14.4) 22 | thor (0.19.1) 23 | trollop (2.1.1) 24 | 25 | PLATFORMS 26 | ruby 27 | 28 | DEPENDENCIES 29 | shopify_theme 30 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | THIS IS NOT THE BUILD TOOL YOU ARE LOOKING FOR. 4 | 5 | We have a Grunt task set up specifcally to run modernizer because the gulp one sucked. 6 | This should be run via gulp. 7 | 8 | */ 9 | 10 | module.exports = function(grunt) { 11 | 12 | grunt.initConfig({ 13 | modernizr: { 14 | dist: { 15 | "devFile" : "remote", 16 | "outputFile" : "theme/assets/modernizr.js", 17 | "extra" : { 18 | "load" : false 19 | }, 20 | "files" : { 21 | "src": ['./src/**/*.*'] 22 | } 23 | } 24 | } 25 | }); 26 | 27 | grunt.loadNpmTasks("grunt-modernizr"); 28 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2006-2014 Shopify Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bootstrapify 4 2 | 3 | Bootstrapify 4 ~~is~~ _was_ going to be a complete rebuild of the popular Shopify theme framework ready for Bootstrap 4. 4 | However while in development we realised two things: 5 | 6 | 1. Shopify's version of sass would mean that we can't use Bootstrap 4, unless we compiled locally. 7 | 2. Bootstrap is not always the right tool for the job. 8 | 9 | But we wanted to release this anyway. 10 | Don't despair because it is stable-ish and has successfully been used [in](http://www.liannbellis.co.nz/) [the](http://www.laineehermsen.co.nz/) [wild](http://www.phd.co.nz/). 11 | 12 | So if it is the right tool for _your_ job then you are more than welcome to use it. If you do, then let us know because we'd like to see what you've done. 13 | 14 | ## Setup 15 | 16 | 1. Clone the bsify4 repo 17 | 18 | git clone https://github.com/luciddesign/Bootstrapify-4.git 19 | 20 | 2. Install dependencies 21 | 22 | bundle && npm install && bower install 23 | 24 | 3. Profit! 25 | 26 | ## Workflow 27 | 28 | Run `gulp`. That is all. 29 | 30 | Want a zipped file of just the theme to upload to shopify? Run `gulp build`. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ****** 41 | 42 | Class names and Id's use dashes: my-class 43 | Liquid variables and includes use underscores: my_snippet 44 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrapify", 3 | "version": "4.0.9", 4 | "keywords": [ 5 | "shopify", 6 | "theme", 7 | "framework", 8 | "css", 9 | "js", 10 | "sass", 11 | "mobile-first", 12 | "responsive", 13 | "front-end", 14 | "lucid" 15 | ], 16 | "main": [ 17 | "theme/assets/*", 18 | "theme/config/*", 19 | "theme/layout/*", 20 | "theme/snippets/*", 21 | "theme/templates/*", 22 | "theme/locales/*" 23 | ], 24 | "ignore": [ 25 | ".*" 26 | ], 27 | "homepage": "https://github.com/luciddesign/Bootstrapify-4", 28 | "authors": [ 29 | "Galen King ", 30 | "Stewart Knapman " 31 | ], 32 | "description": "Shopify theme framework based on Bootstrap 4", 33 | "license": "MIT", 34 | "dependencies": { 35 | "bootstrap-sass": "3.3.4", 36 | "respond": "https://github.com/scottjehl/Respond.git#~1.4.2", 37 | "picturefill": "https://github.com/scottjehl/picturefill.git#~2.2.0", 38 | "shopify-cartjs": "~0.2.3", 39 | "html5shiv": "~3.7.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /clean_data/_custom_overrides.scss.liquid: -------------------------------------------------------------------------------- 1 | /* 2 | Put your project specific custom overrides here 3 | */ -------------------------------------------------------------------------------- /clean_data/additional_footer_content.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Additional Footer Content 4 | Add any custom and project specific scripts that you want included at the end of the theme. 5 | 6 | %}{% endcomment %} -------------------------------------------------------------------------------- /clean_data/additional_header_content.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Additional Header Content 4 | Add any custom and project specific inline scripts and styles that you want included inside the theme head. 5 | 6 | %}{% endcomment %} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bootstrapify4", 3 | "version": "4.0.9", 4 | "description": "Shopify theme framework based on Bootstrap 4", 5 | "main": [ 6 | "theme/assets/*", 7 | "theme/config/*", 8 | "theme/layout/*", 9 | "theme/snippets/*", 10 | "theme/templates/*", 11 | "theme/locales/*" 12 | ], 13 | "scripts": { 14 | "test": "echo \"Error: no test specified\" && exit 1" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/luciddesign/Bootstrapify-4.git" 19 | }, 20 | "authors": [ 21 | "Galen King ", 22 | "Stewart Knapman " 23 | ], 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/luciddesign/Bootstrapify-4/issues" 27 | }, 28 | "homepage": "https://github.com/luciddesign/Bootstrapify-4", 29 | "devDependencies": { 30 | "bless": "^3.0.3", 31 | "browserify": "^9.0.3", 32 | "cheerio": "^0.19.0", 33 | "debowerify": "^1.2.0", 34 | "grunt": "^0.4.5", 35 | "grunt-modernizr": "^0.6.0", 36 | "gulp": "^3.8.10", 37 | "gulp-concat": "^2.4.3", 38 | "gulp-grunt": "^0.5.2", 39 | "gulp-if": "^1.2.5", 40 | "gulp-jasmine": "^2.0.1", 41 | "gulp-jshint": "^1.9.0", 42 | "gulp-jsoncombine": "^1.0.2", 43 | "gulp-plumber": "^0.6.6", 44 | "gulp-rename": "^1.2.0", 45 | "gulp-uglify": "^1.1.0", 46 | "gulp-util": "^3.0.2", 47 | "gulp-zip": "^2.0.2", 48 | "js-yaml": "^3.4.3", 49 | "request": "^2.65.0", 50 | "uglifyify": "^3.0.1", 51 | "vinyl-source-stream": "^1.1.0", 52 | "yargs": "^3.9.1" 53 | }, 54 | "dependencies": { 55 | "imagesloaded": "^3.1.8", 56 | "jquery-orderly": "git+https://github.com/luciddesign/orderly.git", 57 | "jquery.mb.ytplayer": "git+https://github.com/stewartknapman/jquery.mb.YTPlayer.git", 58 | "uniformThumbnails": "git+https://github.com/luciddesign/uniform-thumbnails.git" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /settings_schema/advanced.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Advanced", 3 | "settings": [ 4 | { 5 | "type": "header", 6 | "content": "Style settings" 7 | }, 8 | { 9 | "type": "paragraph", 10 | "content": "This theme uses sass. [ TODO: make this wording better ]" 11 | }, 12 | { 13 | "label": "SCSS variable", 14 | "id": "scss_variables", 15 | "type": "textarea", 16 | "info": "Set or override the standard scss variables" 17 | }, 18 | { 19 | "label": "CSS overrides", 20 | "id": "scss_overrides", 21 | "type": "textarea", 22 | "info": "Override the standard css with your custom styles" 23 | }, 24 | { 25 | "type": "header", 26 | "content": "Header and footer code" 27 | }, 28 | { 29 | "type": "paragraph", 30 | "content": "Insert additional code into the header and footer of your theme" 31 | }, 32 | { 33 | "label": "Header code", 34 | "id": "header_code", 35 | "type": "textarea" 36 | }, 37 | { 38 | "label": "Footer code", 39 | "id": "footer_code", 40 | "type": "textarea" 41 | } 42 | ] 43 | } -------------------------------------------------------------------------------- /settings_schema/cart.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cart", 3 | "settings": [ 4 | { 5 | "type": "paragraph", 6 | "content": "The cart checkbox is for when you want your customers to acknowledge some information before proceeding to the checkout. This maybe a disclaimer, shipping requirements or a note about allergens. You will need to update the content to suit your situation. This can be changed inside the themes language settings under Cart." 7 | }, 8 | { 9 | "label": "Enable cart checkbox", 10 | "id": "enable_cart_checkbox", 11 | "type": "checkbox", 12 | "default": false, 13 | "info": "If enabled then your customers will need to select a checkbox before moving to the checkout." 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /settings_schema/colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Colors", 3 | "settings": [ 4 | { 5 | "label": "Brand primary color", 6 | "id": "brand_primary_color", 7 | "type": "color" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /settings_schema/general.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "General", 3 | "settings": [ 4 | { 5 | "type": "header", 6 | "content": "Branding" 7 | }, 8 | { 9 | "label": "Logo image", 10 | "id": "logo.png", 11 | "type": "image" 12 | }, 13 | { 14 | "label": "Use logo image", 15 | "id": "logo_enabled", 16 | "type": "checkbox", 17 | "default": false 18 | }, 19 | { 20 | "label": "Logo max height", 21 | "id": "logo_max_height", 22 | "type": "text", 23 | "default": "150px", 24 | "info": "This setting constrains the height of your logo allowing you to make it bigger or smaller. Default is 150px high." 25 | }, 26 | { 27 | "label": "Site Favicon", 28 | "id": "favicon.png", 29 | "type": "image" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /settings_schema/homepage_video_banner.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Homepage - Video Banner", 3 | "settings": [ 4 | { 5 | "type": "paragraph", 6 | "content": "TODO: Video Banner info." 7 | }, 8 | { 9 | "type": "paragraph", 10 | "content": "The homepage banners placement in the pages layout can be controled under the homepage settings above." 11 | }, 12 | { 13 | "label": "Video URL", 14 | "id": "video_banner_url", 15 | "type": "text", 16 | "info": "TODO: something about using youtube urls" 17 | }, 18 | { 19 | "label": "Fallback image", 20 | "id": "video_banner_img.jpg", 21 | "type": "image", 22 | "info": "The fallback image is displayed on older browsers and touch screen devices" 23 | }, 24 | { 25 | "label": "Caption alignment", 26 | "id": "video_banner_alignment", 27 | "type": "select", 28 | "options": [ 29 | { "value": "left", "label": "Left"}, 30 | { "value": "center", "label": "Center"}, 31 | { "value": "right", "label": "Right"} 32 | ], 33 | "default": "center" 34 | }, 35 | { 36 | "label": "Caption vertical alignment", 37 | "id": "video_banner_vertical_alignment", 38 | "type": "select", 39 | "options": [ 40 | { "value": "top", "label": "Top"}, 41 | { "value": "middle", "label": "Middle"}, 42 | { "value": "bottom", "label": "Bottom"} 43 | ], 44 | "default": "middle" 45 | }, 46 | { 47 | "label": "Caption heading text", 48 | "id": "video_banner_heading_text", 49 | "type": "text" 50 | }, 51 | { 52 | "label": "Caption text", 53 | "id": "video_banner_caption_text", 54 | "type": "textarea" 55 | }, 56 | { 57 | "label": "'Call to action' button text", 58 | "id": "video_banner_button_text", 59 | "type": "text" 60 | }, 61 | { 62 | "label": "'Call to action' button link", 63 | "id": "video_banner_button_link", 64 | "type": "text", 65 | "info": "If you want to link to a collection then enter /collection/my-collection" 66 | }, 67 | { 68 | "label": "Caption text colour", 69 | "id": "video_banner_text_colour", 70 | "type": "color" 71 | }, 72 | { 73 | "label": "'Call to action' button colour", 74 | "id": "video_banner_button_colour", 75 | "type": "color" 76 | } 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /settings_schema/layout.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Layout", 3 | "settings": [ 4 | { 5 | "type": "header", 6 | "content": "Site Layout" 7 | }, 8 | { 9 | "label": "Container width", 10 | "id": "container_width", 11 | "type": "select", 12 | "options": [ 13 | { "value": "fixed", "label": "Fixed width" }, 14 | { "value": "fluid", "label": "Full width" } 15 | ], 16 | "default": "fixed", 17 | "info": "The container holds the width of your content. A fixed width conatiner will stop the content from getting to wide. Full width will allow your content to always flow out to the edge of the screen no matter how big it is." 18 | }, 19 | { 20 | "type": "header", 21 | "content": "Site Header Layout" 22 | }, 23 | { 24 | "label": "Select a header and navigation layout", 25 | "id": "header_layout", 26 | "type": "select", 27 | "options": [ 28 | { "value": "header_layout_navbar", "label": "Navbar" }, 29 | { "value": "header_layout_centered", "label": "Centered" }, 30 | { "value": "header_layout_stacked", "label": "Stacked" }, 31 | { "value": "header_layout_logo_left", "label": "Logo Left" } 32 | ], 33 | "default": "header_layout_centered", 34 | "info": "You can see each option here [TODO: add link]" 35 | }, 36 | { 37 | "type": "header", 38 | "content": "Column Layout" 39 | }, 40 | { 41 | "label": "Enable adaptive columns", 42 | "id": "enable_adaptive_columns", 43 | "type": "checkbox", 44 | "default": true, 45 | "info": "Products, collections and homepage content will all be effected by these settings." 46 | }, 47 | { 48 | "type": "paragraph", 49 | "content": "If adaptive columns is enabled then column widths will be calculated based on the amount of the items in each collection; this will reduce the empty columns created by small amounts of content. If disabled then column widths will use the column settings below." 50 | }, 51 | { 52 | "label": "Xtra-small screen column count", 53 | "id": "col_count_xs", 54 | "type": "select", 55 | "options": [ 56 | { "value": "1", "label": "1 column" }, 57 | { "value": "2", "label": "2 columns" }, 58 | { "value": "3", "label": "3 columns" }, 59 | { "value": "4", "label": "4 columns" }, 60 | { "value": "6", "label": "6 columns" } 61 | ], 62 | "default": "1" 63 | }, 64 | { 65 | "label": "Small screen column count", 66 | "id": "col_count_sm", 67 | "type": "select", 68 | "options": [ 69 | { "value": "1", "label": "1 column" }, 70 | { "value": "2", "label": "2 columns" }, 71 | { "value": "3", "label": "3 columns" }, 72 | { "value": "4", "label": "4 columns" }, 73 | { "value": "6", "label": "6 columns" } 74 | ], 75 | "default": "2" 76 | }, 77 | { 78 | "label": "Medium screen column count", 79 | "id": "col_count_md", 80 | "type": "select", 81 | "options": [ 82 | { "value": "1", "label": "1 column" }, 83 | { "value": "2", "label": "2 columns" }, 84 | { "value": "3", "label": "3 columns" }, 85 | { "value": "4", "label": "4 columns" }, 86 | { "value": "6", "label": "6 columns" } 87 | ], 88 | "default": "4" 89 | }, 90 | { 91 | "label": "Large screen column count", 92 | "id": "col_count_lg", 93 | "type": "select", 94 | "options": [ 95 | { "value": "1", "label": "1 column" }, 96 | { "value": "2", "label": "2 columns" }, 97 | { "value": "3", "label": "3 columns" }, 98 | { "value": "4", "label": "4 columns" }, 99 | { "value": "6", "label": "6 columns" } 100 | ], 101 | "default": "4" 102 | } 103 | ] 104 | } -------------------------------------------------------------------------------- /settings_schema/list_collections.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "List Collections", 3 | "settings": [ 4 | { 5 | "type": "paragraph", 6 | "content": "The 'List Collections' template shows all of your collections and is availble at yourshop.com/collections" 7 | }, 8 | { 9 | "label": "Show collections product count", 10 | "id": "show_collection_product_count", 11 | "type": "checkbox", 12 | "default": true 13 | }, 14 | { 15 | "label": "Featured collection", 16 | "id": "list_collections_featured_collection", 17 | "type": "link_list", 18 | "info": "If a link list of collections is selected then the list collections template will show just the collections in this list. If it is set to none or the link list is empty then the template will show all collections." 19 | }, 20 | { 21 | "type": "header", 22 | "content": "Layout" 23 | }, 24 | { 25 | "label": "Header layout", 26 | "id": "list_collections_header_layout", 27 | "type": "select", 28 | "options": [ 29 | { "value": "standard", "label": "Standard" }, 30 | { "value": "split_image_left", "label": "Split image left" }, 31 | { "value": "split_image_right", "label": "Split image right" }, 32 | { "value": "banner_image", "label": "Banner image above description" }, 33 | { "value": "banner_description", "label": "Banner containing description" } 34 | ], 35 | "default": "standard" 36 | }, 37 | { 38 | "label": "Header description alignment", 39 | "id": "list_collection_header_description_alignment", 40 | "type": "select", 41 | "options": [ 42 | { "value": "top_left", "label": "Top Left" }, 43 | { "value": "top_center", "label": "Top Center" }, 44 | { "value": "top_right", "label": "Top Right" }, 45 | { "value": "middle_left", "label": "Middle Left" }, 46 | { "value": "middle_center", "label": "Middle Center" }, 47 | { "value": "middle_right", "label": "Middle Right" }, 48 | { "value": "bottom_left", "label": "Bottom Left" }, 49 | { "value": "bottom_center", "label": "Bottom Center" }, 50 | { "value": "bottom_right", "label": "Bottom Right" } 51 | ], 52 | "default": "middle_center", 53 | "info": "IMPORTANT: This setting only applies if your header layout is set to 'Banner containing description'" 54 | }, 55 | { 56 | "label": "Header image", 57 | "id": "list_collections_banner_image.jpg", 58 | "type": "image" 59 | } 60 | ] 61 | } -------------------------------------------------------------------------------- /settings_schema/navigation.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Navigation", 3 | "settings": [ 4 | { 5 | "type": "paragraph", 6 | "content": "If all menus are set to none then the main menu will default to the linklist with the handle 'main-menu'." 7 | }, 8 | { 9 | "label": "Main Menu", 10 | "id": "main_menu_linklist", 11 | "type": "link_list" 12 | }, 13 | { 14 | "label": "Secondary Menu", 15 | "id": "secondary_menu_linklist", 16 | "type": "link_list" 17 | }, 18 | { 19 | "label": "Tertiary Menu", 20 | "id": "tertiary_menu_linklist", 21 | "type": "link_list" 22 | }, 23 | { 24 | "label": "Enable MegaMenu", 25 | "id": "enable_megamenu", 26 | "type": "checkbox", 27 | "default": false 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /settings_schema/newsletter.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Newsletter", 3 | "settings": [ 4 | { 5 | "label": "Newsletter platform", 6 | "id": "newsletter_platform", 7 | "type": "select", 8 | "options": [ 9 | { "value": "shopify", "label": "Shopify" }, 10 | { "value": "campaign-monitor", "label": "Campaign Monitor" }, 11 | { "value": "mailchimp", "label": "MailChimp" } 12 | ], 13 | "default": "shopify" 14 | }, 15 | { 16 | "label": "Newsletter form url", 17 | "id": "newsletter_form_action", 18 | "type": "text", 19 | "info": "To use Campaign Monitor & MailChimp you will need to include the forms action url" 20 | }, 21 | { 22 | "type": "paragraph", 23 | "content": "To find your Campaign Monitor form action url [read the documentation here](#). TODO: link to documentation" 24 | }, 25 | { 26 | "type": "paragraph", 27 | "content": "To find your MailChimp form action url [read the documentation here](http://docs.shopify.com/support/configuration/store-customization/where-do-i-get-my-mailchimp-form-action)." 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /settings_schema/pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Pages", 3 | "settings": [ 4 | { 5 | "type": "header", 6 | "content": "Header Layout" 7 | }, 8 | { 9 | "label": "Use first image as a banner", 10 | "id": "enable_page_banner", 11 | "type": "checkbox", 12 | "default": false, 13 | "info": "When checked the first image from the page will be pulled out of the content and displayed as a fullscreen banner above the article" 14 | }, 15 | { 16 | "label": "Show page title over banner", 17 | "id": "show_page_title_on_banner", 18 | "type": "checkbox", 19 | "default": false 20 | }, 21 | { 22 | "label": "Page title alignment", 23 | "id": "page_title_alignment", 24 | "type": "select", 25 | "options": [ 26 | { "value": "top_left", "label": "Top Left" }, 27 | { "value": "top_center", "label": "Top Center" }, 28 | { "value": "top_right", "label": "Top Right" }, 29 | { "value": "middle_left", "label": "Middle Left" }, 30 | { "value": "middle_center", "label": "Middle Center" }, 31 | { "value": "middle_right", "label": "Middle Right" }, 32 | { "value": "bottom_left", "label": "Bottom Left" }, 33 | { "value": "bottom_center", "label": "Bottom Center" }, 34 | { "value": "bottom_right", "label": "Bottom Right" } 35 | ], 36 | "default": "middle_center" 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /settings_schema/products.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Products", 3 | "settings": [ 4 | { 5 | "type": "header", 6 | "content": "Layout" 7 | }, 8 | { 9 | "label": "Image layout", 10 | "id": "product_image_layout", 11 | "type": "select", 12 | "options": [ 13 | { "value": "left", "label": "Left" }, 14 | { "value": "center", "label": "Center" }, 15 | { "value": "right", "label": "Right" } 16 | ], 17 | "default": "left" 18 | }, 19 | { 20 | "label": "Product content layout", 21 | "id": "product_content_layout", 22 | "type": "select", 23 | "options": [ 24 | { "value": "standard", "label": "Standard" }, 25 | { "value": "tabs", "label": "Tabs" }, 26 | { "value": "sections", "label": "Sections" } 27 | ], 28 | "default": "standard", 29 | "info": "If Tabs or Sections are selected the product description will be split based on heading 2 blocks. Tabs will show the heading 2 as the tabs title." 30 | }, 31 | { 32 | "type": "header", 33 | "content": "Social share links" 34 | }, 35 | { 36 | "label": "Enable Twitter link", 37 | "id": "product_social_share_twitter", 38 | "type": "checkbox", 39 | "default": true 40 | }, 41 | { 42 | "label": "Enable Facebook link", 43 | "id": "product_social_share_facebook", 44 | "type": "checkbox", 45 | "default": true 46 | }, 47 | { 48 | "label": "Enable Pinterest link", 49 | "id": "product_social_share_pinterest", 50 | "type": "checkbox", 51 | "default": true 52 | }, 53 | { 54 | "label": "Enable email link", 55 | "id": "product_social_share_email", 56 | "type": "checkbox", 57 | "default": true 58 | }, 59 | { 60 | "label": "Hide pricing and add to cart button if price is $0.00", 61 | "id": "hide_pricing", 62 | "type": "checkbox", 63 | "default": true 64 | } 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /settings_schema/search.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Search", 3 | "settings": [ 4 | { 5 | "label": "Show search in navigation", 6 | "id": "enable_search", 7 | "type": "checkbox", 8 | "default": true 9 | }, 10 | { 11 | "label": "Search typeahead collection", 12 | "id": "search_typeahead_collecton", 13 | "type": "collection", 14 | "info": "To help users search your site you can select a collection that will gives answers as they type" 15 | }, 16 | { 17 | "label": "Search pagination limit", 18 | "id": "pagination_limit_search", 19 | "type": "text", 20 | "default": "50" 21 | }, 22 | { 23 | "label": "Truncate words for page content in results", 24 | "id": "truncate_page_results", 25 | "type": "text", 26 | "default": "200", 27 | "info": "This will stop pages with long content from making the results to long. If left blank then the whole pages content will be shown" 28 | }, 29 | { 30 | "label": "Empty search collection", 31 | "id": "search_collection", 32 | "type": "collection", 33 | "info": "Select a collection to show on the search page if there are no results" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /settings_schema/social.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Social", 3 | "settings": [ 4 | { 5 | "type": "paragraph", 6 | "content": "Enter the web address of your social media pages to have the links appear" 7 | }, 8 | { 9 | "label": "Facebook", 10 | "id": "social_links_facebook", 11 | "type": "text" 12 | }, 13 | { 14 | "label": "Instagram", 15 | "id": "social_links_instagram", 16 | "type": "text" 17 | }, 18 | { 19 | "label": "Pinterest", 20 | "id": "social_links_pinterest", 21 | "type": "text" 22 | }, 23 | { 24 | "label": "Twitter", 25 | "id": "social_links_twitter", 26 | "type": "text" 27 | }, 28 | { 29 | "label": "Email", 30 | "id": "social_links_email", 31 | "type": "text" 32 | }, 33 | { 34 | "label": "RSS", 35 | "id": "social_links_rss", 36 | "type": "blog", 37 | "info": "Select a blog to link to an RSS feed" 38 | }, 39 | { 40 | "type": "header", 41 | "content": "Instagram Feed" 42 | }, 43 | { 44 | "type": "paragraph", 45 | "content": "To include an instagram feed on your site you will need a client id. You can get a client id by registering a new Instagram API client app at [http://instagram.com/developer/clients/register/](http://instagram.com/developer/clients/register/)" 46 | }, 47 | { 48 | "label": "Client ID", 49 | "id": "instagram_client_id", 50 | "type": "text" 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /settings_schema/theme_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "theme_info", 3 | "logo": "https:\/\/cdn.shopify.com\/s\/files\/1\/0000\/1712\/files\/Lucid-logo-03-512px.png", 4 | "settings": [ 5 | { 6 | "type": "header", 7 | "content": "Bootstrapify 4.0.9 by [Lucid](http:\/\/lucid.co.nz)" 8 | }, 9 | { 10 | "type": "paragraph", 11 | "content": "For theme support please contact us at [shopify@lucid.co.nz](mailto:shopify@lucid.co.nz)" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /settings_schema/thumbnails.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Thumbnails", 3 | "settings": [ 4 | { 5 | "type": "paragraph", 6 | "content": "The thumbnails settings effects both the product and collections thumbs." 7 | }, 8 | { 9 | "label": "Product pagination limit", 10 | "id": "pagination_limit_products", 11 | "type": "text", 12 | "default": "48", 13 | "info": "The maximum amount of thumbs you can show on a page is 50. For best results use a multiple of your thumbs column count." 14 | }, 15 | { 16 | "label": "Nested collection product limit", 17 | "id": "nested_collections_product_limit", 18 | "type": "text", 19 | "default": "12", 20 | "info": "This is only applied to collections with the template of 'nested-products' and will limit each nested collections product length. The maximum is 50." 21 | }, 22 | { 23 | "type": "header", 24 | "content": "Images" 25 | }, 26 | { 27 | "type": "paragraph", 28 | "content": "The 'Crop', 'Ratio' & 'Alignment' settings will only be used if 'Constrain images to the same size' is enabled." 29 | }, 30 | { 31 | "label": "Constrain images to the same size", 32 | "id": "thumbnail_images_constrained", 33 | "type": "checkbox", 34 | "default": true, 35 | "info": "Use this if you have uploaded different sized images across your products and want them to look consistent" 36 | }, 37 | { 38 | "label": "Crop images to fit thumbnail container", 39 | "id": "thumbnail_images_cropped", 40 | "type": "checkbox", 41 | "default": false, 42 | "info": "If left unchecked images will be scaled to fit inside their thumbnail container" 43 | }, 44 | { 45 | "label": "Thumbnail ratio", 46 | "id": "thumbnail_images_ratio", 47 | "type": "text", 48 | "default": "1:1", 49 | "info": "Options are either 'square', 'landscape', 'portrait', or a ratio written in the format 'w:h' (i.e. 1:1 would be sqaure, 4:6 would be portrait)" 50 | }, 51 | { 52 | "label": "Thumbnail alignment", 53 | "id": "thumbnail_images_alignment", 54 | "type": "select", 55 | "options": [ 56 | { "value": "top", "label": "Top" }, 57 | { "value": "middle", "label": "Middle" }, 58 | { "value": "bottom", "label": "Bottom" } 59 | ], 60 | "default": "middle" 61 | } 62 | ] 63 | } -------------------------------------------------------------------------------- /src/js/_polyfills.js: -------------------------------------------------------------------------------- 1 | /* 2 | A collection of polyfills for IE8 3 | */ 4 | 5 | module.exports.getComputedStyle = function () { 6 | if (!window.getComputedStyle) { 7 | window.getComputedStyle = function(el, pseudo) { 8 | this.el = el; 9 | this.getPropertyValue = function(prop) { 10 | var re = /(\-([a-z]){1})/g; 11 | if (prop == 'float') prop = 'styleFloat'; 12 | if (re.test(prop)) { 13 | prop = prop.replace(re, function () { 14 | return arguments[2].toUpperCase(); 15 | }); 16 | } 17 | return el.currentStyle[prop] ? el.currentStyle[prop] : null; 18 | }; 19 | return this; 20 | }; 21 | } 22 | }; 23 | 24 | module.exports.objectKeys = function () { 25 | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys 26 | if (!Object.keys) { 27 | Object.keys = (function() { 28 | 'use strict'; 29 | var hasOwnProperty = Object.prototype.hasOwnProperty, 30 | hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), 31 | dontEnums = [ 32 | 'toString', 33 | 'toLocaleString', 34 | 'valueOf', 35 | 'hasOwnProperty', 36 | 'isPrototypeOf', 37 | 'propertyIsEnumerable', 38 | 'constructor' 39 | ], 40 | dontEnumsLength = dontEnums.length; 41 | 42 | return function(obj) { 43 | if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { 44 | throw new TypeError('Object.keys called on non-object'); 45 | } 46 | 47 | var result = [], prop, i; 48 | 49 | for (prop in obj) { 50 | if (hasOwnProperty.call(obj, prop)) { 51 | result.push(prop); 52 | } 53 | } 54 | 55 | if (hasDontEnumBug) { 56 | for (i = 0; i < dontEnumsLength; i++) { 57 | if (hasOwnProperty.call(obj, dontEnums[i])) { 58 | result.push(dontEnums[i]); 59 | } 60 | } 61 | } 62 | return result; 63 | }; 64 | }()); 65 | } 66 | }; -------------------------------------------------------------------------------- /src/js/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | Main app for Bootstrapify 3 | */ 4 | 5 | var Bsify = require('./bsify.js'); 6 | var CartJS = require('shopify-cartjs'); 7 | var Lookbook = require('./bsify.lookbook_gallery.js'); 8 | 9 | var App = function () { 10 | // init CartJS 11 | CartJS.init(Bsify.cart, Bsify.money_formats); 12 | 13 | // init product template js 14 | if (Bsify.product) { 15 | // Preload images and call image switcher init 16 | Bsify.ImageSwitcher.preload_product_thumbs(); 17 | Bsify.ImageSwitcher.image_swticher(); 18 | // Initialise the image_variant_switcher 19 | Bsify.VariantOptionSwitcher.image_variant_switcher(); 20 | 21 | // Call linkOptionSelectors 22 | // Check that we want to use the linked product options 23 | if (Bsify.linked_product_options) { 24 | // Make sure the product meets the correct citeria for linked product options and then initialise 25 | if (Bsify.product.available && Bsify.product.options.length > 1) { 26 | Bsify.LinkedProductOptions.linkOptionSelectors(Bsify.product); 27 | } 28 | } 29 | } 30 | 31 | // init ajax add to cart 32 | if (Bsify.ajax_add_to_cart) { 33 | Bsify.Cart.init(); 34 | } 35 | 36 | // load uniform thumbs and orderly 37 | Bsify.Thumbnails.init(); 38 | 39 | // init event listeners for banner 40 | Bsify.Banner.init(); 41 | Bsify.BannerVideo.init(); 42 | 43 | // init event listeners for megamenu 44 | Bsify.MegaMenu.init(); 45 | 46 | // init event listeners for scroll_to 47 | Bsify.ScrollTo.init(); 48 | 49 | // init event listeners for modals 50 | Bsify.Modal.init(); 51 | 52 | // init lookbook gallery 53 | if ($('.collection-lookbook').length > 0) { 54 | var lookbook = new Lookbook({ 55 | gallery_wrapper: '.collection-lookbook .products.group', 56 | item_wrapper: '.group-item-wrap', 57 | controls_wrapper: '.lookbook-controls' 58 | }); 59 | 60 | lookbook.on_loaded = function () { 61 | Bsify.Thumbnails.orderly(); 62 | }; 63 | } 64 | 65 | // test for placeholder 66 | if (Modernizr.input.placeholder) { 67 | $('html').addClass('placeholder'); 68 | } else { 69 | $('html').addClass('no-placeholder'); 70 | } 71 | 72 | // init social feeds 73 | Bsify.SocialFeeds.init(); 74 | 75 | new Bsify.PasswordRecover(); 76 | }; 77 | 78 | // initiate the app and expose it to the browser 79 | var app = new App(); 80 | module.exports = app; 81 | window.CartJS = CartJS; -------------------------------------------------------------------------------- /src/js/bsify.banner.js: -------------------------------------------------------------------------------- 1 | Banner = {}; 2 | 3 | var $banner = $('[data-banner]'); 4 | 5 | Banner.init = function () { 6 | if ($banner.length > 0) { 7 | Banner.add_event_handlers(); 8 | } 9 | }; 10 | 11 | Banner.add_event_handlers = function () { 12 | // This event fires immediately when the slide instance method is invoked. 13 | $banner.on('slide.bs.carousel', function (e) { 14 | var $related_target = $(e.relatedTarget); 15 | // Set the data-banner attr to the current slides index so that we can do awesome scss stuffs 16 | $banner.attr('data-banner', $related_target.data('slide')); 17 | }); 18 | 19 | // This event is fired when the carousel has completed its slide transition. 20 | $banner.on('slid.bs.carousel', function () { 21 | }); 22 | }; 23 | 24 | module.exports = Banner; -------------------------------------------------------------------------------- /src/js/bsify.banner_video.js: -------------------------------------------------------------------------------- 1 | BannerVideo = {}; 2 | 3 | require('jquery.mb.ytplayer'); 4 | var $player = $('.banner-video-player'); 5 | 6 | BannerVideo.init = function () { 7 | if ($player.length > 0 && !Modernizr.touch) { 8 | $player.YTPlayer(); 9 | } else { 10 | $player.parent().addClass('fallback-img'); 11 | } 12 | }; 13 | 14 | module.exports = BannerVideo; -------------------------------------------------------------------------------- /src/js/bsify.image_switcher.js: -------------------------------------------------------------------------------- 1 | var ImageSwitcher = {}; 2 | 3 | // NOTE: how will this all work with srcset ??!! 4 | // TODO: swipe between images on mobile 5 | 6 | // Preload images for gallery 7 | // Used the sized src 8 | ImageSwitcher.preload_product_thumbs = function(){ 9 | var $thumbs = $('[data-gallery-main]'); 10 | if ($thumbs.length > 0) { 11 | var main_image_element = $($thumbs.data('gallery-main'))[0]; 12 | var main_image_size = Shopify.Image.imageSize(main_image_element.src); 13 | 14 | if($thumbs.length > 0){ 15 | $thumbs.each(function(){ 16 | var image = new Image(); 17 | var src = $(this).attr('href'); 18 | var sized_src = Shopify.Image.getSizedImageUrl(src, main_image_size); 19 | image.src = sized_src; 20 | }); 21 | } 22 | } 23 | }; 24 | 25 | // Initialise the image switcher for gallery thumbs 26 | ImageSwitcher.image_swticher = function () { 27 | $(document).on('click', '[data-gallery-main]', function (e) { 28 | e.preventDefault(); 29 | var $current_target = $(e.currentTarget); 30 | var main_image_element = $($current_target.data('gallery-main'))[0]; 31 | var image_src = $current_target.attr('href'); 32 | ImageSwitcher.switch_image(main_image_element, image_src); 33 | }); 34 | }; 35 | 36 | // Switch image 37 | // - takes the main image element that will have it's src changed 38 | // - and the new src to change it to 39 | ImageSwitcher.switch_image = function (main_image_element, image_src) { 40 | if (!main_image_element || !image_src) { 41 | return; 42 | } 43 | // get the new image src at the correct size 44 | var main_image_size = Shopify.Image.imageSize(main_image_element.src); 45 | var new_image_src = Shopify.Image.getSizedImageUrl(image_src, main_image_size); 46 | // switch the main image to the new src 47 | if (main_image_element.src !== new_image_src) { 48 | main_image_element.src = new_image_src; 49 | } 50 | }; 51 | 52 | module.exports = ImageSwitcher; -------------------------------------------------------------------------------- /src/js/bsify.js: -------------------------------------------------------------------------------- 1 | // Collect the Bsify object from the window with our translations and liquid variables all set up 2 | var Bsify = window.Bsify || {}; 3 | 4 | /* 5 | Bootstrapify utility methods 6 | */ 7 | 8 | Bsify.get_variant_by_id = function (variant_id) { 9 | var selected_variant; 10 | if (typeof variant_id === 'string') variant_id = parseInt(variant_id); 11 | 12 | for (var i = 0; i < Bsify.product.variants.length; i++) { 13 | var variant = Bsify.product.variants[i]; 14 | if (variant.id === variant_id) { 15 | selected_variant = variant; 16 | } 17 | } 18 | return selected_variant; 19 | }; 20 | 21 | // add bsify classes to the Bsify object 22 | Bsify.Cart = require('./bsify.cart.js'); 23 | Bsify.ImageSwitcher = require('./bsify.image_switcher.js'); 24 | Bsify.VariantOptionSwitcher = require('./bsify.variant_option_switcher.js'); 25 | Bsify.LinkedProductOptions = require('./bsify.linked_product_options.js'); 26 | Bsify.Banner = require('./bsify.banner.js'); 27 | Bsify.BannerVideo = require('./bsify.banner_video.js'); 28 | Bsify.Thumbnails = require('./bsify.thumbnails.js'); 29 | Bsify.SocialFeeds = require('./bsify.social_feeds.js'); 30 | Bsify.PasswordRecover = require('./bsify.password_recover.js'); 31 | Bsify.MegaMenu = require('./bsify.mega_menu.js'); 32 | Bsify.ScrollTo = require('./bsify.scroll_to.js'); 33 | Bsify.Modal = require('./bsify.modal.js'); 34 | 35 | module.exports = Bsify; -------------------------------------------------------------------------------- /src/js/bsify.media_queries.js: -------------------------------------------------------------------------------- 1 | require('./_polyfills.js').getComputedStyle(); 2 | 3 | module.exports.size = function () { 4 | return window.getComputedStyle(document.body,':after').getPropertyValue('content').replace(/['"]/g, ""); 5 | }; -------------------------------------------------------------------------------- /src/js/bsify.mega_menu.js: -------------------------------------------------------------------------------- 1 | var MegaMenu = {}; 2 | var menu_selector = '[data-menu="mega"]'; 3 | var $menu = $(menu_selector); 4 | 5 | MegaMenu.init = function () { 6 | if ($menu.length > 0) { 7 | MegaMenu.add_event_handlers(); 8 | } 9 | }; 10 | 11 | MegaMenu.add_event_handlers = function () { 12 | // Hide all open menus 13 | $(document).on('show.bs.collapse', menu_selector+' .collapse', function () { 14 | $(menu_selector+' .collapse.in').each(function (i, item) { 15 | $(item).collapse('hide'); 16 | }); 17 | $menu.addClass('open'); 18 | }); 19 | 20 | $(document).on('hidden.bs.collapse', menu_selector+' .collapse', function () { 21 | // Timeout hack to make sure the transitions have finished. 22 | setTimeout(function () { 23 | if ($(menu_selector+' .collapse.in').length === 0) { 24 | $menu.removeClass('open'); 25 | } 26 | }, 350); 27 | }); 28 | }; 29 | 30 | module.exports = MegaMenu; -------------------------------------------------------------------------------- /src/js/bsify.modal.js: -------------------------------------------------------------------------------- 1 | /* 2 | TODO: 3 | - handle multiple modals 4 | */ 5 | 6 | var Modal = {}; 7 | var modal_selector = '[data-bsify-modal]'; 8 | var modal_trigger_selector = '[data-modal-trigger]'; 9 | var $modal = $(modal_selector); 10 | var $modal_trigger = $(modal_trigger_selector); 11 | 12 | Modal.init = function () { 13 | if ($modal.length > 0) { 14 | $modal_trigger.each(function (i, trigger) { 15 | Modal._set_trigger_listener(trigger); 16 | }); 17 | } 18 | 19 | Modal._set_event_listeners(); 20 | }; 21 | 22 | // Private 23 | 24 | Modal._set_event_listeners = function () { 25 | // clean up content 26 | $modal.on('hidden.bs.modal', function (e) { 27 | $modal.find('.modal-content').html(''); 28 | }); 29 | }; 30 | 31 | Modal._set_trigger_listener = function (trigger) { 32 | var $trigger = $(trigger); 33 | $trigger.on('click', function (e) { 34 | e.preventDefault(); 35 | var $current_target = $(e.currentTarget); 36 | Modal._load_content($current_target); 37 | }); 38 | }; 39 | 40 | Modal._load_content = function ($current_target) { 41 | var content_url = $current_target.attr('href'); 42 | if (Modal._is_external_content(content_url)) { 43 | $.ajax(content_url).done(function (content) { 44 | $modal.find('.modal-content').html(content); 45 | }); 46 | } else { 47 | var content = $(content_url).html(); 48 | $modal.find('.modal-content').html(content); 49 | } 50 | $modal.modal(); 51 | }; 52 | 53 | Modal._is_external_content = function (content_url) { 54 | return content_url.charAt(0) !== '#'; 55 | }; 56 | 57 | module.exports = Modal; -------------------------------------------------------------------------------- /src/js/bsify.password_recover.js: -------------------------------------------------------------------------------- 1 | var PasswordRecover = function () { 2 | this.recover_string = '#recover'; 3 | this.$links = $('main [href*="/account/login"]'); 4 | this.$sections = $('[data-bsify-toggle-recover]'); 5 | this.force = ($('[data-bsify-toggle-recover-force]').length > 0); 6 | 7 | if (this.$sections.length > 0) { 8 | this._add_event_listeners(); 9 | this.toggle_forms(); 10 | } 11 | }; 12 | 13 | PasswordRecover.prototype.toggle_forms = function () { 14 | if (window.location.hash === this.recover_string || this.force) { 15 | this.show_recover_form(); 16 | } else { 17 | this.show_login_form(); 18 | } 19 | }; 20 | 21 | PasswordRecover.prototype.show_recover_form = function () { 22 | var _this = this; 23 | this._each_section(function ($section) { 24 | _this._toggle_section(($section.data('bsify-toggle-recover') === 'recover'), $section); 25 | }); 26 | }; 27 | 28 | PasswordRecover.prototype.show_login_form = function () { 29 | var _this = this; 30 | this._each_section(function ($section) { 31 | _this._toggle_section(($section.data('bsify-toggle-recover') !== 'recover'), $section); 32 | }); 33 | }; 34 | 35 | // Private 36 | 37 | PasswordRecover.prototype._add_event_listeners = function () { 38 | var _this = this; 39 | _this.$links.on('click', function (e) { 40 | e.preventDefault(); 41 | _this._on_click($(e.currentTarget)); 42 | }); 43 | }; 44 | 45 | PasswordRecover.prototype._on_click = function ($current_target) { 46 | if ($current_target.attr('href').indexOf(this.recover_string) > -1) { 47 | this.show_recover_form(); 48 | } else { 49 | this.show_login_form(); 50 | } 51 | }; 52 | 53 | PasswordRecover.prototype._each_section = function (callback) { 54 | this.$sections.each(function (i, section) { 55 | var $section = $(section); 56 | callback($section); 57 | }); 58 | }; 59 | 60 | PasswordRecover.prototype._toggle_section = function (condition, $section) { 61 | if (condition) { 62 | $section.show(); 63 | } else { 64 | $section.hide(); 65 | } 66 | }; 67 | 68 | module.exports = PasswordRecover; -------------------------------------------------------------------------------- /src/js/bsify.scroll_to.js: -------------------------------------------------------------------------------- 1 | var ScrollTo = {}; 2 | var scroll_selector = '[data-scroll-to]'; 3 | 4 | ScrollTo.init = function () { 5 | $(document).on('click', scroll_selector, function (e) { 6 | e.preventDefault(); 7 | var target = $(this).attr('href'); 8 | ScrollTo.go(target); 9 | }); 10 | }; 11 | 12 | ScrollTo.go = function (target) { 13 | $('body,html').animate({ 14 | scrollTop: $(target).offset().top 15 | }, 800); 16 | }; 17 | 18 | module.exports = ScrollTo; -------------------------------------------------------------------------------- /src/js/bsify.social_feeds.js: -------------------------------------------------------------------------------- 1 | var Instagram = require('./bsify.social_feed_instagram.js'); 2 | 3 | var SocialFeeds = {}; 4 | 5 | SocialFeeds.init = function () { 6 | SocialFeeds.instagram(); 7 | }; 8 | 9 | SocialFeeds.instagram = function () { 10 | var eles = SocialFeeds._get_elements('instagram'); 11 | if (eles.length > 0) { 12 | eles.each(function () { 13 | new Instagram(this); 14 | }); 15 | } 16 | }; 17 | 18 | SocialFeeds._get_elements = function (service) { 19 | var ele_selector = '[data-social-feed="'+service+'"]'; 20 | return $(ele_selector); 21 | }; 22 | 23 | module.exports = SocialFeeds; -------------------------------------------------------------------------------- /src/js/bsify.thumbnails.js: -------------------------------------------------------------------------------- 1 | require('uniformThumbnails'); 2 | require('jquery-orderly'); 3 | 4 | var Thumbnails = {}; 5 | 6 | Thumbnails.init = function () { 7 | var opts = {}; 8 | if (Bsify.thumbnail_settings.constrain_images) { 9 | 10 | opts.fit = (Bsify.thumbnail_settings.crop_images)? 'crop' : 'scale'; 11 | opts.align = Bsify.thumbnail_settings.image_alignment.toLowerCase(); 12 | var ratio = Bsify.thumbnail_settings.image_ratio.toLowerCase(); 13 | if (ratio === 'square' || ratio === 'landscape' || ratio === 'portrait') { 14 | opts.format = ratio; 15 | } else { 16 | opts.ratio = ratio; 17 | } 18 | 19 | var $img_wrappers = $(Bsify.thumbnail_selectors.image_wrapper); 20 | 21 | if ($img_wrappers.length > 0) { 22 | $img_wrappers.uniform_thumbnails(opts).on('ut_complete', function(){ 23 | Thumbnails.orderly(); 24 | }); 25 | } else { 26 | // just use orderly 27 | Thumbnails.orderly(); 28 | } 29 | } else { 30 | // just use orderly 31 | Thumbnails.orderly(); 32 | } 33 | 34 | // Always unifrom thumbs on product thumbnails 35 | $('.product-image-thumbs li a').uniform_thumbnails(opts); 36 | }; 37 | 38 | Thumbnails.orderly = function () { 39 | $(Bsify.thumbnail_selectors.details_wrapper).orderly({ method: 'children' }); 40 | }; 41 | 42 | module.exports = Thumbnails; -------------------------------------------------------------------------------- /src/js/bsify.touch.js: -------------------------------------------------------------------------------- 1 | var Swipe = function () { 2 | this.threshold = 5; 3 | this.xDown = null; 4 | this.yDown = null; 5 | this.add_event_listeners(); 6 | }; 7 | 8 | Swipe.prototype.add_event_listeners = function () { 9 | var _this = this; 10 | if (Element.prototype.addEventListener) { 11 | document.addEventListener('touchstart', function (evt) { 12 | _this.handleTouchStart(evt); 13 | }, false); 14 | document.addEventListener('touchmove', function (evt) { 15 | _this.handleTouchMove(evt); 16 | }, false); 17 | } 18 | }; 19 | 20 | Swipe.prototype.handleTouchStart = function (evt) { 21 | this.xDown = evt.touches[0].clientX; 22 | this.yDown = evt.touches[0].clientY; 23 | }; 24 | 25 | Swipe.prototype.handleTouchMove = function (evt) { 26 | if ( ! this.xDown || ! this.yDown ) return; 27 | 28 | var current_target = evt.targetTouches[0].target; 29 | 30 | var xUp = evt.touches[0].clientX; 31 | var yUp = evt.touches[0].clientY; 32 | 33 | var xDiff = this.xDown - xUp; 34 | var yDiff = this.yDown - yUp; 35 | 36 | if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) { 37 | if ( xDiff > 0 ) { 38 | if (xDiff > this.threshold) { 39 | $(current_target).trigger('swipeLeft'); 40 | } 41 | } else { 42 | if (xDiff * -1 > this.threshold) { 43 | $(current_target).trigger('swipeRight'); 44 | } 45 | } 46 | } else { 47 | if ( yDiff > 0 ) { 48 | if (yDiff > this.threshold) { 49 | $(current_target).trigger('swipeUp'); 50 | } 51 | } else { 52 | if (yDiff * -1 > this.threshold) { 53 | $(current_target).trigger('swipeDown'); 54 | } 55 | } 56 | } 57 | /* reset values */ 58 | this.xDown = null; 59 | this.yDown = null; 60 | }; 61 | 62 | new Swipe(); -------------------------------------------------------------------------------- /src/js/bsify.variant_option_switcher.js: -------------------------------------------------------------------------------- 1 | var VariantOptionSwitcher = {}; 2 | 3 | // Switch the variant if the gallery thumb is linked to one 4 | VariantOptionSwitcher.image_variant_switcher = function () { 5 | 6 | var $product_selector = $(Bsify.selectors.product.select_selector); 7 | 8 | $(document).on('click', '[data-variant-id]', function (e) { 9 | e.preventDefault(); 10 | var $current_target = $(e.currentTarget); 11 | // update the product selector with the new variant id 12 | $product_selector.val($current_target.data('variant-id')); 13 | // update the option selectors 14 | VariantOptionSwitcher.update_product_selector_options($product_selector); 15 | }); 16 | }; 17 | 18 | // Make the original variant selector update the generated option selectors 19 | // we are not doing this on a trigger event because if we did we'd end up in recursive hell 20 | VariantOptionSwitcher.update_product_selector_options = function ($product_selector) { 21 | 22 | // get the selected variant object 23 | var variant_id = $product_selector.val(); 24 | var variant = Bsify.get_variant_by_id(variant_id); 25 | 26 | if (variant) { 27 | // update each option selector 28 | for (var i = 0; i < Bsify.product.options.length; i++) { 29 | var option = 'option'+(i+1); 30 | var value = variant[option]; 31 | $('.single-option-selector:eq('+i+')').val(value).trigger('change'); 32 | } 33 | } 34 | }; 35 | 36 | module.exports = VariantOptionSwitcher; -------------------------------------------------------------------------------- /src/scss/base/_foundation.scss.liquid: -------------------------------------------------------------------------------- 1 | img { 2 | @include img-responsive; 3 | } 4 | 5 | a { 6 | transition: .3s; 7 | } 8 | 9 | /* 10 | Pass media queries to JS: https://adactio.com/journal/5429 11 | We do this so we are using one consitant source for this value. 12 | But also slightly tweaked because Chrome stopped letting us collect the value if the content was inside display:none; 13 | */ 14 | body:after { 15 | content: 'xs'; 16 | display: block; 17 | visibility: hidden; 18 | width: 0; 19 | height: 0; 20 | margin-top: -1.5em; 21 | @media (min-width: $screen-sm-min) { 22 | content: 'sm'; 23 | } 24 | @media (min-width: $screen-md-min) { 25 | content: 'md'; 26 | } 27 | @media (min-width: $screen-lg-min) { 28 | content: 'lg'; 29 | } 30 | } -------------------------------------------------------------------------------- /src/scss/base/_typography.scss.liquid: -------------------------------------------------------------------------------- 1 | /* Typograhical Styles */ 2 | 3 | a:hover, 4 | a:active, 5 | a:focus { 6 | text-decoration: none; 7 | li &, p & { 8 | text-decoration: underline; 9 | } 10 | } 11 | 12 | .pagination>li>a:hover, 13 | .pagination>li>a:focus, 14 | .pagination>li>span:hover, 15 | .pagination>li>span:focus { 16 | text-decoration: none; 17 | } 18 | 19 | h1, 20 | h2, 21 | h3, 22 | h4, 23 | h5, 24 | h6, 25 | .h1, 26 | .h2, 27 | .h3, 28 | .h4, 29 | .h5, 30 | .h6 { 31 | a { 32 | color: inherit; 33 | } 34 | .page-header &:first-child { 35 | margin-top: 0; 36 | } 37 | } 38 | 39 | table { 40 | tfoot tr:first-child td { 41 | border-top-width: 2px; 42 | } 43 | } 44 | 45 | .advanced-section-collection, 46 | .collection-header { 47 | .page-header { 48 | overflow: hidden; 49 | } 50 | .page-header h1, 51 | h2 { 52 | float: left; 53 | } 54 | .view-all-link { 55 | float: right; 56 | margin-top: 1em; 57 | } 58 | } 59 | .collection-description { 60 | clear: both; 61 | } 62 | 63 | .group-item-title, 64 | .cart-item-title { 65 | margin-top: ($line-height-computed / 2); 66 | margin-bottom: ($line-height-computed / 2); 67 | font-size: $font-size-h5; 68 | font-weight: $strong-weight; 69 | 70 | .list-collections &, 71 | .collection-list &, 72 | .search & { 73 | margin-bottom: ($line-height-computed / 2); 74 | font-size: $font-size-h3; 75 | } 76 | } 77 | 78 | .cart-table { 79 | img { 80 | margin: auto; 81 | } 82 | 83 | td { 84 | vertical-align: middle; 85 | text-align: center; 86 | } 87 | 88 | .cart-item-details { 89 | color: $thumbnail-caption-color; 90 | } 91 | 92 | @media (min-width: $grid-float-breakpoint) { 93 | 94 | .cart-item-total-label, 95 | .cart-item-total { 96 | text-align: right; 97 | } 98 | 99 | .cart-item-product-label, 100 | .cart-item-quantity-label, 101 | .cart-item-price-label, 102 | .cart-item-price { 103 | text-align: center; 104 | } 105 | 106 | .cart-item-details { 107 | text-align: left; 108 | } 109 | } 110 | } 111 | 112 | .cart-details { 113 | text-align: right; 114 | margin: $grid-gutter-width auto; 115 | @media (min-width: $grid-float-breakpoint) { 116 | margin: auto; 117 | } 118 | } 119 | 120 | .article-list { 121 | > li > a { 122 | padding-left: 0; 123 | } 124 | a:hover, 125 | a:active, 126 | a:focus { 127 | text-decoration: none; 128 | } 129 | h5 { 130 | margin-top: 0; 131 | } 132 | .article-list-meta { 133 | .separator { 134 | margin: 0 $grid-gutter-width / 8; 135 | } 136 | } 137 | } 138 | 139 | .account-details { 140 | dt, 141 | dd { 142 | text-align: left; 143 | margin-bottom: 0.5em; 144 | } 145 | } -------------------------------------------------------------------------------- /src/scss/base/_variables.scss.liquid: -------------------------------------------------------------------------------- 1 | /* 2 | Fix rounding issue as sass var can only hold 5 decimal places (orig: 1.428571429) 3 | This would cause buttons in input groups to not be lined up correctly with inputs 4 | */ 5 | $line-height-base: 1.42858 !default; 6 | $line-height-large: 1.5 !default; 7 | $font-size-base: 14px !default; 8 | 9 | {% unless settings.brand_primary_color == blank %} 10 | $brand-primary: {{ settings.brand_primary_color }} !default; 11 | {% endunless %} 12 | 13 | /* SCSS Variables */ 14 | $strong-weight: bold !default; 15 | $grid-gutter-width: 30px !default; 16 | $thumbnail-padding: $grid-gutter-width / 2 !default; // TODO: if we have no borders this needs to be set to 0 17 | 18 | /* lookbook vars */ 19 | $lookbook-item-max-width-xs: 180px !default; 20 | $lookbook-item-max-width-sm: 320px !default; 21 | $lookbook-item-max-width-md: 480px !default; 22 | $lookbook-item-max-width-lg: 600px !default; 23 | 24 | $lookbook-min-height-md: 800px !default; 25 | $lookbook-min-height-lg: 1000px !default; 26 | 27 | /* Banner */ 28 | $banner-height-xs: 30em !default; 29 | $banner-height-xs-landscape: 22em !default; 30 | 31 | $advanced-section-alt-background: #f8f8f8 !default; 32 | $mega-menu-background: $advanced-section-alt-background !default; 33 | 34 | {% unless settings.scss_variables == blank %}{{ settings.scss_variables }}{% endunless %} -------------------------------------------------------------------------------- /src/scss/giftcard.scss: -------------------------------------------------------------------------------- 1 | /* Only include the styles needed for the giftcard */ 2 | @import 'base/_variables.scss.liquid'; 3 | @import 'base/_mixins.scss.liquid'; 4 | @import '../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap'; 5 | @import 'base/_foundation.scss.liquid'; 6 | @import 'base/_typography.scss.liquid'; 7 | @import 'module/_gift_card.scss.liquid'; 8 | @import 'theme/_custom_overrides.scss.liquid'; 9 | @import 'theme/_overrides.scss.liquid'; -------------------------------------------------------------------------------- /src/scss/layout/_headers.scss.liquid: -------------------------------------------------------------------------------- 1 | /* Site header and navigation snippet styles and other header layouts - This file is for imports only. DO NOT write scss in this file, it will not be included. */ 2 | @import 'site_headers/_centered.scss.liquid'; 3 | @import 'site_headers/_navbar.scss.liquid'; 4 | @import 'site_headers/_stacked.scss.liquid'; 5 | @import 'site_headers/_logo_left.scss.liquid'; 6 | @import 'collection_headers/_banner.scss.liquid'; 7 | @import 'collection_headers/_split.scss.liquid'; -------------------------------------------------------------------------------- /src/scss/layout/_navigation.scss.liquid: -------------------------------------------------------------------------------- 1 | /* General styles for all navigations */ 2 | 3 | .navbar-brand-logo { 4 | padding-top: 0; 5 | padding-bottom: 0; 6 | } 7 | .navbar-brand img { 8 | max-height: 100%; 9 | } 10 | 11 | .navbar-header { 12 | .cart-link { 13 | margin-right: 0; 14 | } 15 | } 16 | 17 | .nav-inline { 18 | li, li > a { 19 | display: inline-block; 20 | } 21 | } 22 | 23 | .collection-controls > div { 24 | margin-bottom: ($grid-gutter-width / 2); 25 | } 26 | 27 | .collection-controls > div, 28 | .collection-filter-by > div { 29 | float: left; 30 | margin-right: 2px; 31 | @media (min-width: $grid-float-breakpoint) { 32 | float: right; 33 | &:first-of-type { 34 | margin-right: 0; 35 | } 36 | } 37 | } 38 | .collection-filter-by.collection-filter-style-button { 39 | li strong { 40 | display: block; 41 | padding: $nav-link-padding; 42 | padding-left: 0; 43 | } 44 | & > div { 45 | margin-bottom: 0.25em; 46 | @media (min-width: $grid-float-breakpoint) { 47 | float: left; 48 | margin-right: $grid-gutter-width; 49 | } 50 | } 51 | $collection-controls-right-buffer: 350px + $grid-gutter-width; 52 | @media (min-width: $grid-float-breakpoint) { 53 | float: left; 54 | max-width: $container-sm - $collection-controls-right-buffer; 55 | } 56 | @media (min-width: $screen-md-min) { 57 | max-width: $container-md - $collection-controls-right-buffer; 58 | } 59 | @media (min-width: $screen-lg-min) { 60 | max-width: $container-lg - $collection-controls-right-buffer; 61 | } 62 | } 63 | 64 | .collection-sort-by, 65 | .collection-filter-by.collection-filter-style-dropdown { 66 | .fa { 67 | float: right; 68 | padding: 0.2em 0; 69 | } 70 | } 71 | 72 | .pagination-wrapper { 73 | @include make-xs-column(12); 74 | text-align: center; 75 | } 76 | 77 | .list-links { 78 | li + li { 79 | margin-top: 0.5em; 80 | } 81 | } -------------------------------------------------------------------------------- /src/scss/layout/_sticky_footer.scss.liquid: -------------------------------------------------------------------------------- 1 | /* 2 | Sticky footer 3 | Display table breaks full width content due to images without max-width:100%; so don't use it on mobiles 4 | I know, you're asking 'whut?! why is this even a thing?' but there is some javascript thing involved 5 | (*cough* uniformThumbs for constraining images *cough*) 6 | */ 7 | @media (min-width: $grid-float-breakpoint) { 8 | html, 9 | body { height: 100%; } 10 | 11 | body { 12 | display: table; 13 | width: 100%; 14 | } 15 | 16 | .site-content, .site-footer { 17 | display: table-row; 18 | height: 1px; 19 | } 20 | 21 | .site-content { height: 100%; } 22 | } -------------------------------------------------------------------------------- /src/scss/layout/collection_headers/_banner.scss.liquid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidnz/Bootstrapify-4/089e1ec8261020e90106d3285ec5564111aee589/src/scss/layout/collection_headers/_banner.scss.liquid -------------------------------------------------------------------------------- /src/scss/layout/collection_headers/_split.scss.liquid: -------------------------------------------------------------------------------- 1 | .collection-header-split .content-split { 2 | @include make-row(); 3 | } 4 | 5 | .collection-header-split-image-left { 6 | .content-split-text { 7 | @include make-sm-column-push(4); 8 | } 9 | .content-split-images { 10 | @include make-sm-column-pull(8); 11 | } 12 | } 13 | .list-collection-header-split-image-left { 14 | .content-split-text { 15 | @include make-sm-column-push(4); 16 | } 17 | .content-split-images { 18 | @include make-sm-column-pull(8); 19 | } 20 | } -------------------------------------------------------------------------------- /src/scss/layout/site_headers/_centered.scss.liquid: -------------------------------------------------------------------------------- 1 | /* 2 | Styles for centered header layout snippet 3 | */ 4 | 5 | #header-layout-centered { 6 | .header-logo { 7 | text-align: center; 8 | a { 9 | overflow: hidden; 10 | display: inline-block; 11 | vertical-align: middle; 12 | } 13 | 14 | {% unless settings.logo_max_height == blank %} 15 | img { 16 | max-height: {{ settings.logo_max_height }}; 17 | } 18 | {% endunless %} 19 | } 20 | 21 | .header-main-menu { 22 | .navbar { 23 | margin-top: $grid-gutter-width; 24 | margin-bottom: 0; 25 | } 26 | .navbar-nav { 27 | float: none; 28 | text-align: center; 29 | } 30 | .navbar-nav > li { 31 | float: none; 32 | display: inline-block; 33 | } 34 | .navbar-nav > li > a { 35 | display: inline-block; 36 | } 37 | } 38 | 39 | .header-top-menu { 40 | .navbar-left:first-child { 41 | margin-left: -($grid-gutter-width/2); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/scss/layout/site_headers/_logo_left.scss.liquid: -------------------------------------------------------------------------------- 1 | /* 2 | Styles for centered header layout snippet 3 | */ 4 | 5 | #header-layout-logo-left { 6 | .navbar-brand-logo { 7 | overflow: hidden; 8 | display: inline-block; 9 | vertical-align: middle; 10 | 11 | {% unless settings.logo_max_height == blank %} 12 | img { 13 | max-height: {{ settings.logo_max_height }}; 14 | } 15 | {% endunless %} 16 | } 17 | 18 | .header-main-menu { 19 | position: relative; 20 | z-index: 1; 21 | .navbar { 22 | margin-bottom: 0; 23 | padding: $grid-gutter-width/2 0; 24 | } 25 | .navbar-nav { 26 | float: right; 27 | margin-right: -($grid-gutter-width/2); 28 | {% unless settings.logo_max_height == blank %} 29 | margin-top: ({{ settings.logo_max_height | remove: 'px' }} / 2)*1px; 30 | -webkit-transform: translateY(-50%); 31 | -moz-transform: translateY(-50%); 32 | transform: translateY(-50%); 33 | {% endunless %} 34 | } 35 | } 36 | 37 | .header-top-menu { 38 | .navbar { 39 | margin-bottom: 0; 40 | } 41 | .navbar-left:first-child { 42 | margin-left: -($grid-gutter-width/2); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/scss/layout/site_headers/_navbar.scss.liquid: -------------------------------------------------------------------------------- 1 | /* 2 | Styles for navbar header layout snippet 3 | */ 4 | 5 | #header-layout-navbar { 6 | .navbar { 7 | margin-bottom: 0; 8 | } 9 | 10 | .cart-link { 11 | @media (min-width: $grid-float-breakpoint) { 12 | display: none; 13 | } 14 | } 15 | 16 | .header-top-menu { 17 | .navigation-cart { 18 | @media (max-width: $grid-float-breakpoint) { 19 | display: none; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/scss/layout/site_headers/_stacked.scss.liquid: -------------------------------------------------------------------------------- 1 | #header-layout-stacked .header-layout-lg { 2 | & > .navbar { 3 | border: none; 4 | margin-bottom: 0; 5 | } 6 | 7 | .navbar .navbar { 8 | margin-bottom: 0; 9 | } 10 | 11 | .navigation .navigation-cart { 12 | display: none; 13 | @media (min-width: $grid-float-breakpoint) { 14 | display: block; 15 | } 16 | } 17 | 18 | .header-top-menu { 19 | & > .navbar { 20 | z-index: $zindex-navbar + 1; 21 | } 22 | @media (min-width: $grid-float-breakpoint) { 23 | font-size: .9em; 24 | .navbar { 25 | border: none; 26 | } 27 | } 28 | } 29 | 30 | .header-main-menu { 31 | .navbar-nav { 32 | float: none; 33 | text-align: center; 34 | } 35 | .navbar-nav > li { 36 | float: none; 37 | display: inline-block; 38 | } 39 | .navbar-nav > li > a { 40 | display: inline-block; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/scss/module/_forms.scss.liquid: -------------------------------------------------------------------------------- 1 | // TODO: refactor this to remove nested-ness and extends 2 | 3 | .add-to-cart-form, 4 | .cart-form, 5 | .contact-form, 6 | .newsletter-form, 7 | .customer-register-form, 8 | .customer-login-form, 9 | .customer-recover-password-form, 10 | .customer-activate-form, 11 | .customer-reset-form, 12 | .customer-address-form-new, 13 | .customer-address-form-edit { 14 | 15 | .selector-wrapper { 16 | // added in by shopify's js so we have no control of this 17 | // here we have to use extend 18 | @extend .form-group; 19 | select { 20 | @extend .form-control; 21 | } 22 | } 23 | 24 | // notes and errors are either created by Shopify or JS 25 | .errors, .note { 26 | @extend .alert; 27 | } 28 | 29 | .errors, .note.error { 30 | @extend .alert-danger; 31 | } 32 | 33 | .note.success { 34 | @extend .alert-success; 35 | } 36 | } 37 | 38 | .add-to-cart-message, .cart-message { 39 | .note { 40 | @extend .alert; 41 | } 42 | .note.success { 43 | @extend .alert-success; 44 | } 45 | } 46 | 47 | .search-lg { 48 | padding: 1.5em 1em; 49 | text-align: center; 50 | } 51 | 52 | [type="submit"] + .cancel-link { 53 | margin-left: 0.25em; 54 | } 55 | 56 | input.btn-link { 57 | padding: 0; 58 | } 59 | 60 | .placeholder { 61 | [class*="customers"] .site-content { 62 | label { 63 | display: none; 64 | } 65 | } 66 | .customers-addresses .site-content { 67 | label { 68 | display: block; 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/scss/module/_images.scss.liquid: -------------------------------------------------------------------------------- 1 | .fallback-image { 2 | width: 100%; 3 | padding-top: 150%; // What is this crazy black magic?! 4 | background-color: rgba($brand_primary, 0.2); 5 | } 6 | 7 | .footer-payment-icons img { 8 | width: 2.5em; 9 | display: inline-block; 10 | margin-right: 0.15em; 11 | margin-bottom: 0.15em; 12 | } -------------------------------------------------------------------------------- /src/scss/module/_mega_menu.scss.liquid: -------------------------------------------------------------------------------- 1 | .mega-menu { 2 | margin-top: -1px; 3 | border-bottom: $navbar-default-border solid 1px; 4 | background-color: $mega-menu-background; 5 | 6 | // Remove class in markup to push content down when open 7 | &.mega-menu-overlay { 8 | z-index: 100; 9 | position: absolute; 10 | width: 100%; 11 | } 12 | 13 | .collapse .sheet, 14 | .collapsing .sheet { 15 | padding-top: $grid-gutter-width; 16 | padding-bottom: $grid-gutter-width; 17 | } 18 | } 19 | 20 | .mega-menu-group { 21 | @include make-row(); 22 | 23 | .mega-menu-group-list { 24 | @include make-xs-column(12); 25 | @include make-sm-column(4); 26 | @include make-md-column(2); 27 | } 28 | 29 | // Don't show if we have no groups, this is probably not a thing anyway 30 | &[data-group-size="0"] .mega-menu-group-list { 31 | display: none; 32 | } 33 | 34 | @for $i from 1 through 3 { 35 | &[data-group-size="#{$i}"] .mega-menu-group-list { 36 | @include make-sm-column(12 / $i); 37 | } 38 | } 39 | 40 | &[data-group-size="4"] .mega-menu-group-list { 41 | @include make-sm-column(6); 42 | @include make-md-column(3); 43 | } 44 | } -------------------------------------------------------------------------------- /src/scss/module/_social.scss.liquid: -------------------------------------------------------------------------------- 1 | /* Styles for social links and feeds */ 2 | 3 | .social-links, .social-share-links { 4 | ul > li > a { 5 | padding-left: 0; 6 | padding-right: 0; 7 | } 8 | & .fa { 9 | font-size: 1.5em; 10 | } 11 | & .small .fa { 12 | font-size: 1.25em; 13 | } 14 | } 15 | 16 | [data-social-feed="instagram"] { 17 | & > p { 18 | color: $text-muted; 19 | margin-bottom: 3em; 20 | } 21 | .group-item-wrap { 22 | @include make-xs-column(6); 23 | @include make-sm-column(3); 24 | @include make-md-column(2); 25 | } 26 | 27 | .group-item .group-item-details { 28 | padding: 1em; 29 | font-size: 0.85em; 30 | p { 31 | overflow: hidden; 32 | height: 100%; 33 | margin: 0; 34 | } 35 | .user { 36 | display: block; 37 | font-weight: bold; 38 | } 39 | } 40 | 41 | @include hover-captions; 42 | } -------------------------------------------------------------------------------- /src/scss/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'base/_variables.scss.liquid'; 2 | @import 'theme/_custom_variables.scss.liquid'; 3 | 4 | @import 'base/_mixins.scss.liquid'; 5 | @import '../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap'; 6 | @import 'base/_foundation.scss.liquid'; 7 | @import 'base/_typography.scss.liquid'; 8 | 9 | @import 'layout/_sticky_footer.scss.liquid'; 10 | @import 'layout/_layout.scss.liquid'; 11 | @import 'layout/_headers.scss.liquid'; 12 | @import 'layout/_navigation.scss.liquid'; 13 | 14 | @import 'module/_mega_menu.scss.liquid'; 15 | @import 'module/_banner.scss.liquid'; 16 | @import 'module/_forms.scss.liquid'; 17 | @import 'module/_images.scss.liquid'; 18 | @import 'module/_social.scss.liquid'; 19 | 20 | @import 'theme/_custom_overrides.scss.liquid'; 21 | @import 'theme/_overrides.scss.liquid'; -------------------------------------------------------------------------------- /src/scss/theme/_custom_overrides.scss.liquid: -------------------------------------------------------------------------------- 1 | /* 2 | Put your project specific custom overrides here 3 | */ -------------------------------------------------------------------------------- /src/scss/theme/_custom_variables.scss.liquid: -------------------------------------------------------------------------------- 1 | /* 2 | Put your project specific custom variables here 3 | */ -------------------------------------------------------------------------------- /src/scss/theme/_overrides.scss.liquid: -------------------------------------------------------------------------------- 1 | // This file is purely for this one liquid settings so that it is placed right at the bottom of our stylesheet. 2 | {% unless settings.scss_overrides == blank %}{{ settings.scss_overrides }}{% endunless %} -------------------------------------------------------------------------------- /theme/assets/_polyfills.js: -------------------------------------------------------------------------------- 1 | /* 2 | A collection of polyfills for IE8 3 | */ 4 | 5 | module.exports.getComputedStyle = function () { 6 | if (!window.getComputedStyle) { 7 | window.getComputedStyle = function(el, pseudo) { 8 | this.el = el; 9 | this.getPropertyValue = function(prop) { 10 | var re = /(\-([a-z]){1})/g; 11 | if (prop == 'float') prop = 'styleFloat'; 12 | if (re.test(prop)) { 13 | prop = prop.replace(re, function () { 14 | return arguments[2].toUpperCase(); 15 | }); 16 | } 17 | return el.currentStyle[prop] ? el.currentStyle[prop] : null; 18 | }; 19 | return this; 20 | }; 21 | } 22 | }; 23 | 24 | module.exports.objectKeys = function () { 25 | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys 26 | if (!Object.keys) { 27 | Object.keys = (function() { 28 | 'use strict'; 29 | var hasOwnProperty = Object.prototype.hasOwnProperty, 30 | hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), 31 | dontEnums = [ 32 | 'toString', 33 | 'toLocaleString', 34 | 'valueOf', 35 | 'hasOwnProperty', 36 | 'isPrototypeOf', 37 | 'propertyIsEnumerable', 38 | 'constructor' 39 | ], 40 | dontEnumsLength = dontEnums.length; 41 | 42 | return function(obj) { 43 | if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { 44 | throw new TypeError('Object.keys called on non-object'); 45 | } 46 | 47 | var result = [], prop, i; 48 | 49 | for (prop in obj) { 50 | if (hasOwnProperty.call(obj, prop)) { 51 | result.push(prop); 52 | } 53 | } 54 | 55 | if (hasDontEnumBug) { 56 | for (i = 0; i < dontEnumsLength; i++) { 57 | if (hasOwnProperty.call(obj, dontEnums[i])) { 58 | result.push(dontEnums[i]); 59 | } 60 | } 61 | } 62 | return result; 63 | }; 64 | }()); 65 | } 66 | }; -------------------------------------------------------------------------------- /theme/assets/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | Main app for Bootstrapify 3 | */ 4 | 5 | var Bsify = require('./bsify.js'); 6 | var CartJS = require('shopify-cartjs'); 7 | var Lookbook = require('./bsify.lookbook_gallery.js'); 8 | 9 | var App = function () { 10 | // init CartJS 11 | CartJS.init(Bsify.cart, Bsify.money_formats); 12 | 13 | // init product template js 14 | if (Bsify.product) { 15 | // Preload images and call image switcher init 16 | Bsify.ImageSwitcher.preload_product_thumbs(); 17 | Bsify.ImageSwitcher.image_swticher(); 18 | // Initialise the image_variant_switcher 19 | Bsify.VariantOptionSwitcher.image_variant_switcher(); 20 | 21 | // Call linkOptionSelectors 22 | // Check that we want to use the linked product options 23 | if (Bsify.linked_product_options) { 24 | // Make sure the product meets the correct citeria for linked product options and then initialise 25 | if (Bsify.product.available && Bsify.product.options.length > 1) { 26 | Bsify.LinkedProductOptions.linkOptionSelectors(Bsify.product); 27 | } 28 | } 29 | } 30 | 31 | // init ajax add to cart 32 | if (Bsify.ajax_add_to_cart) { 33 | Bsify.Cart.init(); 34 | } 35 | 36 | // load uniform thumbs and orderly 37 | Bsify.Thumbnails.init(); 38 | 39 | // init event listeners for banner 40 | Bsify.Banner.init(); 41 | Bsify.BannerVideo.init(); 42 | 43 | // init event listeners for megamenu 44 | Bsify.MegaMenu.init(); 45 | 46 | // init event listeners for scroll_to 47 | Bsify.ScrollTo.init(); 48 | 49 | // init event listeners for modals 50 | Bsify.Modal.init(); 51 | 52 | // init lookbook gallery 53 | if ($('.collection-lookbook').length > 0) { 54 | var lookbook = new Lookbook({ 55 | gallery_wrapper: '.collection-lookbook .products.group', 56 | item_wrapper: '.group-item-wrap', 57 | controls_wrapper: '.lookbook-controls' 58 | }); 59 | 60 | lookbook.on_loaded = function () { 61 | Bsify.Thumbnails.orderly(); 62 | }; 63 | } 64 | 65 | // test for placeholder 66 | if (Modernizr.input.placeholder) { 67 | $('html').addClass('placeholder'); 68 | } else { 69 | $('html').addClass('no-placeholder'); 70 | } 71 | 72 | // init social feeds 73 | Bsify.SocialFeeds.init(); 74 | 75 | new Bsify.PasswordRecover(); 76 | }; 77 | 78 | // initiate the app and expose it to the browser 79 | var app = new App(); 80 | module.exports = app; 81 | window.CartJS = CartJS; -------------------------------------------------------------------------------- /theme/assets/bsify.banner.js: -------------------------------------------------------------------------------- 1 | Banner = {}; 2 | 3 | var $banner = $('[data-banner]'); 4 | 5 | Banner.init = function () { 6 | if ($banner.length > 0) { 7 | Banner.add_event_handlers(); 8 | } 9 | }; 10 | 11 | Banner.add_event_handlers = function () { 12 | // This event fires immediately when the slide instance method is invoked. 13 | $banner.on('slide.bs.carousel', function (e) { 14 | var $related_target = $(e.relatedTarget); 15 | // Set the data-banner attr to the current slides index so that we can do awesome scss stuffs 16 | $banner.attr('data-banner', $related_target.data('slide')); 17 | }); 18 | 19 | // This event is fired when the carousel has completed its slide transition. 20 | $banner.on('slid.bs.carousel', function () { 21 | }); 22 | }; 23 | 24 | module.exports = Banner; -------------------------------------------------------------------------------- /theme/assets/bsify.banner_video.js: -------------------------------------------------------------------------------- 1 | BannerVideo = {}; 2 | 3 | require('jquery.mb.ytplayer'); 4 | var $player = $('.banner-video-player'); 5 | 6 | BannerVideo.init = function () { 7 | if ($player.length > 0 && !Modernizr.touch) { 8 | $player.YTPlayer(); 9 | } else { 10 | $player.parent().addClass('fallback-img'); 11 | } 12 | }; 13 | 14 | module.exports = BannerVideo; -------------------------------------------------------------------------------- /theme/assets/bsify.image_switcher.js: -------------------------------------------------------------------------------- 1 | var ImageSwitcher = {}; 2 | 3 | // NOTE: how will this all work with srcset ??!! 4 | // TODO: swipe between images on mobile 5 | 6 | // Preload images for gallery 7 | // Used the sized src 8 | ImageSwitcher.preload_product_thumbs = function(){ 9 | var $thumbs = $('[data-gallery-main]'); 10 | if ($thumbs.length > 0) { 11 | var main_image_element = $($thumbs.data('gallery-main'))[0]; 12 | var main_image_size = Shopify.Image.imageSize(main_image_element.src); 13 | 14 | if($thumbs.length > 0){ 15 | $thumbs.each(function(){ 16 | var image = new Image(); 17 | var src = $(this).attr('href'); 18 | var sized_src = Shopify.Image.getSizedImageUrl(src, main_image_size); 19 | image.src = sized_src; 20 | }); 21 | } 22 | } 23 | }; 24 | 25 | // Initialise the image switcher for gallery thumbs 26 | ImageSwitcher.image_swticher = function () { 27 | $(document).on('click', '[data-gallery-main]', function (e) { 28 | e.preventDefault(); 29 | var $current_target = $(e.currentTarget); 30 | var main_image_element = $($current_target.data('gallery-main'))[0]; 31 | var image_src = $current_target.attr('href'); 32 | ImageSwitcher.switch_image(main_image_element, image_src); 33 | }); 34 | }; 35 | 36 | // Switch image 37 | // - takes the main image element that will have it's src changed 38 | // - and the new src to change it to 39 | ImageSwitcher.switch_image = function (main_image_element, image_src) { 40 | if (!main_image_element || !image_src) { 41 | return; 42 | } 43 | // get the new image src at the correct size 44 | var main_image_size = Shopify.Image.imageSize(main_image_element.src); 45 | var new_image_src = Shopify.Image.getSizedImageUrl(image_src, main_image_size); 46 | // switch the main image to the new src 47 | if (main_image_element.src !== new_image_src) { 48 | main_image_element.src = new_image_src; 49 | } 50 | }; 51 | 52 | module.exports = ImageSwitcher; -------------------------------------------------------------------------------- /theme/assets/bsify.js: -------------------------------------------------------------------------------- 1 | // Collect the Bsify object from the window with our translations and liquid variables all set up 2 | var Bsify = window.Bsify || {}; 3 | 4 | /* 5 | Bootstrapify utility methods 6 | */ 7 | 8 | Bsify.get_variant_by_id = function (variant_id) { 9 | var selected_variant; 10 | if (typeof variant_id === 'string') variant_id = parseInt(variant_id); 11 | 12 | for (var i = 0; i < Bsify.product.variants.length; i++) { 13 | var variant = Bsify.product.variants[i]; 14 | if (variant.id === variant_id) { 15 | selected_variant = variant; 16 | } 17 | } 18 | return selected_variant; 19 | }; 20 | 21 | // add bsify classes to the Bsify object 22 | Bsify.Cart = require('./bsify.cart.js'); 23 | Bsify.ImageSwitcher = require('./bsify.image_switcher.js'); 24 | Bsify.VariantOptionSwitcher = require('./bsify.variant_option_switcher.js'); 25 | Bsify.LinkedProductOptions = require('./bsify.linked_product_options.js'); 26 | Bsify.Banner = require('./bsify.banner.js'); 27 | Bsify.BannerVideo = require('./bsify.banner_video.js'); 28 | Bsify.Thumbnails = require('./bsify.thumbnails.js'); 29 | Bsify.SocialFeeds = require('./bsify.social_feeds.js'); 30 | Bsify.PasswordRecover = require('./bsify.password_recover.js'); 31 | Bsify.MegaMenu = require('./bsify.mega_menu.js'); 32 | Bsify.ScrollTo = require('./bsify.scroll_to.js'); 33 | Bsify.Modal = require('./bsify.modal.js'); 34 | 35 | module.exports = Bsify; -------------------------------------------------------------------------------- /theme/assets/bsify.media_queries.js: -------------------------------------------------------------------------------- 1 | require('./_polyfills.js').getComputedStyle(); 2 | 3 | module.exports.size = function () { 4 | return window.getComputedStyle(document.body,':after').getPropertyValue('content').replace(/['"]/g, ""); 5 | }; -------------------------------------------------------------------------------- /theme/assets/bsify.mega_menu.js: -------------------------------------------------------------------------------- 1 | var MegaMenu = {}; 2 | var menu_selector = '[data-menu="mega"]'; 3 | var $menu = $(menu_selector); 4 | 5 | MegaMenu.init = function () { 6 | if ($menu.length > 0) { 7 | MegaMenu.add_event_handlers(); 8 | } 9 | }; 10 | 11 | MegaMenu.add_event_handlers = function () { 12 | // Hide all open menus 13 | $(document).on('show.bs.collapse', menu_selector+' .collapse', function () { 14 | $(menu_selector+' .collapse.in').each(function (i, item) { 15 | $(item).collapse('hide'); 16 | }); 17 | $menu.addClass('open'); 18 | }); 19 | 20 | $(document).on('hidden.bs.collapse', menu_selector+' .collapse', function () { 21 | // Timeout hack to make sure the transitions have finished. 22 | setTimeout(function () { 23 | if ($(menu_selector+' .collapse.in').length === 0) { 24 | $menu.removeClass('open'); 25 | } 26 | }, 350); 27 | }); 28 | }; 29 | 30 | module.exports = MegaMenu; -------------------------------------------------------------------------------- /theme/assets/bsify.modal.js: -------------------------------------------------------------------------------- 1 | /* 2 | TODO: 3 | - handle multiple modals 4 | */ 5 | 6 | var Modal = {}; 7 | var modal_selector = '[data-bsify-modal]'; 8 | var modal_trigger_selector = '[data-modal-trigger]'; 9 | var $modal = $(modal_selector); 10 | var $modal_trigger = $(modal_trigger_selector); 11 | 12 | Modal.init = function () { 13 | if ($modal.length > 0) { 14 | $modal_trigger.each(function (i, trigger) { 15 | Modal._set_trigger_listener(trigger); 16 | }); 17 | } 18 | 19 | Modal._set_event_listeners(); 20 | }; 21 | 22 | // Private 23 | 24 | Modal._set_event_listeners = function () { 25 | // clean up content 26 | $modal.on('hidden.bs.modal', function (e) { 27 | $modal.find('.modal-content').html(''); 28 | }); 29 | }; 30 | 31 | Modal._set_trigger_listener = function (trigger) { 32 | var $trigger = $(trigger); 33 | $trigger.on('click', function (e) { 34 | e.preventDefault(); 35 | var $current_target = $(e.currentTarget); 36 | Modal._load_content($current_target); 37 | }); 38 | }; 39 | 40 | Modal._load_content = function ($current_target) { 41 | var content_url = $current_target.attr('href'); 42 | if (Modal._is_external_content(content_url)) { 43 | $.ajax(content_url).done(function (content) { 44 | $modal.find('.modal-content').html(content); 45 | }); 46 | } else { 47 | var content = $(content_url).html(); 48 | $modal.find('.modal-content').html(content); 49 | } 50 | $modal.modal(); 51 | }; 52 | 53 | Modal._is_external_content = function (content_url) { 54 | return content_url.charAt(0) !== '#'; 55 | }; 56 | 57 | module.exports = Modal; -------------------------------------------------------------------------------- /theme/assets/bsify.password_recover.js: -------------------------------------------------------------------------------- 1 | var PasswordRecover = function () { 2 | this.recover_string = '#recover'; 3 | this.$links = $('main [href*="/account/login"]'); 4 | this.$sections = $('[data-bsify-toggle-recover]'); 5 | this.force = ($('[data-bsify-toggle-recover-force]').length > 0); 6 | 7 | if (this.$sections.length > 0) { 8 | this._add_event_listeners(); 9 | this.toggle_forms(); 10 | } 11 | }; 12 | 13 | PasswordRecover.prototype.toggle_forms = function () { 14 | if (window.location.hash === this.recover_string || this.force) { 15 | this.show_recover_form(); 16 | } else { 17 | this.show_login_form(); 18 | } 19 | }; 20 | 21 | PasswordRecover.prototype.show_recover_form = function () { 22 | var _this = this; 23 | this._each_section(function ($section) { 24 | _this._toggle_section(($section.data('bsify-toggle-recover') === 'recover'), $section); 25 | }); 26 | }; 27 | 28 | PasswordRecover.prototype.show_login_form = function () { 29 | var _this = this; 30 | this._each_section(function ($section) { 31 | _this._toggle_section(($section.data('bsify-toggle-recover') !== 'recover'), $section); 32 | }); 33 | }; 34 | 35 | // Private 36 | 37 | PasswordRecover.prototype._add_event_listeners = function () { 38 | var _this = this; 39 | _this.$links.on('click', function (e) { 40 | e.preventDefault(); 41 | _this._on_click($(e.currentTarget)); 42 | }); 43 | }; 44 | 45 | PasswordRecover.prototype._on_click = function ($current_target) { 46 | if ($current_target.attr('href').indexOf(this.recover_string) > -1) { 47 | this.show_recover_form(); 48 | } else { 49 | this.show_login_form(); 50 | } 51 | }; 52 | 53 | PasswordRecover.prototype._each_section = function (callback) { 54 | this.$sections.each(function (i, section) { 55 | var $section = $(section); 56 | callback($section); 57 | }); 58 | }; 59 | 60 | PasswordRecover.prototype._toggle_section = function (condition, $section) { 61 | if (condition) { 62 | $section.show(); 63 | } else { 64 | $section.hide(); 65 | } 66 | }; 67 | 68 | module.exports = PasswordRecover; -------------------------------------------------------------------------------- /theme/assets/bsify.scroll_to.js: -------------------------------------------------------------------------------- 1 | var ScrollTo = {}; 2 | var scroll_selector = '[data-scroll-to]'; 3 | 4 | ScrollTo.init = function () { 5 | $(document).on('click', scroll_selector, function (e) { 6 | e.preventDefault(); 7 | var target = $(this).attr('href'); 8 | ScrollTo.go(target); 9 | }); 10 | }; 11 | 12 | ScrollTo.go = function (target) { 13 | $('body,html').animate({ 14 | scrollTop: $(target).offset().top 15 | }, 800); 16 | }; 17 | 18 | module.exports = ScrollTo; -------------------------------------------------------------------------------- /theme/assets/bsify.social_feeds.js: -------------------------------------------------------------------------------- 1 | var Instagram = require('./bsify.social_feed_instagram.js'); 2 | 3 | var SocialFeeds = {}; 4 | 5 | SocialFeeds.init = function () { 6 | SocialFeeds.instagram(); 7 | }; 8 | 9 | SocialFeeds.instagram = function () { 10 | var eles = SocialFeeds._get_elements('instagram'); 11 | if (eles.length > 0) { 12 | eles.each(function () { 13 | new Instagram(this); 14 | }); 15 | } 16 | }; 17 | 18 | SocialFeeds._get_elements = function (service) { 19 | var ele_selector = '[data-social-feed="'+service+'"]'; 20 | return $(ele_selector); 21 | }; 22 | 23 | module.exports = SocialFeeds; -------------------------------------------------------------------------------- /theme/assets/bsify.thumbnails.js: -------------------------------------------------------------------------------- 1 | require('uniformThumbnails'); 2 | require('jquery-orderly'); 3 | 4 | var Thumbnails = {}; 5 | 6 | Thumbnails.init = function () { 7 | var opts = {}; 8 | if (Bsify.thumbnail_settings.constrain_images) { 9 | 10 | opts.fit = (Bsify.thumbnail_settings.crop_images)? 'crop' : 'scale'; 11 | opts.align = Bsify.thumbnail_settings.image_alignment.toLowerCase(); 12 | var ratio = Bsify.thumbnail_settings.image_ratio.toLowerCase(); 13 | if (ratio === 'square' || ratio === 'landscape' || ratio === 'portrait') { 14 | opts.format = ratio; 15 | } else { 16 | opts.ratio = ratio; 17 | } 18 | 19 | var $img_wrappers = $(Bsify.thumbnail_selectors.image_wrapper); 20 | 21 | if ($img_wrappers.length > 0) { 22 | $img_wrappers.uniform_thumbnails(opts).on('ut_complete', function(){ 23 | Thumbnails.orderly(); 24 | }); 25 | } else { 26 | // just use orderly 27 | Thumbnails.orderly(); 28 | } 29 | } else { 30 | // just use orderly 31 | Thumbnails.orderly(); 32 | } 33 | 34 | // Always unifrom thumbs on product thumbnails 35 | $('.product-image-thumbs li a').uniform_thumbnails(opts); 36 | }; 37 | 38 | Thumbnails.orderly = function () { 39 | $(Bsify.thumbnail_selectors.details_wrapper).orderly({ method: 'children' }); 40 | }; 41 | 42 | module.exports = Thumbnails; -------------------------------------------------------------------------------- /theme/assets/bsify.touch.js: -------------------------------------------------------------------------------- 1 | var Swipe = function () { 2 | this.threshold = 5; 3 | this.xDown = null; 4 | this.yDown = null; 5 | this.add_event_listeners(); 6 | }; 7 | 8 | Swipe.prototype.add_event_listeners = function () { 9 | var _this = this; 10 | if (Element.prototype.addEventListener) { 11 | document.addEventListener('touchstart', function (evt) { 12 | _this.handleTouchStart(evt); 13 | }, false); 14 | document.addEventListener('touchmove', function (evt) { 15 | _this.handleTouchMove(evt); 16 | }, false); 17 | } 18 | }; 19 | 20 | Swipe.prototype.handleTouchStart = function (evt) { 21 | this.xDown = evt.touches[0].clientX; 22 | this.yDown = evt.touches[0].clientY; 23 | }; 24 | 25 | Swipe.prototype.handleTouchMove = function (evt) { 26 | if ( ! this.xDown || ! this.yDown ) return; 27 | 28 | var current_target = evt.targetTouches[0].target; 29 | 30 | var xUp = evt.touches[0].clientX; 31 | var yUp = evt.touches[0].clientY; 32 | 33 | var xDiff = this.xDown - xUp; 34 | var yDiff = this.yDown - yUp; 35 | 36 | if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) { 37 | if ( xDiff > 0 ) { 38 | if (xDiff > this.threshold) { 39 | $(current_target).trigger('swipeLeft'); 40 | } 41 | } else { 42 | if (xDiff * -1 > this.threshold) { 43 | $(current_target).trigger('swipeRight'); 44 | } 45 | } 46 | } else { 47 | if ( yDiff > 0 ) { 48 | if (yDiff > this.threshold) { 49 | $(current_target).trigger('swipeUp'); 50 | } 51 | } else { 52 | if (yDiff * -1 > this.threshold) { 53 | $(current_target).trigger('swipeDown'); 54 | } 55 | } 56 | } 57 | /* reset values */ 58 | this.xDown = null; 59 | this.yDown = null; 60 | }; 61 | 62 | new Swipe(); -------------------------------------------------------------------------------- /theme/assets/bsify.variant_option_switcher.js: -------------------------------------------------------------------------------- 1 | var VariantOptionSwitcher = {}; 2 | 3 | // Switch the variant if the gallery thumb is linked to one 4 | VariantOptionSwitcher.image_variant_switcher = function () { 5 | 6 | var $product_selector = $(Bsify.selectors.product.select_selector); 7 | 8 | $(document).on('click', '[data-variant-id]', function (e) { 9 | e.preventDefault(); 10 | var $current_target = $(e.currentTarget); 11 | // update the product selector with the new variant id 12 | $product_selector.val($current_target.data('variant-id')); 13 | // update the option selectors 14 | VariantOptionSwitcher.update_product_selector_options($product_selector); 15 | }); 16 | }; 17 | 18 | // Make the original variant selector update the generated option selectors 19 | // we are not doing this on a trigger event because if we did we'd end up in recursive hell 20 | VariantOptionSwitcher.update_product_selector_options = function ($product_selector) { 21 | 22 | // get the selected variant object 23 | var variant_id = $product_selector.val(); 24 | var variant = Bsify.get_variant_by_id(variant_id); 25 | 26 | if (variant) { 27 | // update each option selector 28 | for (var i = 0; i < Bsify.product.options.length; i++) { 29 | var option = 'option'+(i+1); 30 | var value = variant[option]; 31 | $('.single-option-selector:eq('+i+')').val(value).trigger('change'); 32 | } 33 | } 34 | }; 35 | 36 | module.exports = VariantOptionSwitcher; -------------------------------------------------------------------------------- /theme/assets/ico-gift-card.svg.liquid: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/assets/respond-proxy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Respond JS Proxy 7 | 8 | 9 | 95 | 96 | -------------------------------------------------------------------------------- /theme/snippets/_collection_title_filters.liquid: -------------------------------------------------------------------------------- 1 | {% capture collection_title_filters %} 2 | {% for tag in current_tags %} 3 | {{ tag | split: ':' | last }}{% if forloop.length > 1 and forloop.index != forloop.length %}{% if forloop.rindex0 == 1 %}{{ 'collections.collection_title_when_filtered.filter_last_separator' | t }}{% else %}{{ 'collections.collection_title_when_filtered.filter_separator' | t }}{% endif %}{% endif %} 4 | {% endfor %} 5 | {% endcapture %} -------------------------------------------------------------------------------- /theme/snippets/_extract_images.liquid: -------------------------------------------------------------------------------- 1 | {% capture function %} 2 | {% comment Note: 3 | 4 | Takes a block of content, separates out the images from the text and returns the images. 5 | We split the content on tags and spaces then scan through it all looking for the things we want. 6 | Why?! Because reasons! 7 | 8 | You can pass in a variable called `image_size` which will return any sized image with this new size. 9 | If you do this then the returned `images` variable will be the sized images and you will get an `unsized_images` variable 10 | which contains the images before they were resized. This is handy for removing them from content. 11 | 12 | %}{% endcomment %} 13 | 14 | {% comment note: Split content %}{% endcomment %} 15 | {% assign split = _extract_images | split:' ' | join:'~~' | replace: '>', '>~~' | replace: '<', '~~<' | split: '~~' %} 16 | 17 | {% comment note: Find all the images %}{% endcomment %} 18 | {% assign in_img = false %} 19 | {% capture images %}{% endcapture %} 20 | {% for s in split %} 21 | {% unless s == blank %} 22 | {% if s == '' %} 29 | {% assign in_img = false %} 30 | {% endif %} 31 | {% endunless %} 32 | {% endfor %} 33 | 34 | {% if image_size %} 35 | {% assign new_size = image_size | prepend:'_' | append:'.' %} 36 | {% assign image_sizes = 'pico|icon|thumb|small|compact|medium|large|grande|original|1024x1024|2048x2048|master' | split: '|' %} 37 | {% assign images = images | replace: ' <','~~<' | remove_first: '~~' | split: '~~' %} 38 | {% assign unsized_images = images %} 39 | {% capture sized_images %}{% endcapture %} 40 | 41 | {% for image in images %} 42 | {% assign new_image = false %} 43 | {% for size in image_sizes %} 44 | {% assign size_match = size | prepend:'_' | append:'.' %} 45 | {% if image contains size_match %} 46 | {% if size != image_size %} 47 | {% assign new_image = image | replace: size_match, new_size %} 48 | {% endif %} 49 | {% endif %} 50 | {% endfor %} 51 | 52 | {% if new_image %} 53 | {% capture sized_images %}{{ sized_images }} {{ new_image }}{% endcapture %} 54 | {% else %} 55 | {% capture sized_images %}{{ sized_images }} {{ image }}{% endcapture %} 56 | {% endif %} 57 | {% endfor %} 58 | {% assign images = sized_images %} 59 | {% endif %} 60 | 61 | {% assign images = images | replace: ' <','~~<' | remove_first: '~~' | split: '~~' %} 62 | {% assign image_size = null %} 63 | {% endcapture %} -------------------------------------------------------------------------------- /theme/snippets/_has_menu.liquid: -------------------------------------------------------------------------------- 1 | {% if settings.main_menu_linklist == blank and settings.secondary_menu_linklist == blank and settings.tertiary_menu_linklist == blank %} 2 | {% assign has_menu = false %} 3 | {% else %} 4 | {% assign has_menu = true %} 5 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/_page_url.liquid: -------------------------------------------------------------------------------- 1 | {% capture function %} 2 | {% comment note: 3 | Shopify, crazily enough, does not have a global object that returns the current url like it does for the page_title. 4 | So lets fake one. 5 | It will take into account filtered collections. 6 | It will not take into account url params and hash anchors, because reasons. 7 | page_url_with_shop_domain gives you the page_url with the shop.url prepended. 8 | %}{% endcomment %} 9 | 10 | {% if template contains '404' %} 11 | {% assign page_url = '/404' %} 12 | {% elsif template contains 'article' %} 13 | {% assign page_url = article.url %} 14 | {% elsif template contains 'blog' %} 15 | {% assign page_url = blog.url %} 16 | {% elsif template contains 'cart' %} 17 | {% assign page_url = '/cart' %} 18 | {% elsif template contains 'list-collections' %} 19 | {% assign page_url = '/collections' %} 20 | {% elsif template contains 'collection' %} 21 | {% if collection.url != blank %} 22 | {% assign page_url = collection.url %} 23 | {% else %} 24 | {% assign page_url = '/collections/all' %} 25 | {% endif %} 26 | {% if current_tags.size > 0 %} 27 | {% capture current_tag_params %}{% endcapture %} 28 | {% for current_tag in current_tags %} 29 | {% capture current_tag_params %}{{ current_tag_params }}{% unless forloop.first %}+{% endunless %}{{ current_tag | handle }}{% endcapture %} 30 | {% endfor %} 31 | {% assign page_url = page_url | append: '/' | append: current_tag_params %} 32 | {% endif %} 33 | {% elsif template contains 'index' %} 34 | {% assign page_url = '/' %} 35 | {% elsif template contains 'page' %} 36 | {% assign page_url = page.url %} 37 | {% elsif template contains 'product' %} 38 | {% assign page_url = product.url %} 39 | {% elsif template contains 'search' %} 40 | {% assign page_url = '/search' %} 41 | {% elsif template contains 'customers' %} 42 | {% if template contains 'activate_account' %} 43 | {% assign page_url = '/account/activate' %} 44 | {% elsif template contains 'account' %} 45 | {% assign page_url = '/account' %} 46 | {% elsif template contains 'addresses' %} 47 | {% assign page_url = '/account/addresses' %} 48 | {% elsif template contains 'login' %} 49 | {% assign page_url = '/account/login' %} 50 | {% elsif template contains 'order' %} 51 | {% if order != blank %} 52 | {% assign page_url = order.customer_url | remove: shop.url %} 53 | {% else %} 54 | {% assign page_url = '/account/orders' %} 55 | {% endif %} 56 | {% elsif template contains 'register' %} 57 | {% assign page_url = '/account/register' %} 58 | {% elsif template contains 'reset' %} 59 | {% assign page_url = '/account/reset' %} 60 | {% else %} 61 | {% assign page_url = null %} 62 | {% endif %} 63 | {% else %} 64 | {% assign page_url = null %} 65 | {% endif %} 66 | 67 | {% if page_url != null %} 68 | {% assign page_url_with_shop_domain = page_url | prepend: shop.url %} 69 | {% endif %} 70 | {% endcapture %} -------------------------------------------------------------------------------- /theme/snippets/_price.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | The `_price` snippet is is used for outputting a price in a consistent way across the theme. 4 | It also contains the needed data for the currency switcher to do what it needs to do. 5 | 6 | It takes one `with` parameter which is the unformatted price. 7 | If you want to show a translation output for a zero price (i.e. 'free' instead of $0.00) then set the variable `show_zero` to true outside the snippet before you include it. 8 | You can also pass a string as the price parameter as long as it is wrapped in '[]'. This is useful if you are using it inside javascript as a template, where you want to use a string replace. 9 | 10 | To use the `money_with_currency` format use the `_price_with_currency.liquid` snippet 11 | 12 | %}{% endcomment %} 13 | {% if _price == 0 and show_zero %}{{ 'products.price.zero_price' | t }}{% elsif _price contains '[' %}{{ _price }}{% else %}{{ _price | money }}{% endif %} 14 | {% assign show_zero = false %} -------------------------------------------------------------------------------- /theme/snippets/_price_with_currency.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | The `_price_with_currency` snippet is is used for outputting a price in a consistent way across the theme. 4 | It also contains the needed data for the currency switcher to do what it needs to do. 5 | 6 | It takes one `with` parameter which is the unformatted price. 7 | If you want to show a translation output for a zero price (i.e. 'free' instead of $0.00) then set the variable `show_zero` to true outside the snippet before you include it. 8 | You can also pass a string as the price parameter. This is useful if you are using it inside javascript as a template, where you want to use a string replace. 9 | 10 | To use the `money` format use the `_price.liquid` snippet 11 | 12 | %}{% endcomment %} 13 | {% assign _price = _price_with_currency %} 14 | {% if _price == 0 and show_zero %}{{ 'products.price.zero_price' | t }}{% elsif _price contains '[' %}{{ _price }}{% else %}{{ _price | money_with_currency }}{% endif %} 15 | {% assign show_zero = false %} -------------------------------------------------------------------------------- /theme/snippets/additional_footer_content.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Additional Footer Content 4 | Add any custom and project specific scripts that you want included at the end of the theme. 5 | 6 | %}{% endcomment %} -------------------------------------------------------------------------------- /theme/snippets/additional_header_content.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Additional Header Content 4 | Add any custom and project specific inline scripts and styles that you want included inside the theme head. 5 | 6 | %}{% endcomment %} -------------------------------------------------------------------------------- /theme/snippets/advanced_hook.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Advanced Layout Hook 4 | 5 | This will allow you to use an advanced layout linklist on any template. 6 | All you need to do is have a linklist with a correctly constructed handle. 7 | 8 | The linkists handle must match the formula: (pre|post)-template[-handle] 9 | 10 | - pre|post: The hook is available both before and after a templates content with `pre` & `post` respectively. 11 | - template: The type of template you wish to have the layout on e.g. page/collection/product/etc. 12 | - handle: The handle is optional. This specifies the exact page to include the layout on. If handle is omitted then the linklist 13 | will be shown on ALL pages with this template. Use this like a blanket layout i.e. one linklist for all collections. 14 | Linklists with handles will take precedence over those without. 15 | 16 | %}{% endcomment %} 17 | 18 | {% capture template_hook %}{{ advanced_hook }}-{{ template | split: '.' | first }}{% endcapture %} 19 | {% capture page_hook %}{{ template_hook }}{% unless handle == blank %}-{{ handle }}{% endunless %}{% endcapture %} 20 | 21 | {% assign advanced_hook_linklist = null %} 22 | {% if linklists[page_hook] != blank %} 23 | {% assign advanced_hook_linklist = page_hook %} 24 | {% elsif linklists[template_hook] != blank %} 25 | {% assign advanced_hook_linklist = template_hook %} 26 | {% endif %} 27 | 28 | {% if advanced_hook_linklist %} 29 | {% include 'advanced_layout' with advanced_hook_linklist %} 30 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/advanced_layout_banner_video.liquid: -------------------------------------------------------------------------------- 1 | {% if settings.video_banner_url != blank %} 2 | 23 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/advanced_layout_blog.liquid: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 | {% assign blog = advanced_layout_blog %} 7 | {% include 'article_recent' %} 8 |
-------------------------------------------------------------------------------- /theme/snippets/advanced_layout_collections.liquid: -------------------------------------------------------------------------------- 1 | {% if c.collection.products_count > 0 %} 2 |
3 | 6 | 7 |
8 | {% for l in advanced_layout_collections.links %} 9 | {% assign c = l.object %} 10 | {% if c.collection.products_count > 0 %} 11 | {% assign collection_item_title = l.title %} 12 | {% include 'collection_item' %} 13 | {% endif %} 14 | {% endfor %} 15 |
16 |
17 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/advanced_layout_page.liquid: -------------------------------------------------------------------------------- 1 |
2 | 9 | 10 |
11 | {% if advanced_layout_page.template_suffix == 'split' %} 12 | {% include 'content_split' with advanced_layout_page.content %} 13 | {% else %} 14 |
15 | {{ advanced_layout_page.content }} 16 |
17 | {% endif %} 18 |
19 |
20 | -------------------------------------------------------------------------------- /theme/snippets/advanced_layout_products.liquid: -------------------------------------------------------------------------------- 1 | {% if advanced_layout_products.products.size > 0 %} 2 | {% assign original_collection = collection %} 3 | {% assign collection = advanced_layout_products %} 4 | {% assign advanced_layout_products_url = advanced_layout_products.url | default: '/collections/all' %} 5 | 6 | {% assign product_limit = settings.homepage_limit_products | times: 1 %} 7 | {% assign advanced_layout_products_all_size = advanced_layout_products.all_products.size %} 8 | {% if settings.show_product_variants_as_items %} 9 | {% assign advanced_layout_products_all_size = advanced_layout_products.all_products | map: 'variants' | map: 'title' | size %} 10 | {% endif %} 11 | {% if advanced_layout_products_all_size < product_limit %}{% assign collection_size = advanced_layout_products_all_size %}{% else %}{% assign collection_size = product_limit %}{% endif %} 12 |
13 | 24 | 25 |
26 | {% assign product_limit_counter = 0 %} 27 | {% for p in advanced_layout_products.products limit: product_limit %} 28 | {% include 'product_item' %} 29 | {% endfor %} 30 |
31 |
32 | {% assign collection = original_collection %} 33 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/advanced_layout_search.liquid: -------------------------------------------------------------------------------- 1 | {% capture search_title %}{{ 'search.advanced_layout.title_html' | t: shop_name: shop.name }}{% endcapture %} 2 | {% capture search_description %}{{ 'search.advanced_layout.description_html' | t }}{% endcapture %} 3 | 4 |
5 | {% unless search_title == blank %} 6 | 10 | {% endunless %} 11 |
12 | {% assign form_classes = 'search-form' %} 13 | {% assign form_size = 'lg' %} 14 | {% assign override_enabled_setting = true %} 15 | {% include 'search_form' %} 16 |
17 |
-------------------------------------------------------------------------------- /theme/snippets/advanced_layout_section.liquid: -------------------------------------------------------------------------------- 1 | {% unless advanced_layout_section_content == blank %} 2 |
3 | {{ advanced_layout_section_content }} 4 |
5 | {% assign section_count = section_count | plus: 1 %} 6 | {% assign advanced_layout_section_content = '' %} 7 | {% endunless %} -------------------------------------------------------------------------------- /theme/snippets/advanced_layout_social.liquid: -------------------------------------------------------------------------------- 1 | {% capture social_service %}{% include 'social_service' with advanced_layout_social.url %}{% endcapture %} 2 | {% capture social_feed %}{% include 'social_feed' with advanced_layout_social.url %}{% endcapture %} 3 | {% capture params %}{{ social_service }}||{{ advanced_layout_social.title }}{% endcapture %} 4 | {% include 'social_content' with params %} 5 | 6 | {% unless social_feed == blank %} 7 | 16 | {% endunless %} -------------------------------------------------------------------------------- /theme/snippets/advanced_layout_sub_blog.liquid: -------------------------------------------------------------------------------- 1 |
2 |
3 | {% include 'site_blog_list' with advanced_layout_sub_blog %} 4 |
5 |
-------------------------------------------------------------------------------- /theme/snippets/advanced_layout_sub_page.liquid: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {% if advanced_layout_sub_page.published_at != blank %} 5 |

{{ advanced_layout_sub_page.title }}

6 | {% else %} 7 |

{{ advanced_layout_sub_page.title }}

8 | {% endif %} 9 |
10 |
11 | {% if advanced_layout_sub_page.published_at != blank %} 12 | {% comment note: the page is published so only show the first paragraph %}{% endcomment %} 13 | {% assign parts = advanced_layout_sub_page.content | split:'

' %} 14 |

{{ parts.first | remove: '

' }}

15 | {% else %} 16 | {{ advanced_layout_sub_page.content }} 17 | {% endif %} 18 |
19 |
20 |
-------------------------------------------------------------------------------- /theme/snippets/advanced_layout_sub_search.liquid: -------------------------------------------------------------------------------- 1 | {% capture search_title %}{{ 'search.advanced_layout.title_html' | t: shop_name: shop.name }}{% endcapture %} 2 | {% capture search_description %}{{ 'search.advanced_layout.description_html' | t }}{% endcapture %} 3 | 4 |
5 |
6 | {% unless search_title == blank %}

{{ search_title }}

{% endunless %} 7 | {% unless search_description == blank %}

{{ search_description }}

{% endunless %} 8 | {% assign form_classes = 'search-form' %} 9 | {% assign form_size = 'lg' %} 10 | {% assign override_enabled_setting = true %} 11 | {% include 'search_form' %} 12 |
13 |
-------------------------------------------------------------------------------- /theme/snippets/advanced_layout_sub_social.liquid: -------------------------------------------------------------------------------- 1 | {% capture social_service %}{% include 'social_service' with advanced_layout_sub_social.url %}{% endcapture %} 2 | {% capture social_feed %}{% include 'social_feed' with advanced_layout_sub_social.url %}{% endcapture %} 3 | {% capture params %}{{ social_service }}||{{ advanced_layout_sub_social.title }}{% endcapture %} 4 | {% include 'social_content' with params %} 5 | 6 | {% unless social_feed == blank %} 7 |
8 | 15 |
16 | {% endunless %} -------------------------------------------------------------------------------- /theme/snippets/article_comment.liquid: -------------------------------------------------------------------------------- 1 |
  • 2 | {% if settings.enable_gravatars %} 3 |
    4 | 5 | {{ comment.author }} 6 | 7 |
    8 | {% endif %} 9 |
    10 | {% capture comment_date %}{{ comment.created_at | date: format: 'article_short' }}{% endcapture %} 11 | {{ 'blog.comments.comment_meta_html' | t: comment_date: comment_date, comment_author: comment.author }} 12 | {{ comment.content }} 13 |
    14 |
  • -------------------------------------------------------------------------------- /theme/snippets/article_comments.liquid: -------------------------------------------------------------------------------- 1 |
    2 |

    {{ 'blog.comments.title' | t }}

    3 | {% if article.comments.size > 0 %} 4 | {% include 'pagination_limit' with 'comments' %} 5 | {% paginate article.comments by pagination_limit %} 6 | 7 |
      8 | {% for comment in article.comments %} 9 | {% include 'article_comment' %} 10 | {% endfor %} 11 |
    12 | 13 | {% include 'pagination' %} 14 | {% endpaginate %} 15 | {% else %} 16 |

    {{ 'blog.comments.no_comments_message' | t }}

    17 | {% endif %} 18 |
    19 | 20 | {% include 'article_comments_form' %} -------------------------------------------------------------------------------- /theme/snippets/article_comments_form.liquid: -------------------------------------------------------------------------------- 1 |
    2 |

    {{ 'blog.comments.comment_form_title' | t }}

    3 | {% form article %} 4 | 5 | {% if form.posted_successfully? %} 6 | {% if blog.moderated? %} 7 |
    {{ 'blog.comments.success_message_moderated' | t }}
    8 | {% else %} 9 |
    {{ 'blog.comments.success_message' | t }}
    10 | {% endif %} 11 | {% else %} 12 | {% assign form_errors_message = 'blog.comments.error_message' | t %} 13 | {% include 'form_errors' %} 14 | {% endif %} 15 | 16 |
    17 | 18 | 19 |
    20 | 21 |
    22 | 23 | 24 |
    25 | {% capture help_note %}{{ 'blog.comments_form.email_help_note' | t }}{% endcapture %} 26 | {% unless help_note == blank %} 27 | {{ help_note }} 28 | {% endunless %} 29 | 30 |
    31 | 32 | 33 |
    34 | 35 | 36 | {% if blog.moderated? %}{{ 'blog.comments_form.moderated_note' | t }}{% endif %} 37 | {% endform %} 38 |
    -------------------------------------------------------------------------------- /theme/snippets/article_item.liquid: -------------------------------------------------------------------------------- 1 | {% if settings.truncate_article_results == blank %} 2 | {% assign content = article.excerpt_or_content %} 3 | {% else %} 4 | {% assign content = article.excerpt_or_content | strip_html | truncatewords: settings.truncate_article_results %} 5 | {% endif %} 6 | 7 | -------------------------------------------------------------------------------- /theme/snippets/article_meta.liquid: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/snippets/article_recent.liquid: -------------------------------------------------------------------------------- 1 | {% assign recent_articles_limit = settings.recent_articles_limit | default: 3 | times: 1 %} 2 | {% if article %} 3 | {% assign recent_articles_limit_buffered = recent_articles_limit | plus: 1 %} 4 | {% else %} 5 | {% assign recent_articles_limit_buffered = recent_articles_limit %} 6 | {% endif %} 7 | {% assign recent_articles_count = 0 %} 8 | 9 | {% if settings.show_recent_articles == 'list' %} 10 |
      11 | {% for recent_article in blog.articles limit: recent_articles_limit_buffered %} 12 | {% unless recent_article.id == article.id or recent_articles_count > recent_articles_limit %} 13 | {% assign recent_articles_count = recent_articles_count | plus: 1 %} 14 |
    • 15 |
      {{ recent_article.title }}
      16 | {% capture date %}{{ recent_article.published_at | date: format: 'article_short' }}{% endcapture %} 17 |

      {{ 'blog.article_list.published_date_html' | t: date: date }}

      18 |
    • 19 | {% endunless %} 20 | {% endfor %} 21 |
    22 | {% elsif settings.show_recent_articles == 'thumbs' %} 23 | {% if blog.articles.size < recent_articles_limit %} 24 | {% assign group_size = blog.articles.size %} 25 | {% else %} 26 | {% assign group_size = recent_articles_limit %} 27 | {% endif %} 28 |
    29 | {% for recent_article in blog.articles limit: recent_articles_limit_buffered %} 30 | {% unless recent_article.id == article.id or recent_articles_count > recent_articles_limit %} 31 | {% assign recent_articles_count = recent_articles_count | plus: 1 %} 32 | 33 |
    34 |
    35 | 36 | {% if recent_article.image %} 37 | {{ recent_article | img_url: '1024x1024' | img_tag: recent_article.title }} 38 | {% else %} 39 | {% include '_extract_images' with recent_article.content %} 40 | {{ images | first }} 41 | {% endif %} 42 | 43 |
    44 |
    {{ recent_article.title }}
    45 | {% capture date %}{{ recent_article.published_at | date: format: 'article_short' }}{% endcapture %} 46 |

    {{ 'blog.article_list.published_date_html' | t: date: date }}

    47 |
    48 |
    49 |
    50 | 51 | {% endunless %} 52 | {% endfor %} 53 |
    54 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/article_social_share_links.liquid: -------------------------------------------------------------------------------- 1 | {% capture share_links %}{% endcapture %} 2 | 3 | {% if settings.article_social_share_facebook %} 4 | {% capture share_links %}{{ share_links }}
  • {% endcapture %} 5 | {% endif %} 6 | 7 | {% if settings.article_social_share_pinterest %} 8 | {% include '_extract_images' with article.content %} 9 | {% assign image = images | first | split: '"' %} 10 | {% for i in image %} 11 | {% if i contains 'cdn.shopify.com' %} 12 | {% assign img_src = i %} 13 | {% endif %} 14 | {% endfor %} 15 | 16 | {% if article.featured_image.src != blank or img_src != blank %} 17 | {% capture share_links %}{{ share_links }}
  • {% endcapture %} 18 | {% endif %} 19 | {% endif %} 20 | 21 | {% if settings.article_social_share_twitter %} 22 | {% capture twitter_handle %}{% unless settings.social_links_twitter == blank %}@{{ settings.social_links_twitter | split: '/' | last }}{% endunless %}{% endcapture %} 23 | {% capture tweet %}https://twitter.com/intent/tweet?text={{ 'blog.social_links.twitter_message' | t: article_title: article.title, url: article.url, handle: twitter_handle }}{% endcapture %} 24 | {% capture share_links %}{{ share_links }}
  • {% endcapture %} 25 | {% endif %} 26 | 27 | {% if settings.article_social_share_email %} 28 | {% capture email_subject %}{{ 'blog.social_links.email_subject' | t: article_title: article.title, shop_name: shop.name, url: article.url }}{% endcapture %} 29 | {% capture email_body %}{{ 'blog.social_links.email_body' | t: article_title: article.title, shop_name: shop.name, url: article.url }}{% endcapture %} 30 | {% capture share_links %}{{ share_links }}
  • {% endcapture %} 31 | {% endif %} 32 | 33 | {% unless share_links == blank %} 34 | 43 | {% endunless %} 44 | -------------------------------------------------------------------------------- /theme/snippets/collection_controls.liquid: -------------------------------------------------------------------------------- 1 | {% if collection.all_products_count > 0 %} 2 |
    3 | {% include 'collection_controls_template_switcher' %} 4 | {% include 'collection_controls_sort_by' %} 5 | {% include 'collection_controls_filter_by' %} 6 |
    7 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/collection_controls_filter_by.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Collection Filter by Tags 4 | link_to_tag url filter will take into account the sort order and view url params 5 | 6 | Tags can be set as compounding or not. 7 | Tags can be viewed as dropdowns or a tag list. 8 | 9 | Group tags into a new filter by using this syntax `group:tag` 10 | If a tag group only contains one tag and all the products in the collection has that tag then it won't be shown. <-- TODO 11 | 12 | %}{% endcomment %} 13 | 14 | {% if settings.show_collection_filter_by %} 15 | 16 | {% if current_tags.size > 0 %} 17 | 22 | {% endif %} 23 | 24 | {% if collection.all_tags.size > 0 %} 25 | {% comment note: Group tags 26 | Collect group names from each tag. 27 | We have to loop the collected tag groups to check if it contains the current tag otherwise we get an issue where 'group' is not included if 'grouping' comes before it. 28 | %}{% endcomment %} 29 | 30 | {% capture tag_groups %}TAGS{% endcapture %} 31 | {% assign hidden_tag_groups = settings.hidden_tag_group_list | downcase | split: ',' %} 32 | {% for tag in collection.all_tags %} 33 | {% if tag contains ':' %} 34 | {% assign group_name = tag | split: ':' | first %} 35 | {% assign group_name_lower = group_name | downcase %} 36 | 37 | {% unless hidden_tag_groups contains group_name_lower %} 38 | {% assign is_in_tag_groups = false %} 39 | {% assign group_names = tag_groups | downcase | split: '|' %} 40 | {% if group_names contains group_name_lower %} 41 | {% assign is_in_tag_groups = true %} 42 | {% endif %} 43 | 44 | {% unless is_in_tag_groups %} 45 | {% capture tag_groups %}{{ tag_groups }}|{{ group_name }}{% endcapture %} 46 | {% endunless %} 47 | {% endunless %} 48 | {% endif %} 49 | {% endfor %} 50 | 51 |
    52 | {% assign tag_groups = tag_groups | split: '|' %} 53 | {% for tag_group in tag_groups %} 54 | {% assign has_tag_group = false %} 55 | {% unless tag_group == 'TAGS' %} 56 | {% assign has_tag_group = true %} 57 | {% endunless %} 58 | 59 | {% capture tags %}{% include 'collection_controls_filter_by_tag_group' %}{% endcapture %} 60 | {% capture tag_group_title %}{% if has_tag_group %}{{ 'collections.filtering.filter_by_group_title' | t: group: tag_group }}{% else %}{{ 'collections.filtering.filter_by_title' | t }}{% endif %}{% endcapture %} 61 | {% unless tags == blank %} 62 |
    63 | {% case settings.filter_by_tag_style %} 64 | {% when 'dropdown' %} 65 | 73 | 74 | {% when 'button' %} 75 | 79 | 80 | {% endcase %} 81 |
    82 | {% endunless %} 83 | {% endfor %} 84 |
    85 | {% endif %} 86 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/collection_controls_filter_by_tag.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | Don't show this tag if it exists on every product in the collection 3 | %}{% endcomment %} 4 | 5 | {% assign tag_count = 0 %} 6 | {% for product in collection.all_products %} 7 | {% for product_tag in product.tags %} 8 | {% if product_tag == tag %} 9 | {% assign tag_count = tag_count | plus: 1 %} 10 | {% endif %} 11 | {% endfor %} 12 | {% endfor %} 13 | 14 | {% assign include_tag = false %} 15 | {% if tag_count != collection.all_products.size or current_tags contains tag %} 16 | {% assign include_tag = true %} 17 | {% endif %} 18 | 19 | {% if include_tag %} 20 | {% if current_tags contains tag %} 21 | {% assign tag_group_contains_active_tags = true %} 22 | {% endif %} 23 | 24 | {% if current_tags contains tag %} 25 | {% capture tag_text %}{{ tag_value }} {{ 'collections.filtering.tag_title_remove' | t: tag: tag_value }}{% endcapture %} 26 |
  • {{ tag_text | link_to_remove_tag: tag }}
  • 27 | {% else %} 28 | {% if settings.filter_collection_by_multiple_tags %} 29 |
  • {{ tag_value | link_to_add_tag: tag }}
  • 30 | {% else %} 31 |
  • {{ tag_value | link_to_tag: tag }}
  • 32 | {% endif %} 33 | {% endif %} 34 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/collection_controls_filter_by_tag_group.liquid: -------------------------------------------------------------------------------- 1 | {% assign tag_group_contains_active_tags = false %} 2 | {% for tag in collection.all_tags %} 3 | {% if has_tag_group %} 4 | {% assign group = tag | split: ':' | first %} 5 | {% assign tag_value = tag | split: ':' | last %} 6 | {% if tag_group == group %} 7 | {% include 'collection_controls_filter_by_tag' %} 8 | {% endif %} 9 | {% else %} 10 | {% unless tag contains ':' %} 11 | {% assign tag_value = tag %} 12 | {% include 'collection_controls_filter_by_tag' %} 13 | {% endunless %} 14 | {% endif %} 15 | {% endfor %} -------------------------------------------------------------------------------- /theme/snippets/collection_controls_sort_by.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Collection Sort By 4 | 5 | %}{% endcomment %} 6 | 7 | {% if settings.show_collection_sort_by %} 8 |
    9 | 43 |
    44 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/collection_controls_template_switcher.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Collection template switcher 4 | Collect the: 5 | - Default template 6 | - Current template 7 | - Enabled templates list 8 | Then if we have enabled templates other than the default template show them. 9 | 10 | %}{% endcomment %} 11 | 12 | {% if collection %} 13 | {% assign default_template = collection.template_suffix | default: 'grid' %} 14 | {% endif %} 15 | {% assign current_template = template | split: '.' | last %} 16 | {% if current_template == 'collection' %}{% assign current_template = 'grid' %}{% endif %} 17 | {% assign collection_templates = 'grid,list,lookbook' | split: ',' %} 18 | {% assign enabled_templates = '' %} 19 | {% for collection_template in collection_templates %} 20 | {% assign setting_key = 'collection_template_' | append: collection_template %} 21 | {% if settings[setting_key] or collection_template == default_template %} 22 | {% assign enabled_templates = enabled_templates | append: collection_template | append: ',' %} 23 | {% endif %} 24 | {% endfor %} 25 | {% assign enabled_templates = enabled_templates | split: ',' %} 26 | 27 | {% if enabled_templates.size > 1 %} 28 |
    29 | 46 |
    47 | {% endif %} 48 | -------------------------------------------------------------------------------- /theme/snippets/collection_header_component_title.liquid: -------------------------------------------------------------------------------- 1 | {% if current_tags.size > 0 %} 2 | {% include '_collection_title_filters' %} 3 |

    {{ 'collections.collection_title_when_filtered.title_html' | t: title: collection_title, filters: collection_title_filters }}

    4 | {% else %} 5 |

    {{ collection_title }}

    6 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/collection_header_layout_banner_description.liquid: -------------------------------------------------------------------------------- 1 |
    2 | {% if collection_image %} 3 | 11 |
    12 | 15 |
    16 | 17 | {% else %} 18 |
    19 | 25 |
    26 | {% endif %} 27 |
    -------------------------------------------------------------------------------- /theme/snippets/collection_header_layout_banner_image.liquid: -------------------------------------------------------------------------------- 1 |
    2 | {% if collection_image %} 3 | 6 | {% endif %} 7 | 8 |
    9 | 16 |
    17 |
    -------------------------------------------------------------------------------- /theme/snippets/collection_header_layout_split.liquid: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 23 |
    24 |
    -------------------------------------------------------------------------------- /theme/snippets/collection_header_layout_standard.liquid: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 10 |
    11 |
    -------------------------------------------------------------------------------- /theme/snippets/collection_item.liquid: -------------------------------------------------------------------------------- 1 | {% assign collection_title = collection_item_title | default: c.title %} 2 | 26 | {% assign collection_item_title = null %} -------------------------------------------------------------------------------- /theme/snippets/contact_form.liquid: -------------------------------------------------------------------------------- 1 | {% form 'contact' %} 2 | {% if form.posted_successfully? %} 3 |

    4 | {{ 'contact.form.post_success_html' | t }} 5 |

    6 | {% endif %} 7 | 8 | {{ form.errors | default_errors }} 9 | 10 |
    11 | {% assign name_attr = 'contact.form.name' | t | handle %} 12 | 13 | 14 |
    15 | 16 |
    17 | 18 | 19 |
    20 | 21 |
    22 | {% assign name_attr = 'contact.form.phone' | t | handle %} 23 | 24 | 25 |
    26 | 27 |
    28 | 29 | 30 |
    31 | 32 | 33 | {% endform %} -------------------------------------------------------------------------------- /theme/snippets/content_sections.liquid: -------------------------------------------------------------------------------- 1 | {% unless parts %} 2 | {% assign parts = content_sections | split: '' %} 10 | {% assign section_title = section_parts.first | split: '>' | last %} 11 | {% assign section_content = section_parts.last %} 12 | 13 | {% if section_content != blank %} 14 | {% if forloop.first %} 15 | {% assign content_sections_description = section %} 16 | {% else %} 17 | {% capture content_sections_output %} 18 | {{ content_sections_output }} 19 |
    20 |
    21 |
    22 | {% capture section_content_for_split %} 23 |

    {{ section_title }}

    24 | {{ section_content }} 25 | {% endcapture %} 26 | {% include 'content_split' with section_content_for_split %} 27 |
    28 |
    29 |
    30 | {% endcapture %} 31 | {% endif %} 32 | {% endif %} 33 | {% endfor %} -------------------------------------------------------------------------------- /theme/snippets/content_split.liquid: -------------------------------------------------------------------------------- 1 | {% comment Note: 2 | 3 | Takes a block of content, separates out the images from the text and then wraps 4 | them in markup so that they can be displayed in columns side by side. 5 | 6 | %}{% endcomment %} 7 | 8 | {% if content_split contains 'src=' %} 9 | {% assign image_size = 'grande' %} 10 | {% include '_extract_images' with content_split %} 11 | 12 | {% assign content = content_split %} 13 | {% for image in unsized_images %} 14 | {% assign content = content | remove: image %} 15 | {% endfor %} 16 | {% assign content = content | remove: '

    ' %} 17 | 18 | {% comment note: Output content %}{% endcomment %} 19 |
    20 |
    21 | {{ content }} 22 |
    23 |
    24 | {% for image in images %} 25 | {{ image }} 26 | {% endfor %} 27 |
    28 |
    29 | {% else %} 30 |
    31 | {{ content_split }} 32 |
    33 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/content_tabs.liquid: -------------------------------------------------------------------------------- 1 | {% comment TODO: product tags, vendor, type, etc. %}{% endcomment %} 2 | {% unless parts %} 3 | {% assign parts = content_tabs | split: '' %} 13 | {% assign tab_title = tab_parts.first | split: '>' | last %} 14 | {% assign tab_handle = tab_title | handleize | prepend: 'tab-' %} 15 | 16 | {% if tab_parts.size > 1 %} 17 | {% assign tab_count = tab_count | plus: 1 %} 18 | 19 | {% capture tab_titles %} 20 | {{ tab_titles }} 21 | {{ tab_title }} 22 | {% endcapture %} 23 | 24 | {% capture tab_contents %} 25 | {{ tab_contents }} 26 |
    27 | {{ tab_parts.last }} 28 |
    29 | {% endcapture %} 30 | {% else %} 31 | {% assign content_tabs_description = tab_parts.last %} 32 | {% endif %} 33 | {% endfor %} 34 | 35 | {% capture content_tabs_output %} 36 | 39 |
    40 | {{ tab_contents }} 41 |
    42 | {% endcapture %} 43 | 44 | {% assign parts = null %} -------------------------------------------------------------------------------- /theme/snippets/form_errors.liquid: -------------------------------------------------------------------------------- 1 | {% if form.errors %} 2 |
    3 | {{ form_errors_message }} 4 |
      5 | {% for field in form.errors %} 6 |
    • {% unless field == 'form' %}{{ form.errors.translated_fields[field] }} {% endunless %}{{ form.errors.messages[field] }}
    • 7 | {% endfor %} 8 |
    9 |
    10 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/header_component_brand.liquid: -------------------------------------------------------------------------------- 1 | {% comment TODO: Logos -> text / img / svg / font / etc %}{% endcomment %} 2 | 3 | {% comment note: 4 | 5 | `header_component_brand` shows the stores logo or brand name as text depending on theme settings. 6 | Optional variable parameters that are set outside the snippet before it is included: 7 | 8 | `brand_classes`: 9 | This is set prior to including this snippet and is used for adding special/bootstrap/context classes to the brands `a` tag. 10 | This is optional and if it is not set then no class will be added. 11 | %}{% endcomment %} 12 | 13 | 14 | {% if settings.logo_enabled %} 15 | {{ shop.name | escape }} 16 | {% else %} 17 | {{ shop.name }} 18 | {% endif %} 19 | 20 | 21 | {% comment note: unset optional variable parameters. Because sanity. %}{% endcomment %} 22 | {% assign brand_classes = null %} -------------------------------------------------------------------------------- /theme/snippets/header_component_mobile_navbar.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: Mobile and small screen nav %}{% endcomment %} 2 | {% capture navbar_content %} 3 | 4 | {% comment note: fallback for if no menus are set %}{% endcomment %} 5 | {% if has_menu %} 6 | {% unless settings.main_menu_linklist == blank %} 7 | {% assign nav_classes = 'nav navbar-nav' %} 8 | {% assign is_mobile = true %} 9 | {% include 'nav_list' with settings.main_menu_linklist %} 10 | {% endunless %} 11 | 12 | {% unless settings.secondary_menu_linklist == blank %} 13 | {% assign nav_classes = 'nav navbar-nav' %} 14 | {% assign is_mobile = true %} 15 | {% include 'nav_list' with settings.secondary_menu_linklist %} 16 | {% endunless %} 17 | 18 | {% assign nav_classes = 'nav navbar-nav' %} 19 | {% assign nav_list_items = 'accounts' %} 20 | {% assign is_mobile = true %} 21 | {% include 'nav_list' with settings.tertiary_menu_linklist %} 22 | 23 | {% assign form_classes = 'navbar-form' %} 24 | {% include 'search_form' %} 25 | 26 | {% else %} 27 | {% assign nav_classes = 'nav navbar-nav' %} 28 | {% assign is_mobile = true %} 29 | {% include 'nav_list' with 'main-menu' %} 30 | 31 | {% assign nav_classes = 'nav navbar-nav' %} 32 | {% assign nav_list_items = 'accounts' %} 33 | {% assign is_mobile = true %} 34 | {% include 'nav_list' %} 35 | 36 | {% assign form_classes = 'navbar-form' %} 37 | {% include 'search_form' %} 38 | 39 | {% endif %} 40 | {% endcapture %} 41 | {% assign navbar_classes = 'navbar navbar-default' %} 42 | {% assign include_cart_in_header = true %} 43 | {% include 'header_component_navbar' %} -------------------------------------------------------------------------------- /theme/snippets/header_component_navbar.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | `header_component_navbar` is bootstraps basic navbar. It takes several optional variable parameters that are set outside the snippet before it is included. 4 | These parameters are: 5 | 6 | `navbar_classes`: 7 | This is set prior to including this snippet and is used for adding special/bootstrap/context classes to the navbar. 8 | A class of .navbar will always be used and if navbar_classes is not set before including then this will be the only class used. 9 | 10 | `navbar_content`: 11 | This is a capture that contains the navbars nav list content. 12 | 13 | `include_header`: 14 | A boolean value that dictates whether the bootstrap navbar-header is included. 15 | Default is true. 16 | 17 | `include_brand`: 18 | A boolean value that dictates whether the bootstrap navbar-brand is included. 19 | Default is true, _but_ will not show unless the navbar-header is enabled. 20 | 21 | `include_cart_in_header`: 22 | A boolean value that dictates whether the cart link is shown in the navbar header on mobile. 23 | Default is false. 24 | 25 | Internal variables: 26 | `navbar_count`: 27 | This is an incrementing number that doesn't get unset at the end of the snippet which keeps track of the navbars. 28 | It is used to avoid the bootstrap collapse conflicting. 29 | 30 | %}{% endcomment %} 31 | 32 | {% assign navbar_classes = navbar_classes | replace: ',',' ' | replace: ' ',' ' %} 33 | {% unless navbar_classes contains 'navbar ' %} 34 | {% assign navbar_classes = navbar_classes | prepend: 'navbar ' %} 35 | {% endunless %} 36 | 37 | {% if include_header == null %} 38 | {% assign include_header = true %} 39 | {% endif %} 40 | 41 | {% if include_brand == null %} 42 | {% assign include_brand = true %} 43 | {% endif %} 44 | 45 | {% if navbar_count == null %} 46 | {% assign navbar_count = 0 %} 47 | {% endif %} 48 | {% assign navbar_count = navbar_count | plus: 1 %} 49 | 50 | 80 | 81 | {% comment note: unset optional variable parameters. %}{% endcomment %} 82 | {% assign navbar_classes = null %} 83 | {% assign navbar_content = null %} 84 | {% assign include_header = null %} 85 | {% assign include_brand = null %} 86 | {% assign include_cart_in_header = null %} -------------------------------------------------------------------------------- /theme/snippets/header_layout_centered.liquid: -------------------------------------------------------------------------------- 1 | {% include '_has_menu' %} 2 | 3 |
    4 | 5 |
    6 | {% include 'header_component_mobile_navbar' %} 7 |
    8 | 9 | {% comment note: Desktop and large screen nav %}{% endcomment %} 10 | 51 |
    52 | -------------------------------------------------------------------------------- /theme/snippets/header_layout_logo_left.liquid: -------------------------------------------------------------------------------- 1 | {% include '_has_menu' %} 2 | 3 |
    4 | 5 |
    6 | {% include 'header_component_mobile_navbar' %} 7 |
    8 | 9 | {% comment note: Desktop and large screen nav %}{% endcomment %} 10 | 49 |
    50 | -------------------------------------------------------------------------------- /theme/snippets/header_layout_navbar.liquid: -------------------------------------------------------------------------------- 1 | {% include '_has_menu' %} 2 | 3 |
    4 | {% capture navbar_content %} 5 | {% comment note: Add fall back for no menu in settings %}{% endcomment %} 6 | {% if has_menu %} 7 | 8 |
    9 | {% assign form_classes = 'navbar-form navbar-right' %} 10 | {% include 'search_form' %} 11 | 12 | {% assign nav_classes = 'nav navbar-nav navbar-right' %} 13 | {% assign nav_list_items = 'accounts,cart' %} 14 | {% include 'nav_list' with settings.tertiary_menu_linklist %} 15 |
    16 |
    17 | {% unless settings.main_menu_linklist == blank %} 18 | {% assign nav_classes = 'nav navbar-nav' %} 19 | {% include 'nav_list' with settings.main_menu_linklist %} 20 | {% endunless %} 21 | 22 | {% unless settings.secondary_menu_linklist == blank %} 23 | {% assign nav_classes = 'nav navbar-nav navbar-right' %} 24 | {% include 'nav_list' with settings.secondary_menu_linklist %} 25 | {% endunless %} 26 |
    27 | 28 | {% else %} 29 | {% assign nav_classes = 'nav navbar-nav' %} 30 | {% include 'nav_list' with 'main-menu' %} 31 | 32 | {% assign form_classes = 'navbar-form navbar-right' %} 33 | {% include 'search_form' %} 34 | 35 | {% assign nav_classes = 'nav navbar-nav navbar-right' %} 36 | {% assign nav_list_items = 'accounts,cart' %} 37 | {% include 'nav_list' %} 38 | 39 | {% endif %} 40 | {% endcapture %} 41 | 42 | {% assign navbar_classes = 'navbar navbar-default navbar-static-top' %} 43 | {% assign include_cart_in_header = true %} 44 | {% include 'header_component_navbar' %} 45 | {% include 'header_component_megamenu' %} 46 |
    -------------------------------------------------------------------------------- /theme/snippets/header_layout_stacked.liquid: -------------------------------------------------------------------------------- 1 | {% include '_has_menu' %} 2 | 3 |
    4 | 5 |
    6 | {% include 'header_component_mobile_navbar' %} 7 |
    8 | 9 | 59 |
    -------------------------------------------------------------------------------- /theme/snippets/homepage_onboarding.liquid: -------------------------------------------------------------------------------- 1 | {% comment TODO: On-boarding %}{% endcomment %} 2 | 3 | {% assign advanced_layout_section_object = pages['frontpage'] %} 4 | {% assign advanced_layout_section_id = advanced_layout_section_object.handle %} 5 | {% capture advanced_layout_section_content %}{% include 'advanced_layout_page' with advanced_layout_section_object %}{% endcapture %} 6 | {% include 'advanced_layout_section' %} -------------------------------------------------------------------------------- /theme/snippets/link_list.liquid: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /theme/snippets/page_banner.liquid: -------------------------------------------------------------------------------- 1 | {% assign image_size = '2048x2048' %} 2 | {% include '_extract_images' with content %} 3 | {% assign image = images | first %} 4 | {% assign image_to_remove = unsized_images | first %} 5 | {% assign content = content | remove: image_to_remove %} 6 |
    7 | 15 |
    -------------------------------------------------------------------------------- /theme/snippets/pagination.liquid: -------------------------------------------------------------------------------- 1 | {% unless paginate.previous == blank and paginate.next == blank %} 2 |
    3 | 30 |
    31 | {% endunless %} -------------------------------------------------------------------------------- /theme/snippets/pagination_limit.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | The pagination limit is set in the theme settings with a default fallback to 50 3 | For consistency this snippet must be included anywhere pagination is used 4 | 5 | use this same file for paginating blogs and search results as well: 6 | settings.pagination_limit_products -> {% include 'pagination_limit' with 'products' %} 7 | settings.pagination_limit_search -> {% include 'pagination_limit' with 'search' %} 8 | settings.pagination_limit_articles -> {% include 'pagination_limit' with 'articles' %} 9 | 10 | {% endcomment %} 11 | {% assign pagination_key = 'pagination_limit_' | append: pagination_limit %} 12 | {% assign pagination_limit = settings[pagination_key] | default: 50 | times: 1 %} -------------------------------------------------------------------------------- /theme/snippets/product_badges.liquid: -------------------------------------------------------------------------------- 1 | {% if on_sale and sold_out != true %}{{ 'products.badges.on_sale' | t }}{% endif %} 2 | {% if sold_out %}{{ 'products.badges.sold_out' | t }}{% endif %} 3 | -------------------------------------------------------------------------------- /theme/snippets/product_description.liquid: -------------------------------------------------------------------------------- 1 | {% assign product_description_sections = null %} 2 | {% assign parts = product_description | split: ' 1 and settings.product_content_layout != 'standard' %} 4 | {% case settings.product_content_layout %} 5 | {% when 'tabs' %} 6 | {% include 'content_tabs' with product_description %} 7 | {% if content_tabs_description %} 8 |
    {{ content_tabs_description }}
    9 | {% endif %} 10 | {{ content_tabs_output }} 11 | 12 | {% when 'sections' %} 13 | {% include 'content_sections' with product_description %} 14 | {% if content_sections_description %} 15 |
    {{ content_sections_description }}
    16 | {% endif %} 17 | {% assign product_description_sections = content_sections_output %} 18 | 19 | {% endcase %} 20 | {% else %} 21 |
    {{ product_description }}
    22 | {% endif %} -------------------------------------------------------------------------------- /theme/snippets/product_price.liquid: -------------------------------------------------------------------------------- 1 | {% if settings.hide_pricing != true or p.price > 0 %} 2 |
    3 | {% if p.price_varies %} 4 | {% capture price %}{% assign show_zero = true %}{% include '_price' with p.price_min %}{% endcapture %} 5 | {% capture price_with_currency %}{% assign show_zero = true %}{% include '_price_with_currency' with p.price_min %}{% endcapture %} 6 | {{ 'products.price.from_price_html' | t: price: price, price_with_currency: price_with_currency }} 7 | {% else %} 8 | {% capture price %}{% assign show_zero = true %}{% include '_price' with p.price %}{% endcapture %} 9 | {% capture price_with_currency %}{% assign show_zero = true %}{% include '_price_with_currency' with p.price %}{% endcapture %} 10 | {{ 'products.price.standard_price_html' | t: price: price, price_with_currency: price_with_currency }} 11 | {% endif %} 12 |
    13 | {% endif %} 14 | -------------------------------------------------------------------------------- /theme/snippets/product_price_variant.liquid: -------------------------------------------------------------------------------- 1 | {% if settings.hide_pricing != true or v.price > 0 %} 2 |
    3 | {% capture price %}{% assign show_zero = true %}{% include '_price' with v.price %}{% endcapture %} 4 | {% capture price_with_currency %}{% assign show_zero = true %}{% include '_price_with_currency' with v.price %}{% endcapture %} 5 | {{ 'products.price.standard_price_html' | t: price: price, price_with_currency: price_with_currency }} 6 |
    7 | {% endif %} 8 | -------------------------------------------------------------------------------- /theme/snippets/product_social_share_links.liquid: -------------------------------------------------------------------------------- 1 | {% capture share_links %}{% endcapture %} 2 | 3 | {% if settings.product_social_share_facebook %} 4 | {% capture share_links %}{{ share_links }}
  • {% endcapture %} 5 | {% endif %} 6 | 7 | {% if settings.product_social_share_pinterest %} 8 | {% capture share_links %}{{ share_links }}
  • {% endcapture %} 9 | {% endif %} 10 | 11 | {% if settings.product_social_share_twitter %} 12 | {% capture twitter_handle %}{% unless settings.social_links_twitter == blank %}@{{ settings.social_links_twitter | split: '/' | last }}{% endunless %}{% endcapture %} 13 | {% capture tweet %}https://twitter.com/intent/tweet?text={{ 'products.social_links.twitter_message' | t: product_title: product.title, url: canonical_url, handle: twitter_handle }}{% endcapture %} 14 | {% capture share_links %}{{ share_links }}
  • {% endcapture %} 15 | {% endif %} 16 | 17 | {% if settings.product_social_share_email %} 18 | {% capture email_subject %}{{ 'products.social_links.email_subject' | t: product_title: product.title, shop_name: shop.name, url: canonical_url }}{% endcapture %} 19 | {% capture email_body %}{{ 'products.social_links.email_body' | t: product_title: product.title, shop_name: shop.name, url: canonical_url }}{% endcapture %} 20 | {% capture share_links %}{{ share_links }}
  • {% endcapture %} 21 | {% endif %} 22 | 23 | {% unless share_links == blank %} 24 | 29 | {% endunless %} -------------------------------------------------------------------------------- /theme/snippets/search_form.liquid: -------------------------------------------------------------------------------- 1 | {% comment TODO: typeahead %}{% endcomment %} 2 | 3 | {% comment note: 4 | 5 | `search_form` takes an optional variable parameter that is set outside the snippet before it is included. 6 | The parameter is `form_classes`: 7 | If not set it will default to 'search-form form-inline'. 8 | It will always include 'search-form' regardless of what it is given. 9 | 10 | %}{% endcomment %} 11 | 12 | {% if form_classes == null %}{% assign form_classes = 'form-inline' %}{% endif %} 13 | {% assign form_classes = form_classes | replace: ',',' ' | replace: ' ',' ' %} 14 | {% unless form_classes contains 'search-form ' %} 15 | {% assign form_classes = form_classes | prepend: 'search-form ' %} 16 | {% endunless %} 17 | 18 | {% if settings.enable_search or override_enabled_setting %} 19 | 29 | {% endif %} 30 | 31 | {% comment note: unset optional variable parameters. %}{% endcomment %} 32 | {% assign form_classes = null %} 33 | {% assign form_size = null %} 34 | {% assign override_enabled_setting = null %} -------------------------------------------------------------------------------- /theme/snippets/search_result_page.liquid: -------------------------------------------------------------------------------- 1 |
    2 | 20 |
    -------------------------------------------------------------------------------- /theme/snippets/search_results_empty.liquid: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | {% assign form_classes = 'search-form' %} 4 | {% assign form_size = 'lg' %} 5 | {% assign override_enabled_setting = true %} 6 | {% include 'search_form' %} 7 |
    8 | {% if message_title or message_html %} 9 |
    10 |
    11 | {% if message_title %}

    {{ message_title }}

    {% endif %} 12 | {% if message_html %}

    {{ message_html }}

    {% endif %} 13 |
    14 |
    15 | {% endif %} 16 |
    17 | 18 | {% if settings.search_collection %} 19 | {% assign collection = collections[settings.search_collection] %} 20 |

    {{ collection.title }}

    21 | 22 | {% include 'pagination_limit' with 'products' %} 23 | {% assign collection_all_size = collection.all_products.size %} 24 | {% if settings.show_product_variants_as_items %} 25 | {% assign collection_all_size = collection.all_products | map: 'variants' | map: 'title' | size %} 26 | {% endif %} 27 | {% if collection_all_size < pagination_limit %}{% assign collection_size = collection_all_size %}{% else %}{% assign collection_size = pagination_limit %}{% endif %} 28 |
    29 | {% for p in collection.products limit: pagination_limit %} 30 | {% include 'product_item' %} 31 | {% endfor %} 32 |
    33 | {% endif %} 34 | 35 | {% assign message_title = null %} 36 | {% assign message_html = null %} -------------------------------------------------------------------------------- /theme/snippets/site_blog_list.liquid: -------------------------------------------------------------------------------- 1 |

    {{ site_blog_list.title }}

    2 | 3 |
      4 | {% for a in site_blog_list.articles %} 5 |
    • 6 |
      {{ a.title }}
      7 | {% capture date %}{{ a.published_at | date: format: 'article_short' }}{% endcapture %} 8 |

      {{ 'blog.article_list.published_date_html' | t: date: date }}

      9 |
    • 10 | {% endfor %} 11 |
    -------------------------------------------------------------------------------- /theme/snippets/site_newsletter_form.liquid: -------------------------------------------------------------------------------- 1 | {% case settings.newsletter_platform %} 2 | {% when 'campaign-monitor' %} 3 | {% capture email_name %}cm-{{ settings.newsletter_form_action | split: '/' | last }}-{{ settings.newsletter_form_action | split: '/' | last }}{% endcapture %} 4 | {% when 'mailchimp' %} 5 | {% assign email_name = 'EMAIL' %} 6 | {% else %} 7 | {% assign email_name = 'contact[email]' %} 8 | {% endcase %} 9 | 10 | {% capture fields %} 11 |
    12 | 13 | 14 |
    15 | {% endcapture %} 16 | 17 | 33 | 34 | {% comment %} 35 | TODO: move this into the src js files 36 | - also include any feedback message from mailchimp / CM in the alert 37 | {% endcomment %} 38 | -------------------------------------------------------------------------------- /theme/snippets/site_social_links.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | TODO: update list of links; order better? 3 | {% endcomment %} 4 | 5 | 6 | -------------------------------------------------------------------------------- /theme/snippets/site_vars.liquid: -------------------------------------------------------------------------------- 1 | {% include '_page_url' %} -------------------------------------------------------------------------------- /theme/snippets/social_content.liquid: -------------------------------------------------------------------------------- 1 | {% capture function %} 2 | {% comment note: 3 | Function returns a heading and description. 4 | Takes two parameters: 5 | - a service name or url 6 | - a default title 7 | Parameters are split on a double pipe (||) 8 | Returns two variables (to the global scope): 9 | - social_title 10 | - social_description 11 | %}{% endcomment %} 12 | 13 | {% assign params = social_content | split: '||' %} 14 | {% assign service = params[0] %} 15 | {% if service contains '.' %} 16 | {% comment %} if the service is a url strip the service name out of it {% endcomment %} 17 | {% capture service %}{% include 'social_service' with service %}{% endcapture %} 18 | {% endif %} 19 | 20 | {% assign default_title = params[1] %} 21 | 22 | {% capture title_translation %}social_and_newsletter.advanced_layout.{{ service }}_title_html{% endcapture %} 23 | {% capture description_translation %}social_and_newsletter.advanced_layout.{{ service }}_description_html{% endcapture %} 24 | 25 | {% capture social_title %}{{ title_translation | t | default: default_title }}{% endcapture %} 26 | {% capture social_description %}{{ description_translation | t }}{% endcapture %} 27 | {% endcapture %} -------------------------------------------------------------------------------- /theme/snippets/social_feed.liquid: -------------------------------------------------------------------------------- 1 | {% capture function %} 2 | {% comment note: 3 | Function returns a social services feed. 4 | Takes one parameter which is the service url. 5 | Returns a block of markup containing the feed. 6 | %}{% endcomment %} 7 | 8 | {% capture service %}{% include 'social_service' with social_feed %}{% endcapture %} 9 | 10 | {% case service %} 11 | {% when 'facebook' %} 12 | {% capture feed %}{% include 'social_feed_facebook' with social_feed %}{% endcapture %} 13 | 14 | {% when 'instagram' %} 15 | {% capture feed %}{% include 'social_feed_instagram' with social_feed %}{% endcapture %} 16 | 17 | {% when 'twitter' %} 18 | {% capture feed %}{% include 'social_feed_twitter' with social_feed %}{% endcapture %} 19 | 20 | {% when 'vimeo' %} 21 | {% capture feed %}{% include 'social_feed_vimeo' with social_feed %}{% endcapture %} 22 | 23 | {% when 'youtube' %} 24 | {% capture feed %}{% include 'social_feed_youtube' with social_feed %}{% endcapture %} 25 | 26 | {% endcase %} 27 | {% endcapture %}{{ feed }}{% assign feed = null %} -------------------------------------------------------------------------------- /theme/snippets/social_feed_facebook.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | TODO: facebook album feed 3 | {% endcomment %} -------------------------------------------------------------------------------- /theme/snippets/social_feed_instagram.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Load a feed of images from instagram. 3 | A feed can either be from: 4 | - a user's profile 5 | - a hashtag 6 | 7 | To load a users feed pass a parameter into this snippet that is a url in the format of: 8 | instagram.com/{ user_name } 9 | 10 | To load a feed basied on a hashtag pass a parameter into this snippet that is a url in the format of: 11 | instagram.com/#{ hash_tag } 12 | 13 | TODO: default/fallback hashtag, image blacklist, image count 14 | {% endcomment %} 15 | 16 | {% unless settings.instagram_client_id == blank %} 17 | {% if social_feed_instagram contains '#' %} 18 | {% capture data_query %}data-hashtag="{{ social_feed_instagram | split: '.com/#' | last | remove: '/' | escape }}"{% endcapture %} 19 | {% else %} 20 | {% capture data_query %}data-user="{{ social_feed_instagram | split: '.com/' | last | remove: '/' | escape }}"{% endcapture %} 21 | {% endif %} 22 |
    23 | {% endunless %} -------------------------------------------------------------------------------- /theme/snippets/social_feed_twitter.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | TODO: twitter feed 3 | {% endcomment %} -------------------------------------------------------------------------------- /theme/snippets/social_feed_vimeo.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | TODO: embed vimeo video 3 | {% endcomment %} -------------------------------------------------------------------------------- /theme/snippets/social_feed_youtube.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | TODO: embed youtube video 3 | {% endcomment %} -------------------------------------------------------------------------------- /theme/snippets/social_service.liquid: -------------------------------------------------------------------------------- 1 | {% capture function %} 2 | {% comment note: 3 | Function that extracts the a social services name from a url. 4 | Takes one parameter which is url to extract the service name from. 5 | Returns a variable which is assigned and output so that it can be captured inside the snippet that includes it. 6 | %}{% endcomment %} 7 | 8 | {% assign strip_url_protocol = social_service | remove: 'https://' | remove: 'http://' | remove: '//' | remove: 'www.' %} 9 | {% assign service = strip_url_protocol | split: '.' | first | downcase %} 10 | 11 | {% endcapture %}{{ service }}{% assign service = null %} -------------------------------------------------------------------------------- /theme/templates/404.liquid: -------------------------------------------------------------------------------- 1 |
    2 | 5 | 6 | {% assign message_html = 'errors.404.not_found_message_html' | t %} 7 | {% include 'search_results_empty' %} 8 |
    -------------------------------------------------------------------------------- /theme/templates/blog.liquid: -------------------------------------------------------------------------------- 1 | {% assign image = false %} 2 | {% assign banner_index = false %} 3 | {% assign title_over_banner = false %} 4 | {% for i in (1..5) %} 5 | {% assign blog_banner_key = 'blog_banner_' | append: i %} 6 | {% assign blog_banner_image_key = 'blog_banner_image_' | append: i | append: '.jpg' %} 7 | {% assign blog_title_over_banner_key = 'blog_banner_' | append: i | append: '_title_over_banner' %} 8 | {% if settings[blog_banner_key] == handle %} 9 | {% assign banner_index = i %} 10 | {% assign image = blog_banner_image_key %} 11 | {% assign title_over_banner = settings[blog_title_over_banner_key] %} 12 | {% endif %} 13 | {% endfor %} 14 | 15 | {% if image %} 16 |
    17 | 25 |
    26 | {% endif %} 27 | 28 |
    29 | {% if title_over_banner == false %} 30 | 33 | {% endif %} 34 | 35 |
    36 | {% if blog.articles.size > 0 %} 37 | {% include 'pagination_limit' with 'articles' %} 38 | {% paginate blog.articles by pagination_limit %} 39 | {% if blog.articles.size < pagination_limit %}{% assign articles_size = blog.articles.size | times: 1 %}{% else %}{% assign articles_size = pagination_limit | times: 1 %}{% endif %} 40 |
    41 | {% for article in blog.articles %} 42 | {% include 'article_item' %} 43 | {% endfor %} 44 | {% include 'pagination' %} 45 |
    46 | {% endpaginate %} 47 | {% if settings.enable_rss_link %} 48 | 51 | {% endif %} 52 | {% else %} 53 |
    54 |

    {{ 'blog.general.empty_message_title' | t }}

    55 |

    {{ 'blog.general.empty_message_html' | t }}

    56 |
    57 | {% endif %} 58 |
    59 |
    -------------------------------------------------------------------------------- /theme/templates/collection.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | The base markup for our different collection templates is actually all the same 3 | The difference in templates is achieved by the styles 4 | We differentiate between each template by the template class on the body tag 5 | 6 | We can set different details to be displayed inside each thumb by setting global vars 7 | e.g. display product description, etc. 8 | %}{% endcomment %} 9 | 10 | {% include 'collection_page' %} -------------------------------------------------------------------------------- /theme/templates/collection.list.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | The base markup for our different collection templates is actually all the same 3 | The difference in templates is achieved by the styles 4 | We differentiate between each template by the template class on the body tag 5 | 6 | We can set different details to be displayed inside each thumb by setting global vars 7 | e.g. display product description, etc. 8 | %}{% endcomment %} 9 | 10 | {% assign product_list_item = true %} 11 | {% include 'collection_page' %} -------------------------------------------------------------------------------- /theme/templates/collection.lookbook.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | The base markup for our different collection templates is actually all the same 3 | The difference in templates is achieved by the styles 4 | We differentiate between each template by the template class on the body tag 5 | 6 | We can set different details to be displayed inside each thumb by setting global vars 7 | e.g. display product description, etc. 8 | %}{% endcomment %} 9 | 10 | {% include 'collection_page' %} -------------------------------------------------------------------------------- /theme/templates/collection.nested-collections.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: 2 | 3 | Nested Collections: 4 | To achieve a nested collection set a master collection with the template of `collection.nested`. 5 | This will then look for a linklist with a namespaced handle that matches `collection-{{ collection_handle }}`. 6 | Each collection in the matched linklist will then be displayed as it's nesting. 7 | 8 | Nested Collection: Collections 9 | Display the collection thumb for each collection 10 | 11 | %}{% endcomment %} 12 | 13 | {% assign collections_linklist_key = 'collection-' | append: handle %} 14 | {% assign collections_linklist = linklists[collections_linklist_key] %} 15 | 16 | {% assign collections_size = 0 %} 17 | {% capture nested_collections %}{% endcapture %} 18 | {% for collection_link in collections_linklist.links %} 19 | {% if collection_link.type == 'collection_link' %} 20 | {% assign c = collection_link.object %} 21 | {% assign collections_size = collections_size | plus: 1 %} 22 | {% capture nested_collections %} 23 | {{ nested_collections }} 24 | {% include 'collection_item' %} 25 | {% endcapture %} 26 | {% endif %} 27 | {% endfor %} 28 | 29 | {% comment note: Collection header layouts are also used on the list collections page, so set up the relevant variables outside the include first %}{% endcomment %} 30 | {% assign collection_title = collection.title %} 31 | {% assign collection_description = collection.description %} 32 | {% if settings.collection_header_layout contains 'split_image' %} 33 | {% assign collection_image = collection.image.src | img_url: 'grande' %} 34 | {% capture header_classes %}collection-header collection-header-split collection-header-{{ settings.collection_header_layout | replace: '_','-' }}{% endcapture %} 35 | {% include 'collection_header_layout_split' %} 36 | {% else %} 37 | {% assign collection_image = collection.image.src | img_url: '2048x2048' %} 38 | {% capture header_classes %}collection-header collection-header-{{ settings.collection_header_layout | replace: '_','-' }}{% endcapture %} 39 | {% assign collection_header_layout_snippet = 'collection_header_layout_' | append: settings.collection_header_layout %} 40 | {% include collection_header_layout_snippet %} 41 | {% endif %} 42 | 43 |
    44 |
    45 | {% if nested_collections == blank %} 46 |
    47 |
    48 |

    {{ 'collections.general.empty_message_title' | t }}

    49 |

    {{ 'collections.general.empty_message_html' | t }}

    50 |
    51 |
    52 | {% else %} 53 |
    54 | {{ nested_collections }} 55 |
    56 | {% endif %} 57 |
    58 |
    -------------------------------------------------------------------------------- /theme/templates/customers/activate_account.liquid: -------------------------------------------------------------------------------- 1 |
    2 | 5 | 6 |
    7 |
    8 |

    {{ 'customer_accounts.activate_account.description' | t }}

    9 | {% form 'activate_customer_password' %} 10 | {% assign form_errors_message = 'errors.customer_accounts.form_message' | t %} 11 | {% include 'form_errors' %} 12 | 13 |
    14 | 15 | 16 |
    17 | 18 |
    19 | 20 | 21 |
    22 | 23 | 24 | {{ 'customer_accounts.activate_account.button_separator' | t }} 25 | {% endform %} 26 |
    27 |
    28 |
    -------------------------------------------------------------------------------- /theme/templates/customers/register.liquid: -------------------------------------------------------------------------------- 1 |
    2 | 5 | 6 |
    7 |
    8 | 9 |
    {{ 'customer_accounts.register.description_html' | t }}
    10 | 11 | {% form 'create_customer' %} 12 | {% assign form_errors_message = 'errors.customer_accounts.form_message' | t %} 13 | {% include 'form_errors' %} 14 | 15 |
    16 | 17 | 18 |
    19 | 20 |
    21 | 22 | 23 |
    24 | 25 |
    26 | 27 | 28 |
    29 | 30 |
    31 | 32 | 33 |
    34 | 35 |
    36 | {% if form.password_needed %}

    {{ 'customer_accounts.login.reset_link' | t }}

    {% endif %} 37 |

    {{ 'customer_accounts.register.login_link' | t | customer_login_link }}

    38 |
    39 | 40 | {% if current_url %}{% endif %} 41 | 42 | {{ 'customer_accounts.register.note_html' | t: shop_url: shop.url }} 43 | {% endform %} 44 |
    45 |
    46 |
    -------------------------------------------------------------------------------- /theme/templates/customers/reset_password.liquid: -------------------------------------------------------------------------------- 1 |
    2 | 5 | 6 |
    7 |
    8 |

    {{ 'customer_accounts.reset_password.description' | t }}

    9 | {% form 'reset_customer_password' %} 10 | {% assign form_errors_message = 'errors.customer_accounts.form_message' | t %} 11 | {% include 'form_errors' %} 12 | 13 |
    14 | 15 | 16 |
    17 | 18 |
    19 | 20 | 21 |
    22 | 23 | 24 | {% endform %} 25 |
    26 |
    27 |
    -------------------------------------------------------------------------------- /theme/templates/list-collections.liquid: -------------------------------------------------------------------------------- 1 | {% comment note: List collection header layouts are also used on the collections page, so set up the relevant variables outside the include first %}{% endcomment %} 2 | {% capture collection_title %}{{ 'collections.list_collections.title' | t }}{% endcapture %} 3 | {% capture collection_description %}{{ 'collections.list_collections.description_html' | t }}{% endcapture %} 4 | {% assign collection_image = 'list_collections_banner_image.jpg' | asset_url %} 5 | {% if settings.list_collections_header_layout contains 'split_image' %} 6 | {% capture header_classes %}list-collection-header collection-header-split list-collection-header-{{ settings.list_collections_header_layout | replace: '_','-' }}{% endcapture %} 7 | {% include 'collection_header_layout_split' %} 8 | {% else %} 9 | {% capture header_classes %}list-collection-header collection-header-{{ settings.list_collections_header_layout | replace: '_','-' }}{% endcapture %} 10 | {% assign collection_header_layout_snippet = 'collection_header_layout_' | append: settings.list_collections_header_layout %} 11 | {% include collection_header_layout_snippet %} 12 | {% endif %} 13 | 14 |
    15 | {% assign featured_collections = linklists[settings.list_collections_featured_collection] %} 16 | {% if featured_collections.links != blank %} 17 | {% assign collections_size = featured_collections.links.size | times: 1 %} 18 |
    19 | {% for collection_link in featured_collections.links %} 20 | {% assign c = collection_link.object %} 21 | {% include 'collection_item' %} 22 | {% endfor %} 23 |
    24 | 25 | {% else %} 26 | {% comment note: No featured collections so fallback to all collections %}{% endcomment %} 27 | 28 | {% assign collections_size = collections.size | times: 1 %} 29 |
    30 | {% for c in collections %} 31 | {% include 'collection_item' %} 32 | {% endfor %} 33 |
    34 | {% endif %} 35 |
    -------------------------------------------------------------------------------- /theme/templates/page.contact-modal.liquid: -------------------------------------------------------------------------------- 1 | {% layout none %} 2 |
    3 | 7 | 16 | 19 |
    -------------------------------------------------------------------------------- /theme/templates/page.contact.liquid: -------------------------------------------------------------------------------- 1 | {% assign image = false %} 2 | {% assign content = page.content %} 3 | {% if settings.enable_page_banner and content contains 'src=' %} 4 | {% include 'page_banner' %} 5 | {% endif %} 6 | 7 |
    8 | {% unless settings.show_page_title_on_banner and image %} 9 | 12 | {% endunless %} 13 | 14 |
    15 |
    16 | {{ content }} 17 |
    18 | 19 |
    20 | {% include 'contact_form' %} 21 |
    22 |
    23 |
    -------------------------------------------------------------------------------- /theme/templates/page.full-width.liquid: -------------------------------------------------------------------------------- 1 | {% assign image = false %} 2 | {% assign content = page.content %} 3 | {% if settings.enable_page_banner and content contains 'src=' %} 4 | {% include 'page_banner' %} 5 | {% endif %} 6 | 7 |
    8 | {% unless settings.show_page_title_on_banner and image %} 9 | 12 | {% endunless %} 13 | 14 | {{ content }} 15 |
    -------------------------------------------------------------------------------- /theme/templates/page.liquid: -------------------------------------------------------------------------------- 1 | {% assign image = false %} 2 | {% assign content = page.content %} 3 | {% if settings.enable_page_banner and content contains 'src=' %} 4 | {% include 'page_banner' %} 5 | {% endif %} 6 | 7 |
    8 | {% unless settings.show_page_title_on_banner and image %} 9 | 12 | {% endunless %} 13 | 14 |
    15 | {{ content }} 16 |
    17 |
    -------------------------------------------------------------------------------- /theme/templates/page.modal.liquid: -------------------------------------------------------------------------------- 1 | {% layout none %} 2 |
    3 | 7 | 10 | 13 |
    -------------------------------------------------------------------------------- /theme/templates/page.split.liquid: -------------------------------------------------------------------------------- 1 | {% assign image = false %} 2 | {% assign content = page.content %} 3 | {% if settings.enable_page_banner and content contains 'src=' %} 4 | {% include 'page_banner' %} 5 | {% endif %} 6 | 7 |
    8 | {% unless settings.show_page_title_on_banner and image %} 9 | 12 | {% endunless %} 13 | 14 |
    15 | {% include 'content_split' with content %} 16 |
    17 |
    18 | -------------------------------------------------------------------------------- /utils/blessify.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var yaml = require('js-yaml'); 3 | var bless = require('bless'); 4 | var request = require('request'); 5 | 6 | var Blessify = function (config_path, stylesheet_name, output_path, output_filename, callback) { 7 | console.log('[Blessify] Started'); 8 | this.stylesheet_name = stylesheet_name; 9 | this.output_path = output_path; 10 | this.callback = callback; 11 | this.output_filename = output_filename || 'styles_ie_'; 12 | this.output_files = []; 13 | 14 | this.config = this._get_config(config_path); 15 | this.asset_url = this._build_asst_url(); 16 | 17 | this.bless = bless.Parser({ output: '', options: {}}); 18 | 19 | // find the correct asset path so we can find the stylesheet in question. 20 | this.request_data(this.asset_url, function (data) { 21 | data = JSON.parse(data); // asset json data 22 | var asset_path = this._get_asset_path(data.assets[0].public_url); 23 | 24 | // Now we have the asset path lets get the stylesheet 25 | this._request_stylesheet(asset_path); 26 | }); 27 | }; 28 | 29 | Blessify.prototype.request_data = function (url, callback) { 30 | var _this = this; 31 | request(url, function (error, response, body) { 32 | if (!error && response.statusCode == 200) { 33 | callback.apply(_this, [body]); 34 | } 35 | }); 36 | }; 37 | 38 | // Private 39 | 40 | Blessify.prototype._get_config = function (config_path) { 41 | try { 42 | return yaml.safeLoad(fs.readFileSync(config_path, 'utf8')); 43 | } catch (e) { 44 | console.log(e); 45 | } 46 | }; 47 | 48 | Blessify.prototype._build_asst_url = function () { 49 | return "https://"+this.config[':api_key']+":"+this.config[':password']+"@"+this.config[':store']+"/admin/themes/"+this.config[':theme_id']+"/assets.json"; 50 | }; 51 | 52 | Blessify.prototype._get_asset_path = function (path) { 53 | var asset_path = ''; 54 | var path_parts = path.split('/'); 55 | for (var i = 0; i < path_parts.length - 1; i++) { 56 | asset_path += path_parts[i] + '/'; 57 | } 58 | return asset_path; 59 | }; 60 | 61 | Blessify.prototype._request_stylesheet = function (asset_path) { 62 | var style_url = asset_path + this.stylesheet_name; 63 | this.request_data(style_url, function (data) { 64 | this._bless_stylesheet(data); 65 | }); 66 | }; 67 | 68 | Blessify.prototype._bless_stylesheet = function (css) { 69 | var _this = this; 70 | var blessed = this.bless.parse(css, function (err, files, numSelectors) { 71 | console.log('[Blessify] Selectors: '+numSelectors, 'Files: '+files.length); 72 | _this._output_stylesheets(files); 73 | 74 | if (_this.callback) { 75 | _this.callback.apply(_this, [_this.output_files]); 76 | } 77 | 78 | console.log('[Blessify] Complete'); 79 | }); 80 | }; 81 | 82 | Blessify.prototype._output_stylesheets = function (files) { 83 | for (var i = 0; i < files.length; i++) { 84 | this._output_stylesheet(files[i].content, i); 85 | } 86 | }; 87 | 88 | Blessify.prototype._output_stylesheet = function (file, i) { 89 | var filename = this._get_output_filename(i); 90 | fs.writeFileSync(filename, file, 'utf8'); 91 | }; 92 | 93 | Blessify.prototype._get_output_filename = function (i) { 94 | var filename = this.output_filename + (i+1) + '.css'; 95 | var filepath = this._build_output_path() + filename; 96 | var fileobj = { 97 | filename: filename, 98 | filepath: filepath 99 | }; 100 | 101 | this.output_files.push(fileobj); 102 | return fileobj.filepath; 103 | }; 104 | 105 | Blessify.prototype._build_output_path = function () { 106 | if (this.output_path.length > 0) { 107 | if (this.output_path[this.output_path.length - 1] !== '/') this.output_path = this.output_path + '/'; 108 | return this.output_path; 109 | } else { 110 | return ''; 111 | } 112 | }; 113 | 114 | module.exports = Blessify; -------------------------------------------------------------------------------- /utils/sass_import.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | /* 4 | If file contains @import 5 | Loop files contents 6 | collect imports 7 | find file 8 | recurse 9 | Else return files path 10 | 11 | */ 12 | 13 | var SassImport = function (target) { 14 | this.paths = []; 15 | this.target = target; 16 | this.pwd = this._get_pwd(target); 17 | 18 | this._collect_from(this.target, this.pwd); 19 | return this.paths; 20 | }; 21 | 22 | SassImport.prototype._collect_from = function (path, pwd) { 23 | var data = fs.readFileSync(path, 'utf8'); 24 | this._read_data(data, pwd); 25 | }; 26 | 27 | SassImport.prototype._read_data = function (data, pwd) { 28 | data = data.split("\n"); 29 | for (i in data) { 30 | var line = data[i]; 31 | if (this._is_import(line)) { 32 | path = this._get_path(line); 33 | path = this._complete_path(path, pwd); 34 | 35 | if (this._has_imports(path)) { 36 | current_pwd = this._get_pwd(path); 37 | this._collect_from(path, current_pwd); 38 | } else { 39 | this.paths.push(path); 40 | } 41 | } 42 | } 43 | }; 44 | 45 | SassImport.prototype._is_import = function (str) { 46 | return /^@import/.test(str); 47 | }; 48 | 49 | SassImport.prototype._has_imports = function (path) { 50 | var data = fs.readFileSync(path, 'utf8'); 51 | return /@import/.test(data); 52 | }; 53 | 54 | SassImport.prototype._get_path = function (str) { 55 | return /('|")(.+)('|")/.exec(str)[2]; 56 | }; 57 | 58 | SassImport.prototype._get_pwd = function (str) { 59 | return /(.+\/)/.exec(str)[1]; 60 | }; 61 | 62 | SassImport.prototype._complete_path = function (path, pwd) { 63 | // Bootstrap imports don't include file type or leading underscore when importing 64 | // TODO: make this less of a hack 65 | 66 | var tmp_path = pwd + path; 67 | if (!fs.existsSync(tmp_path) || fs.lstatSync(tmp_path).isDirectory()) { 68 | // check for missing file type 69 | if (fs.existsSync(tmp_path+'.scss')) path += '.scss'; 70 | if (fs.existsSync(tmp_path+'.sass')) path += '.sass'; 71 | 72 | // check for missing leading underscore 73 | var path_parts = path.split('/'); 74 | var fname = path_parts.pop(); 75 | var underscored_path = path_parts.join('/') + '/_' + fname; 76 | if (fs.existsSync(pwd + underscored_path)) path = underscored_path; 77 | 78 | // check for missing both 79 | if (fs.existsSync(pwd + underscored_path + '.scss')) path = underscored_path + '.scss'; 80 | if (fs.existsSync(pwd + underscored_path + '.sass')) path = underscored_path + '.sass'; 81 | } 82 | return pwd + path; 83 | }; 84 | 85 | module.exports = SassImport; --------------------------------------------------------------------------------